Skip to content

Cancel Dequeued Runs #12

Cancel Dequeued Runs

Cancel Dequeued Runs #12

# Cancels the checks still running for a merge group the queue has thrown away.
#
# When a required check fails, the queue removes the pull request and deletes the
# group's branch, but it does not stop the other checks already running against it.
# They run to completion against a ref nobody will merge — for Zebra that is a full
# unit test matrix, so one dequeue can waste over an hour of runner time.
#
# `merge_group` has a `destroyed` webhook activity type, but it is not valid as a
# workflow trigger, and `workflow_run` arrives only once every job in the run has
# finished, which is too late to cancel anything. Deleting the branch is the earliest
# signal available: it arrives about twenty seconds after the group is destroyed.
name: Cancel Dequeued Runs
on:
delete:
permissions: {}
jobs:
cancel-runs:
# `delete` fires for every branch and tag; only the queue's own branches matter.
if: >-
github.event.ref_type == 'branch' &&
startsWith(github.event.ref, 'gh-readonly-queue/')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
actions: write
steps:
- name: Cancel active runs for the deleted merge group branch
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const branch = context.payload.ref;
const activeStatuses = new Set([
'in_progress',
'pending',
'queued',
'requested',
'waiting',
]);
const runs = await github.paginate(
github.rest.actions.listWorkflowRunsForRepo,
{
owner: context.repo.owner,
repo: context.repo.repo,
branch,
event: 'merge_group',
per_page: 100,
},
);
const active = runs.filter(
(run) => run.id !== context.runId && activeStatuses.has(run.status),
);
for (const run of active) {
core.info(`Cancelling ${run.name} run ${run.id} (${run.status})`);
try {
await github.rest.actions.cancelWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id,
});
} catch (error) {
// A run that finished between listing and cancelling answers 409.
if (error.status === 409) {
core.info(`Run ${run.id} completed before it could be cancelled`);
continue;
}
throw error;
}
}
core.notice(`Cancelled ${active.length} run(s) for ${branch}.`);