scripts/git-hooks/pre-push refuses a delete, a remote tip missing locally and
any update whose remote tip isn't an ancestor of the local tip; a new branch
and a fast-forward pass. scripts/install-git-hooks.sh puts it in the common
.git/hooks, refuses a set core.hooksPath or a different hook without
--force-install, and runs a canary through git hook run. scripts/slot-busy.sh
exits 0 CLEAR, 1 BUSY, 2 error. scripts/test-git-guard.sh (61 cases) and the
mutant runner in agents/darkwing/work/queue-211; TOOLS.md gets the usage.
Not run yet: written during Gander's slot (sage-2801).
(cherry picked from commit 20e885dc35)
258 lines
13 KiB
Bash
Executable File
258 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Sandboxed selftests for the push guard, its installer and the busy check
|
|
# (row 211, #426): scripts/git-hooks/pre-push, scripts/install-git-hooks.sh
|
|
# and scripts/slot-busy.sh.
|
|
#
|
|
# Pushes go from a scratch clone and its worktree to a scratch bare remote,
|
|
# never to origin. Git sees no user or system config. The busy check runs
|
|
# against a fake /proc (pids above pid_max) for the exit codes and rules, and
|
|
# against the real /proc with a sleeping stand-in whose argv is `node --test`.
|
|
set -uo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
REPO="$(pwd)"
|
|
unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES
|
|
|
|
SANDBOX="$(cd "$(mktemp -d)" && pwd -P)"
|
|
STANDIN=""
|
|
cleanup() {
|
|
if [ -n "$STANDIN" ]; then kill "$STANDIN" 2>/dev/null; wait "$STANDIN" 2>/dev/null; fi
|
|
rm -rf "$SANDBOX"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
# Status colors: terminal-only, NO_COLOR-respecting; plain when piped.
|
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
C_OK=$'\033[0;32m'; C_FAIL=$'\033[0;31m'; C_RESET=$'\033[0m'
|
|
else
|
|
C_OK=""; C_FAIL=""; C_RESET=""
|
|
fi
|
|
check() {
|
|
if [ "$2" = "0" ]; then PASS=$((PASS+1)); echo "${C_OK}OK${C_RESET} $1"; else FAIL=$((FAIL+1)); echo "${C_FAIL}FAIL${C_RESET} $1"; fi
|
|
}
|
|
has() { [[ "$1" == *"$2"* ]]; }
|
|
|
|
# Git sees no user or system config: no hooks path, no signing, a fixed identity.
|
|
: > "$SANDBOX/gitconfig"
|
|
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL="$SANDBOX/gitconfig"
|
|
export GIT_AUTHOR_NAME=suite GIT_AUTHOR_EMAIL=suite@local GIT_COMMITTER_NAME=suite GIT_COMMITTER_EMAIL=suite@local
|
|
|
|
HOOK_SRC="$REPO/scripts/git-hooks/pre-push"
|
|
INSTALL="$REPO/scripts/install-git-hooks.sh"
|
|
BUSY="$REPO/scripts/slot-busy.sh"
|
|
|
|
B="$SANDBOX/remote.git"
|
|
C="$SANDBOX/clone"
|
|
W="$SANDBOX/wt"
|
|
O="$SANDBOX/other"
|
|
commit() { # $1: repo, $2: file and message
|
|
echo "$2" > "$1/$2" && git -C "$1" add "$2" && git -C "$1" commit -q -m "$2"
|
|
}
|
|
rtip() { git -C "$B" rev-parse -q --verify "refs/heads/$1"; }
|
|
|
|
# ---- the remote, a clone with the guard, a worktree ----
|
|
git init -q --bare -b main "$B"
|
|
git init -q -b main "$SANDBOX/seed"
|
|
commit "$SANDBOX/seed" base
|
|
git -C "$SANDBOX/seed" push -q "$B" main
|
|
git clone -q "$B" "$C"
|
|
|
|
echo "== install =="
|
|
out=$(bash "$INSTALL" --repo "$C" 2>&1); rc=$?
|
|
check "the install into a clean clone exits 0" "$rc"
|
|
check "it reports the install and a passed canary" "$(has "$out" "ok installed the push guard" && has "$out" "canary passed"; echo $?)"
|
|
check "the hook is in the common .git/hooks, with the repository's bytes, executable" \
|
|
"$(cmp -s "$HOOK_SRC" "$C/.git/hooks/pre-push" && [ -x "$C/.git/hooks/pre-push" ]; echo $?)"
|
|
check "no temporary file is left in .git/hooks" \
|
|
"$(ls -A "$C/.git/hooks" | grep -q '^\.pre-push\.install'; [ $? = 1 ]; echo $?)"
|
|
git -C "$C" worktree add -q -b seat/w "$W" origin/main
|
|
check "the worktree's git runs the common hook" \
|
|
"$([ "$(git -C "$W" rev-parse --path-format=absolute --git-path hooks/pre-push)" = "$C/.git/hooks/pre-push" ]; echo $?)"
|
|
|
|
echo "== pushes from the worktree =="
|
|
commit "$W" one
|
|
out=$(git -C "$W" push origin seat/w 2>&1); rc=$?
|
|
check "a new branch passes" "$rc"
|
|
check "the remote branch is at the pushed commit" "$([ "$(rtip seat/w)" = "$(git -C "$W" rev-parse HEAD)" ]; echo $?)"
|
|
|
|
commit "$W" two
|
|
out=$(git -C "$W" push origin seat/w 2>&1); rc=$?
|
|
check "a fast-forward passes" "$rc"
|
|
check "the remote branch moved forward" "$([ "$(rtip seat/w)" = "$(git -C "$W" rev-parse HEAD)" ]; echo $?)"
|
|
|
|
before=$(rtip seat/w)
|
|
git -C "$W" reset -q --hard HEAD~1
|
|
commit "$W" two-rewritten
|
|
after=$(git -C "$W" rev-parse HEAD)
|
|
out=$(git -C "$W" push --force origin seat/w 2>&1); rc=$?
|
|
check "push --force of a rewritten branch is refused" "$([ "$rc" -ne 0 ]; echo $?)"
|
|
check "the refusal is the guard's, and names the ref and both shas" \
|
|
"$(has "$out" "mosaic push guard: refused: refs/heads/seat/w: not a fast-forward" && has "$out" "$before" && has "$out" "$after"; echo $?)"
|
|
check "the remote ref is unchanged after the refused --force" "$([ "$(rtip seat/w)" = "$before" ]; echo $?)"
|
|
|
|
out=$(git -C "$W" push origin +seat/w:seat/w 2>&1); rc=$?
|
|
check "a +refspec push is refused" "$([ "$rc" -ne 0 ] && has "$out" "not a fast-forward"; echo $?)"
|
|
check "the remote ref is unchanged after the refused +refspec" "$([ "$(rtip seat/w)" = "$before" ]; echo $?)"
|
|
|
|
out=$(git -C "$W" push --force-with-lease origin seat/w 2>&1); rc=$?
|
|
check "push --force-with-lease is refused too" "$([ "$rc" -ne 0 ] && has "$out" "not a fast-forward"; echo $?)"
|
|
|
|
out=$(git -C "$W" push origin :seat/w 2>&1); rc=$?
|
|
check "a delete (:ref) is refused" "$([ "$rc" -ne 0 ] && has "$out" "mosaic push guard: refused: refs/heads/seat/w: a delete"; echo $?)"
|
|
out=$(git -C "$W" push --delete origin seat/w 2>&1); rc=$?
|
|
check "a delete (--delete) is refused" "$([ "$rc" -ne 0 ] && has "$out" "a delete"; echo $?)"
|
|
check "the remote branch still exists after the refused deletes" "$([ "$(rtip seat/w)" = "$before" ]; echo $?)"
|
|
|
|
# Another clone moves the remote; this clone never fetches it.
|
|
git clone -q -b seat/w "$B" "$O"
|
|
commit "$O" from-other
|
|
git -C "$O" push -q origin seat/w
|
|
moved=$(rtip seat/w)
|
|
check "the other clone moved the remote, and this clone lacks that commit" \
|
|
"$([ "$moved" != "$before" ] && ! git -C "$C" cat-file -e "$moved^{commit}" 2>/dev/null; echo $?)"
|
|
out=$(git -C "$W" push --force origin seat/w 2>&1); rc=$?
|
|
check "a push over a remote tip missing locally is refused, saying fetch first" \
|
|
"$([ "$rc" -ne 0 ] && has "$out" "isn't in this repository; fetch first"; echo $?)"
|
|
check "the remote ref is unchanged after that refusal" "$([ "$(rtip seat/w)" = "$moved" ]; echo $?)"
|
|
check "the guard fetched nothing" "$(! git -C "$C" cat-file -e "$moved^{commit}" 2>/dev/null; echo $?)"
|
|
|
|
echo "== the hook, fed by hand =="
|
|
z=0000000000000000000000000000000000000000
|
|
hookrun() { (cd "$C" && printf '%s\n' "$1" | sh "$C/.git/hooks/pre-push" origin "$B" 2>&1); }
|
|
out=$(hookrun "refs/heads/x $(git -C "$C" rev-parse HEAD) refs/heads/x"); rc=$?
|
|
check "a short ref line is refused" "$([ "$rc" -ne 0 ] && has "$out" "unreadable ref line"; echo $?)"
|
|
out=$(hookrun "refs/heads/x HEAD refs/heads/x $z"); rc=$?
|
|
check "a non-hex object name is refused" "$([ "$rc" -ne 0 ] && has "$out" "non-hex"; echo $?)"
|
|
out=$(hookrun "refs/heads/x 1234567890123456789012345678901234567890 refs/heads/x $(git -C "$C" rev-parse HEAD)"); rc=$?
|
|
check "a failed ancestor check (an unknown local object) is refused" \
|
|
"$([ "$rc" -ne 0 ] && has "$out" "the ancestor check failed"; echo $?)"
|
|
out=$(printf '' | (cd "$C" && sh "$C/.git/hooks/pre-push" origin "$B" 2>&1)); rc=$?
|
|
check "an empty push passes" "$rc"
|
|
|
|
echo "== installer refusals =="
|
|
fresh() { rm -rf "$1"; git clone -q "$B" "$1"; }
|
|
fresh "$SANDBOX/hp"
|
|
git -C "$SANDBOX/hp" config core.hooksPath .husky
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/hp" 2>&1); rc=$?
|
|
check "the install refuses with core.hooksPath set (exit 2)" "$([ "$rc" = 2 ]; echo $?)"
|
|
check "it says core.hooksPath is set" "$(has "$out" "core.hooksPath is set"; echo $?)"
|
|
check "no hook was written under the hooksPath refusal" \
|
|
"$([ ! -e "$SANDBOX/hp/.git/hooks/pre-push" ] && [ ! -e "$SANDBOX/hp/.husky/pre-push" ]; echo $?)"
|
|
|
|
fresh "$SANDBOX/df"
|
|
printf '#!/bin/sh\nexit 0\n' > "$SANDBOX/df/.git/hooks/pre-push"
|
|
chmod 0755 "$SANDBOX/df/.git/hooks/pre-push"
|
|
ln "$SANDBOX/df/.git/hooks/pre-push" "$SANDBOX/df-old"
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/df" 2>&1); rc=$?
|
|
check "the install refuses a different installed pre-push (exit 2)" \
|
|
"$([ "$rc" = 2 ] && has "$out" "a different pre-push hook is installed"; echo $?)"
|
|
check "the different hook is untouched" \
|
|
"$([ "$(cat "$SANDBOX/df/.git/hooks/pre-push")" = "$(printf '#!/bin/sh\nexit 0')" ]; echo $?)"
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/df" --force-install 2>&1); rc=$?
|
|
check "--force-install replaces it" \
|
|
"$([ "$rc" = 0 ] && has "$out" "replaced a different hook" && cmp -s "$HOOK_SRC" "$SANDBOX/df/.git/hooks/pre-push"; echo $?)"
|
|
check "the replacement was a rename: the old file keeps its bytes" \
|
|
"$([ "$(cat "$SANDBOX/df-old")" = "$(printf '#!/bin/sh\nexit 0')" ]; echo $?)"
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/df" 2>&1); rc=$?
|
|
check "a second install over the same bytes exits 0" "$([ "$rc" = 0 ] && has "$out" "the same bytes"; echo $?)"
|
|
|
|
fresh "$SANDBOX/ln"
|
|
ln -s /dev/null "$SANDBOX/ln/.git/hooks/pre-push"
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/ln" --force-install 2>&1); rc=$?
|
|
check "the install refuses a symlinked pre-push, even with --force-install" "$([ "$rc" = 2 ] && has "$out" "is a symlink"; echo $?)"
|
|
|
|
fresh "$SANDBOX/wd"
|
|
git -C "$SANDBOX/wd" worktree add -q -b seat/x "$SANDBOX/wd-wt" origin/main
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/wd-wt" 2>&1); rc=$?
|
|
check "an install named by a worktree path lands in the common .git/hooks" \
|
|
"$([ "$rc" = 0 ] && cmp -s "$HOOK_SRC" "$SANDBOX/wd/.git/hooks/pre-push"; echo $?)"
|
|
|
|
out=$(GIT_DIR="$SANDBOX/wd/.git" bash "$INSTALL" --repo "$SANDBOX/wd" 2>&1); rc=$?
|
|
check "the install refuses with GIT_DIR set" "$([ "$rc" = 2 ] && has "$out" "GIT_DIR is set"; echo $?)"
|
|
mkdir -p "$SANDBOX/plain"
|
|
out=$(bash "$INSTALL" --repo "$SANDBOX/plain" 2>&1); rc=$?
|
|
check "the install refuses a directory that isn't a checkout" "$([ "$rc" = 2 ]; echo $?)"
|
|
out=$(bash "$INSTALL" --nope 2>&1); rc=$?
|
|
check "an unknown argument exits 4" "$([ "$rc" = 4 ]; echo $?)"
|
|
|
|
echo "== slot-busy, fake /proc =="
|
|
P="$SANDBOX/proc"
|
|
fakeproc() { # $1: pid, $2: cwd, rest: argv
|
|
local pid=$1 cwd=$2; shift 2
|
|
mkdir -p "$P/$pid"
|
|
printf '%s\0' "$@" > "$P/$pid/cmdline"
|
|
ln -sfn "$cwd" "$P/$pid/cwd"
|
|
}
|
|
busyrun() { bash "$BUSY" --proc-root "$P" 2>&1; }
|
|
fakeproc 4194401 /srv node server.mjs
|
|
fakeproc 4194402 /srv grep -r gate .
|
|
fakeproc 4194403 /srv /bin/bash -c "source /home/u/.claude/shell-snapshots/snapshot-bash-1.sh && eval 'node --test x.test.mjs'"
|
|
mkdir -p "$P/4194404"; : > "$P/4194404/cmdline"
|
|
out=$(busyrun); rc=$?
|
|
check "no matching process: exit 0 and CLEAR" "$([ "$rc" = 0 ] && [ "$out" = CLEAR ]; echo $?)"
|
|
check "an agent's shell-snapshot wrapper isn't a match" "$(! has "$out" "4194403"; echo $?)"
|
|
|
|
fakeproc 4194410 /seat/a node --test packages/x/tests/a.test.mjs
|
|
out=$(busyrun); rc=$?
|
|
check "a node --test process: exit 1" "$([ "$rc" = 1 ]; echo $?)"
|
|
check "its BUSY line names pid, rule and cwd" "$(has "$out" "BUSY 4194410 node --test cwd=/seat/a node --test"; echo $?)"
|
|
check "BUSY output never says CLEAR" "$(! has "$out" "CLEAR"; echo $?)"
|
|
ran=$(bash "$BUSY" --proc-root "$P" >/dev/null && echo ran)
|
|
check "\`slot-busy > LOG && step\` doesn't run the step while BUSY" "$([ -z "$ran" ]; echo $?)"
|
|
rm -rf "${P:?}/4194410"
|
|
|
|
rule() { # $1: expected rule, rest: argv
|
|
local want=$1; shift
|
|
rm -rf "${P:?}/4194420"; fakeproc 4194420 /seat/b "$@"
|
|
out=$(busyrun); rc=$?
|
|
check "busy on: $* ($want)" "$([ "$rc" = 1 ] && has "$out" "BUSY 4194420 $want "; echo $?)"
|
|
}
|
|
rule "node --test" /usr/bin/node --experimental-strip-types --test a.test.ts
|
|
rule "gate" bash /mnt/scratch/sage-gate.sh 253
|
|
rule "gate" bash ./gate.sh
|
|
rule "mutant" bash agents/x/work/queue-1/mutants.sh M3
|
|
rule "test script" bash scripts/test-land-work.sh
|
|
rule "docker compose" docker compose -p x up -d
|
|
rule "docker compose" /usr/bin/docker-compose up
|
|
rm -rf "${P:?}/4194420"
|
|
|
|
out=$(bash "$BUSY" --proc-root "$SANDBOX/no-such-proc" 2>&1); rc=$?
|
|
check "a missing proc root: exit 2" "$([ "$rc" = 2 ]; echo $?)"
|
|
mkdir -p "$SANDBOX/empty-proc"
|
|
out=$(bash "$BUSY" --proc-root "$SANDBOX/empty-proc" 2>&1); rc=$?
|
|
check "a proc root with no processes: exit 2" "$([ "$rc" = 2 ]; echo $?)"
|
|
out=$(bash "$BUSY" --nope 2>&1); rc=$?
|
|
check "an unknown argument: exit 2" "$([ "$rc" = 2 ]; echo $?)"
|
|
|
|
echo "== slot-busy, real /proc =="
|
|
mkdir -p "$SANDBOX/bin" "$SANDBOX/standin"
|
|
cat > "$SANDBOX/bin/node" <<'EOF'
|
|
#!/bin/sh
|
|
trap 'exit 0' TERM
|
|
while :; do sleep 0.2; done
|
|
EOF
|
|
chmod +x "$SANDBOX/bin/node"
|
|
(cd "$SANDBOX/standin" && exec "$SANDBOX/bin/node" --test standin.test.mjs) &
|
|
STANDIN=$!
|
|
for _ in $(seq 50); do
|
|
has "$(tr '\0' ' ' < "/proc/$STANDIN/cmdline" 2>/dev/null)" "--test standin" && break
|
|
sleep 0.1
|
|
done
|
|
out=$(bash "$BUSY" 2>&1); rc=$?
|
|
check "with a stand-in node --test running: exit 1" "$([ "$rc" = 1 ]; echo $?)"
|
|
check "the stand-in has a BUSY line with its cwd" \
|
|
"$(printf '%s\n' "$out" | grep -q "^BUSY $STANDIN node --test cwd=$SANDBOX/standin "; echo $?)"
|
|
check "this suite, an ancestor, has no BUSY line" "$(! printf '%s\n' "$out" | grep -q "^BUSY $$ "; echo $?)"
|
|
ran=$(bash "$BUSY" >/dev/null && echo ran)
|
|
check "\`slot-busy > LOG && step\` doesn't run the step with the stand-in up" "$([ -z "$ran" ]; echo $?)"
|
|
kill "$STANDIN" 2>/dev/null; wait "$STANDIN" 2>/dev/null
|
|
gone=$STANDIN; STANDIN=""
|
|
out=$(bash "$BUSY" 2>&1); rc=$?
|
|
check "after the stand-in exits, it has no BUSY line" "$(! printf '%s\n' "$out" | grep -q "^BUSY $gone "; echo $?)"
|
|
check "the real-/proc run exits 0 or 1, never 2" "$([ "$rc" = 0 ] || [ "$rc" = 1 ]; echo $?)"
|
|
|
|
echo "---"
|
|
echo "git-guard selftests: $PASS passed, $FAIL failed"
|
|
[ "$FAIL" -eq 0 ]
|