Files
computer-playbook/scripts/github/cancel/branch_runs.sh
T
kevinveenbirkenbachandClaude Opus 5 fb54aa1451 fix(ci): force-cancel superseded runs that ignore the cancel
A cancel request does not stop a running step. The runner asks it to end, and a step that ignores the signal keeps going, bounded only by its own timeout-minutes -- 350 for a deploy. Meanwhile the concurrency group holds the newer run on pending until the occupant is terminal, not until it is cancel-requested. Measured on run 32859801382: the cancel was accepted at 15:27:45 and thirteen minutes later ten deploy jobs were still running and the newer run still had not started.

The sweep now waits FORCE_CANCEL_AFTER_SECONDS and force-cancels whatever is still not completed. Cleanup steps are skipped for those, which is the price: the artefacts of an already superseded run help nobody. Only entry-cancel-superseded.yml sets the variable -- a closed pull request and a deleted branch have nothing waiting on the group, so they keep cancelling and walking away.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 18:07:00 +02:00

55 lines
1.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
: "${BRANCH:?Missing BRANCH}"
: "${GH_TOKEN:?Missing GH_TOKEN}"
: "${REPOSITORY:?Missing REPOSITORY}"
CURRENT_RUN_ID="${CURRENT_RUN_ID:-}"
INCLUDE_PATHS="${INCLUDE_PATHS:-}"
KEEP_NEWEST_PER="${KEEP_NEWEST_PER:-}"
FORCE_CANCEL_AFTER_SECONDS="${FORCE_CANCEL_AFTER_SECONDS:-}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/github/cancel/lib.sh
source "${SCRIPT_DIR}/lib.sh"
require_tools
validate_include_paths
validate_keep_newest_per
validate_force_cancel_after
echo "Searching active workflow runs for branch: ${BRANCH}"
runs="$(collect_runs)"
run_ids="$(
printf '%s\n' "${runs}" | jq -s -r \
--arg branch "${BRANCH}" \
--arg current_run_id "${CURRENT_RUN_ID}" \
--arg include_paths "${INCLUDE_PATHS}" \
--arg keep_newest "${KEEP_NEWEST_PER}" '
def drop_newest: sort_by([(.run_started_at // .created_at), .id]) | .[:-1];
def allowlist:
$include_paths | split("\n") | map(gsub("^\\s+|\\s+$"; "")) | map(select(length > 0));
allowlist as $paths
| unique_by(.id)
| map(
select(.path != ".github/workflows/entry-pr-closed-cancel-workflows.yml")
| select($current_run_id == "" or (.id | tostring) != $current_run_id)
| select($include_paths == "" or ((.path // "") | IN($paths[])))
| select(
.head_branch == $branch
or any(.pull_requests[]?; (.head.ref // "") == $branch)
)
)
| (if $keep_newest == "all" then drop_newest
elif $keep_newest == "event" then
group_by(.event // "") | map(drop_newest) | flatten
else . end)
| .[].id
'
)"
cancel_all "${run_ids}"