Skip to content

Commit 298b3c6

Browse files
ci: update automated backport workflow to skip if backport already in-flight or completed (#6317)
Updates the backport workflow so it filters out branches that have already been backported, allowing new labels to trigger fresh cherry-picks without reprocessing completed targets. Also adds an automatic cleanup step that removes the `needs-backport` trigger label once a run succeeds, aligning its behavior with the other label-driven automations. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6317-ci-update-automated-backport-workflow-to-skip-if-backport-already-in-flight-or-completed-2996d73d36508113b90df43b1f68344f) by [Unito](https://www.unito.io)
1 parent 20a1a9e commit 298b3c6

File tree

1 file changed

+78
-31
lines changed

1 file changed

+78
-31
lines changed

.github/workflows/pr-backport.yaml

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -69,34 +69,7 @@ jobs:
6969
git config user.name "github-actions[bot]"
7070
git config user.email "github-actions[bot]@users.noreply.github.com"
7171
72-
- name: Check if backports already exist
73-
id: check-existing
74-
env:
75-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76-
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
77-
run: |
78-
# Check for existing backport PRs for this PR number
79-
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
80-
81-
if [ -z "$EXISTING_BACKPORTS" ]; then
82-
echo "skip=false" >> $GITHUB_OUTPUT
83-
exit 0
84-
fi
85-
86-
# For manual triggers with force_rerun, proceed anyway
87-
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
88-
echo "skip=false" >> $GITHUB_OUTPUT
89-
echo "::warning::Force rerun requested - existing backports will be updated"
90-
exit 0
91-
fi
92-
93-
echo "Found existing backport PRs:"
94-
echo "$EXISTING_BACKPORTS"
95-
echo "skip=true" >> $GITHUB_OUTPUT
96-
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
97-
9872
- name: Collect backport targets
99-
if: steps.check-existing.outputs.skip != 'true'
10073
id: targets
10174
run: |
10275
TARGETS=()
@@ -151,8 +124,76 @@ jobs:
151124
echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT
152125
echo "Found backport targets: ${TARGETS[*]}"
153126
127+
- name: Filter already backported targets
128+
id: filter-targets
129+
env:
130+
EVENT_NAME: ${{ github.event_name }}
131+
FORCE_RERUN_INPUT: >-
132+
${{ github.event_name == 'workflow_dispatch' && inputs.force_rerun
133+
|| 'false' }}
134+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
135+
PR_NUMBER: >-
136+
${{ github.event_name == 'workflow_dispatch' && inputs.pr_number
137+
|| github.event.pull_request.number }}
138+
run: |
139+
set -euo pipefail
140+
141+
REQUESTED_TARGETS="${{ steps.targets.outputs.targets }}"
142+
if [ -z "$REQUESTED_TARGETS" ]; then
143+
echo "skip=true" >> $GITHUB_OUTPUT
144+
echo "pending-targets=" >> $GITHUB_OUTPUT
145+
exit 0
146+
fi
147+
148+
FORCE_RERUN=false
149+
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$FORCE_RERUN_INPUT" = "true" ]; then
150+
FORCE_RERUN=true
151+
fi
152+
153+
mapfile -t EXISTING_BRANCHES < <(
154+
git ls-remote --heads origin "backport-${PR_NUMBER}-to-*" || true
155+
)
156+
157+
PENDING=()
158+
SKIPPED=()
159+
160+
for target in $REQUESTED_TARGETS; do
161+
SAFE_TARGET=$(echo "$target" | tr '/' '-')
162+
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
163+
164+
if [ "$FORCE_RERUN" = true ]; then
165+
PENDING+=("$target")
166+
continue
167+
fi
168+
169+
if printf '%s\n' "${EXISTING_BRANCHES[@]:-}" |
170+
grep -Fq "refs/heads/${BACKPORT_BRANCH}"; then
171+
SKIPPED+=("$target")
172+
else
173+
PENDING+=("$target")
174+
fi
175+
done
176+
177+
SKIPPED_JOINED="${SKIPPED[*]:-}"
178+
PENDING_JOINED="${PENDING[*]:-}"
179+
180+
echo "already-exists=${SKIPPED_JOINED}" >> $GITHUB_OUTPUT
181+
echo "pending-targets=${PENDING_JOINED}" >> $GITHUB_OUTPUT
182+
183+
if [ -z "$PENDING_JOINED" ]; then
184+
echo "skip=true" >> $GITHUB_OUTPUT
185+
if [ -n "$SKIPPED_JOINED" ]; then
186+
echo "::warning::Backport branches already exist for: ${SKIPPED_JOINED}"
187+
fi
188+
else
189+
echo "skip=false" >> $GITHUB_OUTPUT
190+
if [ -n "$SKIPPED_JOINED" ]; then
191+
echo "::notice::Skipping already backported targets: ${SKIPPED_JOINED}"
192+
fi
193+
fi
194+
154195
- name: Backport commits
155-
if: steps.check-existing.outputs.skip != 'true'
196+
if: steps.filter-targets.outputs.skip != 'true'
156197
id: backport
157198
env:
158199
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -170,7 +211,7 @@ jobs:
170211
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
171212
fi
172213
173-
for target in ${{ steps.targets.outputs.targets }}; do
214+
for target in ${{ steps.filter-targets.outputs.pending-targets }}; do
174215
TARGET_BRANCH="${target}"
175216
SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-')
176217
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
@@ -219,7 +260,7 @@ jobs:
219260
fi
220261
221262
- name: Create PR for each successful backport
222-
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
263+
if: steps.filter-targets.outputs.skip != 'true' && steps.backport.outputs.success
223264
env:
224265
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
225266
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -258,7 +299,7 @@ jobs:
258299
done
259300
260301
- name: Comment on failures
261-
if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
302+
if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
262303
env:
263304
GH_TOKEN: ${{ github.token }}
264305
run: |
@@ -287,3 +328,9 @@ jobs:
287328
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
288329
fi
289330
done
331+
332+
- name: Remove needs-backport label
333+
if: steps.filter-targets.outputs.skip != 'true' && success()
334+
run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport"
335+
env:
336+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)