返回 CodeWhale
ratchet-gate.sh
根目录 / scripts / ratchet-gate.sh
1 #!/usr/bin/env bash
2 # Run one whole-repo ratchet so a pull request is blocked by the debt it adds,
3 # not by the debt it inherits.
4 #
5 # Before 0.10.1 the four budget ratchets in ci.yml were advisory on every pull
6 # request and fatal on push. Every PR looked green, the regression surfaced only
7 # after merge, and main went red (38 of 154 main-push runs green, 09-16..09-22).
8 # This wrapper makes the ratchet block the PR and keeps exactly one escape
9 # hatch: when the base the PR merges into fails the same check, the failure is
10 # inherited debt, so it reports a warning instead of blocking the innocent PR.
11 #
12 # Usage:
13 # scripts/ratchet-gate.sh --name NAME --update "CMD" [--base SHA] -- CHECK...
14 #
15 # --name label used in annotations (e.g. blocking-calls)
16 # --update the exact receipt command that lands the fix in this PR; printed
17 # on failure so the author can regenerate and commit the budget
18 # --base commit to re-run the check on when it fails (the PR's merge
19 # base). Defaults to $RATCHET_BASE_SHA; empty means no base
20 # re-check, so the failure blocks (push, schedule, dispatch).
21 # CHECK... the checker command, run from the repository root, and again
22 # from a detached checkout of --base when a base re-check runs.
23 #
24 # The base re-check needs a second checkout of an older commit. It uses a
25 # throwaway `git worktree` under $RUNNER_TEMP (or mktemp), removed on exit.
26 # That is a CI mechanism: locally, run the checker or scripts/preflight.sh.
27 # Cargo-backed checkers share this checkout's target directory so the base
28 # measurement is an incremental rebuild, not a cold one.
29 set -euo pipefail
30
31 name=""
32 update_cmd=""
33 base="${RATCHET_BASE_SHA:-}"
34 while [[ "$#" -gt 0 ]]; do
35 case "$1" in
36 --name) name="${2:?--name needs a value}"; shift 2 ;;
37 --update) update_cmd="${2:?--update needs a value}"; shift 2 ;;
38 --base) base="${2-}"; shift 2 ;;
39 --) shift; break ;;
40 *)
41 echo "usage: $0 --name NAME --update CMD [--base SHA] -- CHECK..." >&2
42 exit 2
43 ;;
44 esac
45 done
46 if [[ -z "${name}" || -z "${update_cmd}" || "$#" -eq 0 ]]; then
47 echo "usage: $0 --name NAME --update CMD [--base SHA] -- CHECK..." >&2
48 exit 2
49 fi
50
51 root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
52 cd "${root}"
53
54 status=0
55 "$@" || status=$?
56 if [[ "${status}" -eq 0 ]]; then
57 exit 0
58 fi
59
60 receipt() {
61 echo "" >&2
62 echo "To land the fix in this PR: remove the new sites, or if the growth is intended run" >&2
63 echo " ${update_cmd}" >&2
64 echo "and commit the regenerated budget with the reason in the PR description." >&2
65 }
66
67 if [[ -z "${base}" ]]; then
68 echo "::error title=${name} ratchet::${name} budget check failed (exit ${status}). Fix: ${update_cmd}" >&2
69 receipt
70 exit 1
71 fi
72
73 if ! git rev-parse -q --verify "${base}^{commit}" >/dev/null; then
74 git fetch --no-tags --quiet origin "${base}" || true
75 fi
76 if ! git rev-parse -q --verify "${base}^{commit}" >/dev/null; then
77 # Failing closed: without the base we cannot prove the debt is inherited.
78 echo "::error title=${name} ratchet::${name} failed and base ${base} could not be resolved to prove the debt is inherited. Fix: ${update_cmd}" >&2
79 receipt
80 exit 1
81 fi
82
83 echo "[ratchet-gate] ${name} failed on this tree; re-running on base ${base} to tell added debt from inherited debt." >&2
84 scratch="$(mktemp -d "${RUNNER_TEMP:-${TMPDIR:-/tmp}}/ratchet-base.XXXXXX")"
85 base_tree="${scratch}/tree"
86 # shellcheck disable=SC2329 # invoked by the EXIT trap
87 cleanup() {
88 git -C "${root}" worktree remove --force "${base_tree}" >/dev/null 2>&1 || true
89 rm -rf "${scratch}"
90 }
91 trap cleanup EXIT
92 git worktree add --quiet --detach "${base_tree}" "${base}"
93
94 base_status=0
95 (
96 cd "${base_tree}"
97 export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-${root}/target}"
98 "$@"
99 ) || base_status=$?
100
101 if [[ "${base_status}" -ne 0 ]]; then
102 echo "::warning title=${name} ratchet (inherited)::${name} also fails on base ${base} (exit ${base_status}), so this is inherited debt, not added by this PR. Not blocking; main must be fixed with: ${update_cmd}" >&2
103 exit 0
104 fi
105
106 echo "::error title=${name} ratchet::${name} passes on base ${base} but fails with this PR (exit ${status}): this change adds the debt. Fix: ${update_cmd}" >&2
107 receipt
108 exit 1
109
109 lines BASH