From db8a92b8b72be1e4f4db51140ca5e4667d65648e Mon Sep 17 00:00:00 2001 From: sudlud Date: Tue, 21 Jul 2026 14:59:34 +0200 Subject: [PATCH] fix(CI): cancel a PR's CI when the PR closes (#26719) Co-authored-by: Claude Opus 4.8 (1M context) Co-authored-by: CodeRabbit --- .github/workflows/cancel-pr-runs-on-close.yml | 48 +++++++++++++++++++ .github/workflows/core-build-nopch.yml | 5 +- .github/workflows/core-build-pch.yml | 5 +- .github/workflows/core_modules_build.yml | 5 +- .github/workflows/docker_build.yml | 5 +- .github/workflows/tools_build.yml | 5 +- .github/workflows/windows_build.yml | 5 +- 7 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/cancel-pr-runs-on-close.yml diff --git a/.github/workflows/cancel-pr-runs-on-close.yml b/.github/workflows/cancel-pr-runs-on-close.yml new file mode 100644 index 000000000..0416af439 --- /dev/null +++ b/.github/workflows/cancel-pr-runs-on-close.yml @@ -0,0 +1,48 @@ +name: cancel-pr-runs-on-close +on: + # pull_request_target, not pull_request: for a PR from a fork the GITHUB_TOKEN + # of a pull_request run is read-only, so the cancel would 403. This workflow + # never checks out or runs PR code, so the elevated token is not exposed to it. + pull_request_target: + types: + - closed + +permissions: + actions: write + +jobs: + cancel: + runs-on: ubuntu-latest + name: cancel CI still running for the closed PR + if: github.repository == 'azerothcore/azerothcore-wotlk' + steps: + - name: Cancel workflow runs for this PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + PR_SHA: ${{ github.event.pull_request.head.sha }} + PR_REPO: ${{ github.event.pull_request.head.repo.full_name }} + run: | + set -euo pipefail + + # head_sha alone would let anyone cancel another PR's CI: push that + # PR's head commit to your own fork, open a PR and close it, and every + # run for the commit matches. So the run must also come from this PR's + # head repository. `gh run list` exposes neither head_repository nor + # the (fork-empty) pull_requests field, hence the raw API call. + gh api "repos/${GH_REPO}/actions/runs?event=pull_request&head_sha=${PR_SHA}&per_page=100" \ + --jq '.workflow_runs[] + | select(.head_repository.full_name == env.PR_REPO and .status != "completed") + | .id' \ + | while read -r id; do + # gh has no bulk cancel. A run that finishes between the listing + # and the cancel returns 409, which is not worth failing over. + if ! err=$(gh run cancel "$id" 2>&1); then + if echo "$err" | grep -Fq "HTTP 409"; then + echo "::warning::run $id was already finished" + else + echo "::error::Failed to cancel run $id: $err" + exit 1 + fi + fi + done diff --git a/.github/workflows/core-build-nopch.yml b/.github/workflows/core-build-nopch.yml index 0e8a26b66..68a4515e6 100644 --- a/.github/workflows/core-build-nopch.yml +++ b/.github/workflows/core-build-nopch.yml @@ -20,8 +20,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/core-build-pch.yml b/.github/workflows/core-build-pch.yml index 2a8d27a01..e5a2af071 100644 --- a/.github/workflows/core-build-pch.yml +++ b/.github/workflows/core-build-pch.yml @@ -20,8 +20,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/core_modules_build.yml b/.github/workflows/core_modules_build.yml index 322c06385..d3f02d615 100644 --- a/.github/workflows/core_modules_build.yml +++ b/.github/workflows/core_modules_build.yml @@ -28,8 +28,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 7263820e3..27334bf75 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -13,8 +13,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/tools_build.yml b/.github/workflows/tools_build.yml index dbd8eba50..75cb5fe2f 100644 --- a/.github/workflows/tools_build.yml +++ b/.github/workflows/tools_build.yml @@ -13,8 +13,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/windows_build.yml b/.github/workflows/windows_build.yml index 3f8f576e9..6bec39ac1 100644 --- a/.github/workflows/windows_build.yml +++ b/.github/workflows/windows_build.yml @@ -13,8 +13,9 @@ concurrency: # # - PRs use `refs/pull//merge`, so new commits cancel older # in-progress runs for the same PR. - # - When a PR is merged, a push to the target branch starts a new group, - # canceling any still-running PR CI. + # - A merge does NOT cancel the PR's own runs: this group is keyed on + # refs/pull//merge and the master push on refs/heads/master, so they + # never share a group. See the cancel-pr-runs-on-close workflow. # - Branch pushes are isolated by ref. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true