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