#!/usr/bin/env bash # test_build_hub_tree_gate.sh — build-hub.sh refuses a stale or dirty tree (R-1021). # # Builds a tiny scratch repo (a bare "origin" + a clone holding the hub's directory shape), copies build-hub.sh next # to it (so its workspace/ is scratch too) and runs it with FELHOM_HUB_REPO_DIR pointing at the clone and a STUB # `docker` first on PATH: the real Docker is never called, nothing is built or pushed. Each case asserts the exit code # AND a line of output: # dirty (an untracked file) -> refused, "not clean" # dirty (a modified tracked file) -> refused, "not clean" # behind origin, the pull blocked -> refused (local commit diverged: "pull --ff-only failed") # ahead of origin (unpushed commit) -> refused, "is not origin/main" # behind origin, the pull clean -> passes, and the Commit line names the NEW head (read after the pull) # clean, at origin/main -> passes, the stub docker reached # Run: bash scripts/test_build_hub_tree_gate.sh (exit 0 all cases right · 1 a case wrong) set -uo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" WS="$(mktemp -d)" trap 'rm -rf "${WS:?}"' EXIT fails=0 mkdir -p "$WS/bin" "$WS/build" printf '#!/bin/sh\necho "STUB docker $*"\n' > "$WS/bin/docker"; chmod +x "$WS/bin/docker" cp "$HERE/build-hub.sh" "$WS/build/build.sh" fresh() { rm -rf "${WS:?}/origin.git" "${WS:?}/clone" "${WS:?}/other" "${WS:?}/build/workspace" git init -q --bare -b main "$WS/origin.git" git clone -q "$WS/origin.git" "$WS/clone" 2>/dev/null (cd "$WS/clone" && mkdir -p hub/cmd/hub hub/internal/api hub/internal/store hub/internal/web \ && printf 'module x\n\ngo 1.22\n' > hub/go.mod && echo a > hub/cmd/hub/a.txt \ && git add -A && git -c user.name=t -c user.email=t@t commit -q -m init && git push -q origin main) git clone -q "$WS/origin.git" "$WS/other" 2>/dev/null } upstream_commit() { # a new commit on origin, made from another clone (cd "$WS/other" && git pull -q && echo "$1" > "hub/cmd/hub/$1.txt" && git add -A \ && git -c user.name=t -c user.email=t@t commit -q -m "$1" && git push -q origin main) } run() { # name, expected rc, required output fragment local out rc out=$(cd "$WS/build" && PATH="$WS/bin:$PATH" FELHOM_HUB_REPO_DIR="$WS/clone" bash ./build.sh 9.9.9 2>&1); rc=$? if [[ $rc -eq $2 && "$out" == *"$3"* ]]; then echo " ok $1 (rc=$rc)" else echo " XX $1 (rc=$rc, expected $2; wanted: $3)"; echo "$out" | tail -8 | sed 's/^/ /'; fails=$((fails+1)) fi } fresh; touch "$WS/clone/leftover.txt" run "dirty: an untracked file" 1 "the tree is not clean" fresh; echo b >> "$WS/clone/hub/cmd/hub/a.txt" run "dirty: a modified tracked file" 1 "the tree is not clean" fresh; upstream_commit up1; (cd "$WS/clone" && echo l > hub/cmd/hub/local.txt && git add -A && git -c user.name=t -c user.email=t@t commit -q -m local) run "behind origin and the pull blocked (diverged)" 1 "pull --ff-only failed" fresh; (cd "$WS/clone" && echo l > hub/cmd/hub/local.txt && git add -A && git -c user.name=t -c user.email=t@t commit -q -m local) run "ahead of origin (an unpushed commit)" 1 "is not origin/main" fresh; upstream_commit up2; NEW=$(git -C "$WS/origin.git" rev-parse --short main) run "behind origin, clean pull: Commit is the NEW head" 0 "Commit: ${NEW}" fresh run "clean, at origin/main: builds (stub docker)" 0 "STUB docker build" if [[ $fails -gt 0 ]]; then echo "build-hub tree gate: $fails case(s) WRONG"; exit 1; fi echo "build-hub tree gate OK — 6 case(s)"