Commit 5a7928e484 for woocommerce
commit 5a7928e48486dfb66a6ee425e990252ddcc52874
Author: Alba Rincón <albarin@users.noreply.github.com>
Date: Thu Jan 22 16:50:44 2026 +0100
Block cherry-picking PR from merging when they have conflicts (#62771)
* Add cherry-pick has conflicts label
* Block merge when conflicts
* wip
* Fix map description
* Fix conflicts mapping
* Improve cherry pick check workflow
* Simplify list conflicts
* Fix check-conflicts-label job
* Remove unused vars
* Remove debug echo
* Add warning callout
* Use jq to process conflicts
* Use single quote
* Use env vars for the conflict status
diff --git a/.github/workflows/release-cherry-pick-pr-check-conflicts.yml b/.github/workflows/release-cherry-pick-pr-check-conflicts.yml
new file mode 100644
index 0000000000..ce6b907f7f
--- /dev/null
+++ b/.github/workflows/release-cherry-pick-pr-check-conflicts.yml
@@ -0,0 +1,32 @@
+name: 'Block merge if cherry-pick conflicts exist'
+
+on:
+ pull_request:
+ types:
+ - opened
+ - reopened
+ - edited
+ - synchronize
+ - ready_for_review
+ - labeled
+ - unlabeled
+
+permissions:
+ contents: read
+ pull-requests: read
+
+jobs:
+ check-conflicts-label:
+ name: Check for conflicts label
+ runs-on: ubuntu-latest
+ if: |
+ startsWith(github.head_ref, 'cherry-pick-') &&
+ contains(github.event.pull_request.labels.*.name, 'cherry pick has conflicts')
+ permissions:
+ pull-requests: read
+ statuses: write
+ steps:
+ - name: Block merge due to conflicts
+ run: |
+ echo "❌ This PR has unresolved cherry-pick conflicts. Please resolve conflicts before merging."
+ exit 1
\ No newline at end of file
diff --git a/.github/workflows/shared-cherry-pick.yml b/.github/workflows/shared-cherry-pick.yml
index a284169faf..33b43553e1 100644
--- a/.github/workflows/shared-cherry-pick.yml
+++ b/.github/workflows/shared-cherry-pick.yml
@@ -202,12 +202,17 @@ jobs:
echo "has_conflicts=false" >> $GITHUB_OUTPUT
echo "Cherry-pick completed successfully"
else
- # Look for conflict markers in git status
- if git status --porcelain | grep -E "^(DD|AU|UD|UA|DU|AA|UU)"; then
+ # Capture conflicted files with their status codes and build JSON array using jq
+ CONFLICT_JSON=$(git status --porcelain | grep -E "^(DD|AU|UD|UA|DU|AA|UU)" | jq -R -n -c '
+ [inputs | {status: .[0:2], file: .[3:]}]
+ ')
+
+ if [[ "$CONFLICT_JSON" != "[]" ]]; then
echo "has_conflicts=true" >> $GITHUB_OUTPUT
- echo "Cherry-pick had conflicts - resolving automatically"
+ echo "conflict_status=$CONFLICT_JSON" >> $GITHUB_OUTPUT
+
+ echo "Cherry-pick had conflicts - details: $CONFLICT_JSON"
- # For modify/delete conflicts, we need to handle them specifically
# Add all files (both conflicted and resolved)
git add .
@@ -245,6 +250,8 @@ jobs:
- name: Create cherry-pick pull request
id: create-pr
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+ env:
+ CONFLICT_STATUS: ${{ steps.cherry-pick.outputs.conflict_status }}
with:
github-token: ${{ secrets.WC_BOT_PR_CREATE_TOKEN || secrets.GITHUB_TOKEN }}
script: |
@@ -265,7 +272,30 @@ jobs:
let prBody = `This PR is a cherry-pick of #${prNumber} to \`${targetBranch}\`.\n\n`;
if (hasConflicts) {
- prBody += `⚠️ **WARNING**: This cherry-pick contained conflicts that have not been resolved. Please review the changes carefully before merging!\n\n`;
+ prBody += `> [!WARNING]\n`;
+ prBody += `> This cherry-pick contained conflicts that have not been resolved\n`;
+ prBody += `> 1. Review and resolve all conflicts\n`;
+ prBody += `> 2. Remove the \`cherry pick has conflicts\` label (required to merge)\n`;
+
+ // Parse JSON array of conflicts
+ const conflicts = JSON.parse(process.env.CONFLICT_STATUS || '[]');
+ // Map status codes to human-readable descriptions
+ const conflictTypeMap = {
+ 'DD': 'deleted by both',
+ 'UA': 'added by cherry-pick, does not exist in target branch',
+ 'UD': 'deleted by cherry-pick, modified in target branch',
+ 'AU': 'modified by cherry-pick, added in target branch',
+ 'DU': 'modified by cherry-pick, deleted in target branch',
+ 'AA': 'added by both with different content',
+ 'UU': 'modified by both'
+ };
+ prBody += `>\n`; // Empty blockquote line for spacing
+ prBody += `> **Conflicted Files:**\n`;
+ for (const { status, file } of conflicts) {
+ const conflictType = conflictTypeMap[status] || 'unknown conflict type';
+ prBody += `> - \`${file}\` (${conflictType})\n`;
+ }
+ prBody += `\n`;
}
prBody += `## Original PR Description\n\n${originalPr.body || '*No description provided*'}`;
@@ -284,6 +314,7 @@ jobs:
title: `[Backport to ${targetBranch}] ${originalPr.title}`,
body: prBody
});
+
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
@@ -291,6 +322,21 @@ jobs:
reviewers: [context.actor, originalPr.user.login]
});
+ // Add label if there are conflicts
+ if (hasConflicts) {
+ try {
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: newPr.number,
+ labels: ['cherry pick has conflicts']
+ });
+ core.info(`Added 'cherry pick has conflicts' label to PR #${newPr.number}`);
+ } catch (error) {
+ core.warning(`Failed to add conflicts label: ${error.message}`);
+ }
+ }
+
core.setOutput('pr_number', newPr.number.toString());
core.setOutput('original_author', originalPr.user?.login || '');
core.setOutput('original_merger', originalPr.merged_by?.login || '');
@@ -384,4 +430,4 @@ jobs:
echo "status=success" >> $GITHUB_OUTPUT
echo "error_message=" >> $GITHUB_OUTPUT
echo "cherry_pick_pr_number=${{ needs.cherry-pick.outputs.cherry_pick_pr_number }}" >> $GITHUB_OUTPUT
- fi
+ fi
\ No newline at end of file