From 640454bfe204527636b4366c9a58666f72c0e039 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 26 Feb 2026 10:42:17 +0100 Subject: [PATCH 1/2] ci: Cancel in-progress CI jobs when a PR is closed or merged The existing concurrency groups use `github.ref` which for PRs resolves to `refs/pull//merge`. When a PR is merged, the push to main creates a different concurrency group (`refs/heads/main`), so the still-running PR workflows are never cancelled, wasting CI resources. This adds a new workflow that triggers on `pull_request: closed` (fired on both merge and manual close) and cancels all in-progress and queued workflow runs for the PR's head branch via the GitHub API. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/cancel-pr-workflows.yml | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/cancel-pr-workflows.yml diff --git a/.github/workflows/cancel-pr-workflows.yml b/.github/workflows/cancel-pr-workflows.yml new file mode 100644 index 0000000000..f9b6ecfa99 --- /dev/null +++ b/.github/workflows/cancel-pr-workflows.yml @@ -0,0 +1,47 @@ +name: Cancel PR Workflows + +on: + pull_request: + types: [closed] + +jobs: + cancel: + name: Cancel In-Progress Workflows + runs-on: ubuntu-latest + steps: + - name: Cancel in-progress workflow runs + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const branch = context.payload.pull_request.head.ref; + + const workflows = await github.rest.actions.listWorkflowRunsForRepo({ + owner, + repo, + branch, + status: 'in_progress', + }); + + const waitingWorkflows = await github.rest.actions.listWorkflowRunsForRepo({ + owner, + repo, + branch, + status: 'queued', + }); + + const runs = [...workflows.data.workflow_runs, ...waitingWorkflows.data.workflow_runs]; + + for (const run of runs) { + if (run.id === context.runId) { + continue; + } + console.log(`Cancelling run ${run.id} (${run.name})`); + await github.rest.actions.cancelWorkflowRun({ + owner, + repo, + run_id: run.id, + }); + } + + console.log(`Cancelled ${runs.length > 1 ? runs.length - 1 : 0} workflow run(s) for branch ${branch}`); From ccbff3376cd4c3a3ae54ea0824569edfba7ff01c Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 26 Feb 2026 12:04:27 +0100 Subject: [PATCH 2/2] fix(ci): Handle cancel errors gracefully in PR cleanup workflow Wrap the cancelWorkflowRun call in try/catch so that if a run completes between the list and cancel calls, the error doesn't abort cancellation of the remaining runs. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/cancel-pr-workflows.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cancel-pr-workflows.yml b/.github/workflows/cancel-pr-workflows.yml index f9b6ecfa99..7120231aec 100644 --- a/.github/workflows/cancel-pr-workflows.yml +++ b/.github/workflows/cancel-pr-workflows.yml @@ -36,12 +36,16 @@ jobs: if (run.id === context.runId) { continue; } - console.log(`Cancelling run ${run.id} (${run.name})`); - await github.rest.actions.cancelWorkflowRun({ - owner, - repo, - run_id: run.id, - }); + try { + console.log(`Cancelling run ${run.id} (${run.name})`); + await github.rest.actions.cancelWorkflowRun({ + owner, + repo, + run_id: run.id, + }); + } catch (error) { + console.log(`Failed to cancel run ${run.id}: ${error.message}`); + } } console.log(`Cancelled ${runs.length > 1 ? runs.length - 1 : 0} workflow run(s) for branch ${branch}`);