Commit 85e89e583d for woocommerce
commit 85e89e583d28b4c88463ebb28db8e02578870744
Author: Alba Rincón <albarin@users.noreply.github.com>
Date: Mon Jan 26 09:14:36 2026 +0100
Prevent the cherry-pick to trunk workflow to fail due to duplicate branches (#62578)
* Prevent the cherry-pick workflow to create duplicate branches
* Delete branch before creating PR
* Revert changes
* Skip if branch exists
* Skip cherry-pick without failing when branch already exists
* Handle skipped
* Detect PR status and update the message accordingly
* Update messages
* Fix error
* Fix interpolation
* Removed unused output
* Remove unused output
* set pr to empty
* Reopen closed pr, fix order and skip cherry pick if branch exists
* Fix permission
* Fix core.info error
* Refactor verify branches
* Simplify the logic for already existing PRs
* Improve comment
diff --git a/.github/workflows/cherry-pick-to-trunk.yml b/.github/workflows/cherry-pick-to-trunk.yml
index 2ded09b280..605eba14a4 100644
--- a/.github/workflows/cherry-pick-to-trunk.yml
+++ b/.github/workflows/cherry-pick-to-trunk.yml
@@ -187,6 +187,28 @@ jobs:
:info: *Cherry pick <${{ env.CHERRY_PICK_PR_URL }}|created> (${{ env.BASE_BRANCH }} → `trunk`)* Please ensure <${{ env.CHERRY_PICK_PR_URL }}|this cherry pick> is merged before releasing. Source PR: _<${{ env.SOURCE_PR_URL }}|#${{ env.SOURCE_PR_NUMBER }} - ${{ env.SOURCE_PR_TITLE }}>_.
${{ env.ADD_CP_TO_FROZEN_NAG == 'true' && format('\n\nNOTE: This PR might need to be cherry-picked to the frozen release ({0}).', env.NEXT_BRANCH) || '' }}
+ handle-skipped:
+ needs: [prepare, cherry-pick, frozen-release-check]
+ runs-on: ubuntu-latest
+ if: ${{ always() && needs.cherry-pick.outputs.status == 'skipped' }}
+ steps:
+ - name: Comment on original PR about skip
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+ env:
+ ERROR_MESSAGE: ${{ needs.cherry-pick.outputs.error_message }}
+ with:
+ github-token: ${{ secrets.WC_BOT_PR_CREATE_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ let body = '⚠️ **Cherry-pick to `trunk` was skipped.**\n\n' +
+ '**Reason:** ' + process.env.ERROR_MESSAGE + '\n\n';
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: parseInt('${{ needs.prepare.outputs.pr_number }}'),
+ body
+ });
+
handle-failures:
needs: [prepare, cherry-pick, frozen-release-check]
runs-on: ubuntu-latest
diff --git a/.github/workflows/shared-cherry-pick.yml b/.github/workflows/shared-cherry-pick.yml
index 33b43553e1..9802b570b6 100644
--- a/.github/workflows/shared-cherry-pick.yml
+++ b/.github/workflows/shared-cherry-pick.yml
@@ -47,8 +47,8 @@ jobs:
name: Verify requirements
runs-on: ubuntu-latest
permissions:
- contents: read
- pull-requests: read
+ contents: write
+ pull-requests: write
outputs:
status: ${{ steps.aggregate-status.outputs.status }}
error_message: ${{ steps.aggregate-status.outputs.error_message }}
@@ -110,7 +110,7 @@ jobs:
const cherryPickBranch = `cherry-pick-PR${prNumber}-to-${targetBranch}`;
try {
- // Check target branch exists
+ // 1. Verify target branch exists
await github.rest.repos.getBranch({
owner: context.repo.owner,
repo: context.repo.repo,
@@ -118,31 +118,72 @@ jobs:
});
core.info(`Target branch '${targetBranch}' exists`);
- // Check cherry-pick branch doesn't exist
+ // 2. Check if cherry-pick branch exists
+ let branchExists = false;
try {
await github.rest.repos.getBranch({
owner: context.repo.owner,
repo: context.repo.repo,
branch: cherryPickBranch
});
- core.setOutput('error_message', `Branch '${cherryPickBranch}' already exists. Please delete it first.`);
- core.setFailed(`Branch '${cherryPickBranch}' already exists.`);
- return;
+ branchExists = true;
+ core.info(`Branch '${cherryPickBranch}' exists`);
} catch (branchError) {
- if (branchError.status !== 404) {
- core.setOutput('error_message', `Error checking cherry-pick branch: ${branchError.message}`);
- core.setFailed(`Error checking cherry-pick branch: ${branchError.message}`);
- return;
- }
+ if (branchError.status !== 404) throw branchError;
+ core.info(`Branch '${cherryPickBranch}' does not exist (will be created)`);
+ }
+
+ // 3. Check if a PR already exists for this cherry-pick
+ const { data: prs } = await github.rest.pulls.list({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ head: `${context.repo.owner}:${cherryPickBranch}`,
+ state: 'all'
+ });
+
+ if (prs.length === 0) {
+ // No existing PR - proceed with cherry-pick
+ core.info(`No existing PR found - ready to create cherry-pick`);
+ core.setOutput('cherry_pick_branch', cherryPickBranch);
+ return;
+ }
+
+ // 4. Handle existing PR scenarios
+ const openPr = prs.find(pr => pr.state === 'open');
+ const mergedPr = prs.find(pr => pr.merged_at);
+
+ // Case 1: There's an open PR
+ if (openPr) {
+ core.info(`Found open PR #${openPr.number}`);
+ core.setOutput('status', 'skipped');
+ core.setOutput('error_message', `An open cherry-pick PR already exists: #${openPr.number}`);
+ return;
}
+ // Case 2: A PR was already merged
+ if (mergedPr) {
+ core.info(`Found merged PR #${mergedPr.number}`);
+ core.setOutput('status', 'skipped');
+ core.setOutput('error_message', `A cherry-pick PR was already merged: #${mergedPr.number}`);
+ return;
+ }
+
+ // Case 3: Only closed (not merged) PRs exist, we delete branch and create fresh PR
+ if (branchExists) {
+ await github.rest.git.deleteRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: `heads/${cherryPickBranch}`
+ });
+ core.info(`Found only closed PRs. Deleted branch '${cherryPickBranch}'.`);
+ }
+
core.setOutput('cherry_pick_branch', cherryPickBranch);
- core.info(`Cherry-pick branch '${cherryPickBranch}' is available`);
} catch (error) {
const errorMsg = error.status === 404
? `Target branch '${targetBranch}' does not exist`
- : `Error checking target branch: ${error.message}`;
+ : `Error during verification: ${error.message}`;
core.setOutput('error_message', errorMsg);
core.setFailed(errorMsg);
@@ -152,7 +193,10 @@ jobs:
id: aggregate-status
if: always()
run: |
- if [[ "${{ steps.check-pr.outcome }}" == "failure" ]]; then
+ if [[ "${{ steps.verify-branches.outputs.status }}" == "skipped" ]]; then
+ echo "status=skipped" >> $GITHUB_OUTPUT
+ echo "error_message=${{ steps.verify-branches.outputs.error_message }}" >> $GITHUB_OUTPUT
+ elif [[ "${{ steps.check-pr.outcome }}" == "failure" ]]; then
echo "status=failed" >> $GITHUB_OUTPUT
echo "error_message=${{ steps.check-pr.outputs.error_message }}" >> $GITHUB_OUTPUT
elif [[ "${{ steps.verify-branches.outcome }}" == "failure" ]]; then
@@ -192,6 +236,12 @@ jobs:
PR_NUMBER="${{ inputs.pr_number }}"
TARGET_BRANCH="${{ inputs.target_branch }}"
+ # Check if branch already exists
+ if git ls-remote --heads origin "$CHERRY_PICK_BRANCH" | grep -q "$CHERRY_PICK_BRANCH"; then
+ echo "Branch $CHERRY_PICK_BRANCH already exists remotely. Skipping cherry-pick."
+ exit 0
+ fi
+
# Create cherry-pick branch
git checkout -b "$CHERRY_PICK_BRANCH"
git fetch origin "$MERGE_COMMIT_SHA"
@@ -401,7 +451,6 @@ jobs:
echo "cherry_pick_pr_number=${{ steps.create-pr.outputs.pr_number }}" >> $GITHUB_OUTPUT
fi
-
aggregate-overall-status:
name: Aggregate final status
runs-on: ubuntu-latest
@@ -418,7 +467,11 @@ jobs:
verify_status="${{ needs.verify.outputs.status }}"
cherry_status="${{ needs.cherry-pick.outputs.status }}"
- if [[ "$verify_status" != "success" ]]; then
+ if [[ "$verify_status" == "skipped" ]]; then
+ echo "status=skipped" >> $GITHUB_OUTPUT
+ echo "error_message=${{ needs.verify.outputs.error_message }}" >> $GITHUB_OUTPUT
+ echo "cherry_pick_pr_number=" >> $GITHUB_OUTPUT
+ elif [[ "$verify_status" != "success" ]]; then
echo "status=failed" >> $GITHUB_OUTPUT
echo "error_message=${{ needs.verify.outputs.error_message }}" >> $GITHUB_OUTPUT
echo "cherry_pick_pr_number=" >> $GITHUB_OUTPUT