Fix PR-close cancellation skipping workflows beyond the first 30 (#27785)
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
name: Cancel PR Workflows on Close
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
workflow_id: all
|
||||
access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ignore_sha: true
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
@@ -0,0 +1,104 @@
|
||||
name: Cancel PR Workflows on Close
|
||||
|
||||
# Cancels every unfinished run on a PR's head branch when the PR closes
|
||||
# (merged or not). Runs are listed repo-wide by branch, not per workflow:
|
||||
# the previous styfle/cancel-workflow-action `workflow_id: all` fetched
|
||||
# only the first page (30) of repo workflows, so newer workflows (e.g.
|
||||
# pr-test-extra.yml) were never cancelled and kept occupying runners.
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel unfinished runs of this PR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
HEAD_BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
HEAD_REPO_ID: ${{ github.event.pull_request.head.repo.id }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "$HEAD_REPO_ID" ]; then
|
||||
echo "Head repo no longer exists (fork deleted), nothing to match"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FAILURES=0
|
||||
|
||||
# `branch=` matches run.head_branch, shared with same-named
|
||||
# branches of other forks -- filter by head repo id. Exclude this
|
||||
# run itself: pull_request_target runs also carry the head branch.
|
||||
# The status filter takes one value per request, so sweep every
|
||||
# non-terminal state (pending/waiting/action_required runs would
|
||||
# otherwise start after the PR is closed). Pass 2 catches runs
|
||||
# still materializing during pass 1.
|
||||
for pass in 1 2; do
|
||||
run_ids=""
|
||||
for status in queued in_progress waiting pending requested action_required; do
|
||||
ids=""
|
||||
for attempt in 1 2 3; do
|
||||
if ids=$(gh api -X GET "repos/$REPO/actions/runs" \
|
||||
--paginate \
|
||||
-f branch="$HEAD_BRANCH" \
|
||||
-f status="$status" \
|
||||
-F per_page=100 \
|
||||
--jq ".workflow_runs[]
|
||||
| select(.head_repository.id == $HEAD_REPO_ID and .id != $GITHUB_RUN_ID)
|
||||
| .id"); then
|
||||
break
|
||||
fi
|
||||
ids=""
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "::error::Listing $status runs failed after 3 attempts"
|
||||
FAILURES=1
|
||||
else
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
run_ids="$run_ids"$'\n'"$ids"
|
||||
done
|
||||
run_ids=$(echo "$run_ids" | sed '/^$/d' | sort -u)
|
||||
|
||||
if [ -z "$run_ids" ]; then
|
||||
echo "Pass $pass: no unfinished runs found"
|
||||
break
|
||||
fi
|
||||
echo "Pass $pass: cancelling $(echo "$run_ids" | wc -l | tr -d ' ') run(s)"
|
||||
|
||||
for run_id in $run_ids; do
|
||||
echo "Cancelling https://github.com/$REPO/actions/runs/$run_id"
|
||||
if gh run cancel "$run_id" --repo "$REPO" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
# Plain cancel fails for runs stuck behind approval /
|
||||
# deployment protection rules; force-cancel handles those.
|
||||
if gh api -X POST "repos/$REPO/actions/runs/$run_id/force-cancel" >/dev/null 2>&1; then
|
||||
echo " force-cancelled"
|
||||
continue
|
||||
fi
|
||||
# Finished between listing and cancelling is fine.
|
||||
state=$(gh api "repos/$REPO/actions/runs/$run_id" --jq '.status' 2>/dev/null || echo "unknown")
|
||||
if [ "$state" = "completed" ]; then
|
||||
echo " already finished, nothing to cancel"
|
||||
else
|
||||
echo "::error::Failed to cancel run $run_id (status: $state)"
|
||||
FAILURES=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$pass" -eq 1 ]; then
|
||||
sleep 20
|
||||
fi
|
||||
done
|
||||
|
||||
exit "$FAILURES"
|
||||
Reference in New Issue
Block a user