What a clone lacked: the gates went green with a thousand skipped tests and said nothing, every login script assumed a worktree-pool slot and its probe account, the client defaulted to the maintainer's account, a docker path and a home directory were in the code, the temp files broke on GNU coreutils, and two generators could only run against a sibling repo. Now the test gates print how many data-gated tests skipped and why; a scripted run logs in as the account its checkout declares in `.probe-identity` or as the three variables name, never a default, and the client refuses a scripted login on any other account; the event and message tables regenerate from vendored inputs, byte for byte; the docs state the real prerequisites. Out of the tree, into the maintainer's own tooling: the worktree pool, its hooks, the perf leg runner, the cross-platform compile and the Vulkan container leg — a way of working, and setups not every machine has. One hook stays: no writes into the install. Two tests that could never run are gone with their subject: the quest-timer geometry diff compared a copy of the window that no longer exists (and joined a chain path onto a folder, so it always skipped), and the FrameXML flag gate now reads the stock files off the player's own patch chain instead of an extracted folder only one machine had. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
138 lines
6.5 KiB
Bash
Executable File
138 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# check.sh — the ROUND's verify (decision 1822): fmt everywhere (cheap), clippy + test scoped to
|
||
# the crates this round's changes can actually affect — the changed crates plus everything that
|
||
# depends on them. The workspace-wide chain is `scripts/gates.sh`'s job, paid at sync→land.
|
||
#
|
||
# Why this verb exists: the fleet was paying the FULL chain per round — measured over the fortnight
|
||
# to 2026-09-01 at 3,028 `cargo test --workspace` runs (72 h of wall time) and 1,572 workspace-wide
|
||
# clippys (33 h) against only 393 actual gate moments, one session hitting the whole suite 161
|
||
# times in a day while editing a single leaf crate. 59 % of edits land in `benilla-app`, which
|
||
# nothing but the `benilla` bin depends on: the honest scope of a round is 1–3 crates, not 19.
|
||
#
|
||
# Scope rule — conservative by construction, unknown means FULL, never "probably fine":
|
||
# crates/<dir>/** → that crate (owner read off cargo metadata, not the dir name)
|
||
# assets/ui/** → benilla-app (ui_script's shipped-UI tests read it at runtime)
|
||
# *.md → no gate can change (gates.sh's docs-only rule)
|
||
# anything else → escalate: exec scripts/gates.sh (workspace manifests, .cargo/,
|
||
# rust-toolchain, scripts/, shaders outside crates, …)
|
||
# Change set = fork point vs main + staged + unstaged + untracked — the round's work, not the last
|
||
# edit. CHECK_FULL=1 skips straight to gates.sh.
|
||
#
|
||
# Same fail-fast discipline as gates.sh: never compose `cargo … | tail` pipelines by hand — the
|
||
# pipe's exit code masks the gate's (that bug bit twice before gates.sh existed).
|
||
set -uo pipefail
|
||
|
||
# Gate the tree you are STANDING IN (gates.sh learned this; same rule here).
|
||
here="$(cd "$(dirname "$0")/.." && pwd)"
|
||
root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||
[ -n "$root" ] && [ -f "$root/scripts/check.sh" ] || root="$here"
|
||
cd "$root" || exit 1
|
||
|
||
if [ "${CHECK_FULL:-}" = "1" ]; then
|
||
exec "$root/scripts/gates.sh"
|
||
fi
|
||
|
||
# ── The round's change set ───────────────────────────────────────────────────────────────────────
|
||
base="$(git merge-base HEAD main 2>/dev/null || git rev-parse HEAD)"
|
||
changed="$( { git diff --name-only "$base" HEAD 2>/dev/null
|
||
git diff --name-only HEAD 2>/dev/null
|
||
git ls-files --others --exclude-standard 2>/dev/null; } | sort -u )"
|
||
|
||
if [ -z "$changed" ]; then
|
||
echo "check: no changes against main and a clean tree — nothing to verify"
|
||
exit 0
|
||
fi
|
||
|
||
# ── Map files → crate dirs; anything unmappable escalates to the full chain ──────────────────────
|
||
dirs=""
|
||
full_reason=""
|
||
while IFS= read -r f; do
|
||
case "$f" in
|
||
docs/* | *.md) ;; # provably gate-inert (gates.sh's docs-only rule)
|
||
assets/ui/*) dirs="$dirs benilla-app-DIR:crates/benilla-app" ;;
|
||
crates/*/*) d="${f#crates/}"; dirs="$dirs DIR:crates/${d%%/*}" ;;
|
||
*) full_reason="$f" ;;
|
||
esac
|
||
done <<EOF
|
||
$changed
|
||
EOF
|
||
|
||
if [ -n "$full_reason" ]; then
|
||
echo "check: '$full_reason' is outside the crate map — the whole workspace could be affected."
|
||
echo "check: escalating to the full chain: scripts/gates.sh"
|
||
exec "$root/scripts/gates.sh"
|
||
fi
|
||
|
||
dirlist="$(printf '%s\n' $dirs | sed 's/.*DIR://' | sort -u)"
|
||
if [ -z "$dirlist" ]; then
|
||
echo "check: docs-only round ($(printf '%s\n' "$changed" | wc -l | tr -d ' ') file(s)) — no gate can change; fmt only"
|
||
cargo fmt --all -- --check || exit 1
|
||
echo "check: green (docs only)"
|
||
exit 0
|
||
fi
|
||
|
||
# ── Changed crates → reverse-dependency closure, off cargo metadata (dir names are not package
|
||
# names here: crates/mpq is benilla-mpq) ──────────────────────────────────────────────────────
|
||
pkgs="$(python3 - "$root" $dirlist <<'PY'
|
||
import json, subprocess, sys, os
|
||
root, dirs = sys.argv[1], set(sys.argv[2:])
|
||
meta = json.loads(subprocess.run(
|
||
["cargo", "metadata", "--format-version", "1", "--no-deps"],
|
||
capture_output=True, text=True, cwd=root, check=True).stdout)
|
||
by_dir, deps = {}, {}
|
||
names = {p["name"] for p in meta["packages"]}
|
||
for p in meta["packages"]:
|
||
by_dir[os.path.relpath(os.path.dirname(p["manifest_path"]), root)] = p["name"]
|
||
deps[p["name"]] = {d["name"] for d in p["dependencies"] if d["name"] in names}
|
||
changed = {by_dir[d] for d in dirs if d in by_dir}
|
||
missing = [d for d in dirs if d not in by_dir]
|
||
if missing: # a changed dir cargo doesn't know: not our map's to scope
|
||
print("FULL " + " ".join(missing)); sys.exit(0)
|
||
scope = set(changed)
|
||
grew = True
|
||
while grew:
|
||
grew = False
|
||
for name, ds in deps.items():
|
||
if name not in scope and ds & scope:
|
||
scope.add(name); grew = True
|
||
print(" ".join(sorted(scope)))
|
||
PY
|
||
)" || { echo "check: cargo metadata failed — falling back to the full chain"; exec "$root/scripts/gates.sh"; }
|
||
|
||
case "$pkgs" in FULL*)
|
||
echo "check: changed path(s) not in the workspace map (${pkgs#FULL }) — full chain"
|
||
exec "$root/scripts/gates.sh" ;;
|
||
esac
|
||
|
||
pflags=""
|
||
for p in $pkgs; do pflags="$pflags -p $p"; done
|
||
echo "check: scope = $pkgs"
|
||
echo "check: (changed crates + everything that depends on them; full chain still runs at sync→land)"
|
||
|
||
log="$(mktemp "${TMPDIR:-/tmp}/benilla-check.XXXXXX")"
|
||
skips="$(mktemp "${TMPDIR:-/tmp}/benilla-check-skips.XXXXXX")"
|
||
trap 'rm -f "$log" "$skips"' EXIT
|
||
run() {
|
||
local name="$1"; shift
|
||
if ! "$@" >"$log" 2>&1; then
|
||
echo "CHECK FAILED: $name"
|
||
tail -30 "$log"
|
||
exit 1
|
||
fi
|
||
echo "check ok: $name"
|
||
}
|
||
|
||
run fmt cargo fmt --all -- --check
|
||
run clippy cargo clippy $pflags --all-targets -- -D warnings
|
||
run test env BENILLA_SKIP_LOG="$skips" cargo test $pflags
|
||
# The data-gated tests skip without the install or the addon corpus, and libtest swallows the
|
||
# line; the resolver logs each skip to $BENILLA_SKIP_LOG and this counts them (gates.sh says why).
|
||
if [ -s "$skips" ]; then
|
||
echo " test: $(wc -l <"$skips" | tr -d ' ') data-gated tests SKIPPED on this machine —"
|
||
sort "$skips" | uniq -c | sort -rn | sed 's/^ *\([0-9]*\) \(.*\)/ \1 × \2/'
|
||
echo " (they run where the data is: docs/CONTRIBUTING.md, \"Setting up\")"
|
||
fi
|
||
|
||
echo "CHECK GREEN (scoped: $pkgs)"
|
||
echo " (land still pays the full chain once: scripts/gates.sh — memoized, so an already-gated tree is free)"
|