From aa2d94cc50b8e8da8bc3368d3e888c68d5c26f1f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 2 Nov 2026 03:21:26 -0800 Subject: [PATCH] ci: cancel a PR's runs when it is merged or closed (#12330) A merged or closed PR kept every pull_request run going: concurrency groups cancel a run only when a newer push arrives. After #12329 merged, 16 of its runs were still queued or running. On pull_request_target closed, cancel the queued and running pull_request runs on the PR's head branch whose commit belongs to the PR. Claude-Session: https://claude.ai/code/session_011UpvafvK8JpagF94MHWqwK Co-authored-by: Claude Opus 5.5 --- .github/workflows/cancel-closed-pr-runs.yml | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/cancel-closed-pr-runs.yml diff --git a/.github/workflows/cancel-closed-pr-runs.yml b/.github/workflows/cancel-closed-pr-runs.yml new file mode 100644 index 0000000000..0e07039ac9 --- /dev/null +++ b/.github/workflows/cancel-closed-pr-runs.yml @@ -0,0 +1,37 @@ +name: "Cancel runs of closed PRs" + +# Merging or closing a PR leaves its pull_request runs going: concurrency +# groups cancel a run only when a newer push arrives. pull_request_target gives +# this job a token that can cancel runs, fork PRs included. It checks out and +# runs no PR code. +on: + pull_request_target: + types: [closed] + +permissions: + actions: write + pull-requests: read + +jobs: + cancel: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Cancel the PR's queued and running pull_request runs + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + PR: ${{ github.event.pull_request.number }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + run: | + set -euo pipefail + # Match the PR's own commits: forks reuse branch names such as master. + shas=$(gh api --paginate "repos/$GH_REPO/pulls/$PR/commits" --jq '.[].sha') + for status in queued in_progress waiting pending requested; do + gh run list --branch "$HEAD_REF" --event pull_request --status "$status" \ + --limit 200 --json databaseId,headSha --jq '.[] | "\(.databaseId) \(.headSha)"' + done | while read -r id sha; do + grep -qx "$sha" <<<"$shas" || continue + echo "cancel run $id at $sha" + gh run cancel "$id" || true + done