OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / tools / ci-gates-local.sh

53 lines · bash ref:
#!/bin/bash
# ci-gates-local.sh [REV=HEAD] -- every gate-* step of .pear-git/ci.yaml, on a CLEAN
# checkout of REV, with CI's environment, BEFORE pushing. Prints the failures.
#
# Why: the dev tree is not the commit. 2026-10-07 a mis-spliced commit passed every
# local gate (the dev tree held the fix uncommitted) and failed CI; two CI rounds were
# lost one gate at a time. And without CI=true the editor prefs leak in -- two
# rotation gates "failed" locally that pass in CI.
#
# Order as CI: checkout -> overlay sync -> import -> gates.
#
# THE TREE IS A PERSISTENT DETACHED WORKTREE (~/.cache/hyper-tps-ci-worktree), not a
# fresh `git archive` (user 2026-10-13: "is it possible to speed up this suite?"). The
# archive ran the hyper-overlay smudge filter on EVERY overlay file -- ~20 min per run,
# against 12 min for all the gates -- and extracting over a reused tree never deleted a
# file the commit had removed, so a stale script could pass a gate CI would fail.
# `checkout --detach -f REV` rewrites only what changed (the filter runs on changed
# overlay files only) and removes what the commit removed; `clean -fd` drops untracked
# leftovers but keeps ignored ones (.godot, the import cache). Gate logs live OUTSIDE
# the tree (~/.cache/hyper-tps-ci-logs), so nothing they write is ever in it.
set -u
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REV="${1:-HEAD}"
SHA="$(git -C "$REPO" rev-parse --verify "$REV^{commit}")" || exit 1
T="${CI_TREE:-$HOME/.cache/hyper-tps-ci-worktree}"
L="${CI_LOGS:-$HOME/.cache/hyper-tps-ci-logs}"
mkdir -p "$L"
t0=$(date +%s)
if [ -e "$T/.git" ]; then
  git -C "$T" checkout --detach -f "$SHA" -q || exit 1
  git -C "$T" clean -fdq || exit 1
else
  git -C "$REPO" worktree add --detach "$T" "$SHA" -q || exit 1
  [ -d "$T/.godot" ] || cp -r "$REPO/.godot" "$T/" 2>/dev/null   # a warm import cache
fi
echo "-- checkout $(git -C "$T" rev-parse --short HEAD): $(( $(date +%s) - t0 )) s"
cd "$T" || exit 1
pgh overlay sync > "$L/overlay.log" 2>&1 || echo "overlay sync failed (see $L/overlay.log)"
GODOT=$(grep -m1 -o '/[^ "]*godot.linuxbsd.editor.x86_64' .pear-git/ci.yaml)
"$GODOT" --headless --import > "$L/import.log" 2>&1
echo "-- overlay sync + import: $(( $(date +%s) - t0 )) s"
awk '/^  - name: gate-/{n=$3} n && /^    run: "/{sub(/^    run: "/,""); sub(/"$/,""); print n "\t" $0; n=""}' \
  .pear-git/ci.yaml > "$L/gates.tsv"
: > "$L/gates_result.txt"
while IFS=$'\t' read -r name cmd; do
  cmd=${cmd//\\\"/\"}
  CI=true PEAR_GIT=true REPO_NAME=hyper-tps CHANNEL=master \
    timeout 300 bash -c "$cmd" > "$L/gate_$name.log" 2>&1 < /dev/null
  echo "$? $name" >> "$L/gates_result.txt"
done < "$L/gates.tsv"
echo "== $(git -C "$REPO" rev-parse --short "$SHA"): $(grep -c '^0 ' "$L/gates_result.txt") / $(wc -l < "$L/gates_result.txt") gates passed in $(( $(date +%s) - t0 )) s (logs: $L/gate_<name>.log)"
grep -v '^0 ' "$L/gates_result.txt" && exit 1 || exit 0