#!/usr/bin/env bash # # check-workflow-concurrency.sh # # Every workflow must declare a concurrency group, so a push supersedes the run # before it instead of queueing behind it. # # Why this is a gate and not a convention. None of the 15 workflows declared # `concurrency:` until 2026-10-05. A session pushing four versions of a branch # therefore queued four complete sets of runs, all of which had to drain: one # PR's 21 contexts settled none in 70 minutes behind a backlog of 25 runs that # grew faster than it cleared. The repo's CI became unusable for every other # session, and two PRs were merged on locally-executed equivalents instead of # on the gate. A gate that cannot run is not a gate. # # One omitted `concurrency:` block reintroduces that for the whole repo, and it # is invisible: the workflow still passes, it just queues forever under load. # So it is checked rather than remembered. # # Runs in seconds with no toolchain, as a step inside an existing job — adding # a job of its own would spend runner capacity to protect runner capacity. set -euo pipefail cd "$(dirname "$0")/.." fail=0 count=0 for f in .github/workflows/*.yml; do count=$((count + 1)) name=$(basename "$f") # A top-level key, so match at column zero: a `concurrency:` nested under a # job is a different (and legal) thing that does not bound the workflow. if ! grep -qE '^concurrency:' "$f"; then echo "::error file=$f::$name declares no top-level \`concurrency:\` block." echo " Add:" echo " concurrency:" echo " group: \${{ github.workflow }}-\${{ github.ref }}" echo " cancel-in-progress: true" fail=1 continue fi # The group must vary by ref. A fixed group serialises every branch against # every other, which is worse than no group at all: one busy branch then # blocks the repo instead of only itself. if ! grep -A3 -E '^concurrency:' "$f" | grep -qE 'group:.*github\.ref'; then echo "::error file=$f::$name has a concurrency group that does not vary by \`github.ref\`." echo " A fixed group serialises unrelated branches against each other." fail=1 fi done if [ "$count" -eq 0 ]; then echo "::error::no workflows found — this check looked in the wrong place." exit 1 fi if [ "$fail" -ne 0 ]; then echo echo "checked $count workflow(s): at least one would queue instead of superseding." exit 1 fi echo "workflow concurrency: $count workflow(s), each grouped per ref."