#!/usr/bin/env bash # # check-workflow-concurrency-selftest.sh # # Proves `check-workflow-concurrency.sh` can fail. A check that cannot fail is # decoration, and this repo has a documented history of gates that passed # because nothing they guarded was ever read. # # First the positive control: the check passes on an untouched copy, so the # failures below are each caused by the change made for it. Then every way the # property can break is exercised against a scratch copy, never the working # tree, and each must fail with its own error, not merely fail: # # 1. a workflow with no top-level `concurrency:` block; # 2. a group that does not vary by `github.ref` — which would serialise every # branch against every other, worse than having no group at all; # 3. a group per ref alone, the form every workflow had until 2026-11-10 — # each push to main then cancels the run before it, and a workflow main # touches more often than its runner's queue drains never finishes; # 4. `cancel-in-progress: false` — on Forgejo, every run of the group held # behind the one before it, and none ever dropped. # # In a script rather than inline in the workflow on purpose. The first version # embedded a Python heredoc in a `run:` block, and the heredoc body sat at # column zero, which made the workflow file invalid YAML. An unparseable # workflow means no CI at all — the exact failure this change exists to # prevent, reintroduced by the fix for it. set -euo pipefail cd "$(dirname "$0")/.." root=$(pwd) target=.github/workflows/repo-checks.yml tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT cp -R .github scripts "$tmp/" # ── 0. the untouched copy passes ──────────────────────────────────────────── if ! ( cd "$tmp" && ./scripts/check-workflow-concurrency.sh ) > "$tmp/out" 2>&1; then cat "$tmp/out" echo "::error::the check fails on an untouched copy, so its failures below would prove nothing" exit 1 fi # Rewrites the first line of the target matching a pattern, in the scratch copy. rewrite() { cp -f "$root/$target" "$tmp/$target" python3 - "$tmp/$target" "$1" "$2" <<'PY' import re, sys p, pattern, repl = sys.argv[1:4] s = open(p).read() out = re.sub(pattern, repl, s, count=1, flags=re.M) assert out != s, f"fixture unchanged: nothing matched {pattern!r}" open(p, "w").write(out) PY } # Runs the check on the scratch copy: it must fail, saying $2. must_refuse() { if ( cd "$tmp" && ./scripts/check-workflow-concurrency.sh ) > "$tmp/out" 2>&1; then echo "::error::the check passed $1" exit 1 fi if ! grep -qF "$2" "$tmp/out"; then cat "$tmp/out" echo "::error::the check failed on $1, but not with the error for it ($2)" exit 1 fi } # ── 1. remove the block entirely ──────────────────────────────────────────── rewrite '^concurrency:\n( .*\n)+' '' must_refuse "a workflow with no concurrency block" "declares no top-level" # ── 2. a group that does not vary by ref ──────────────────────────────────── rewrite '^( group:).*$' '\1 fixed-for-the-whole-repo' must_refuse "a group that does not vary by ref" "does not vary by" # ── 3. a group per ref alone: main's runs cancelled by the next push ──────── rewrite '^( group:).*$' '\1 ${{ github.workflow }}-${{ github.ref }}' must_refuse "a group per ref alone" "does not give a push a group of its own" # ── 4. cancel-in-progress: false ──────────────────────────────────────────── rewrite '^( cancel-in-progress:).*$' '\1 false' must_refuse "cancel-in-progress: false" "does not set \`cancel-in-progress: true\`" echo "self-test: the check passes an untouched copy and rejects a missing block, a fixed group, a group per ref alone and cancel-in-progress: false"