koopa-admin-log/scripts/dossiers-ngi/smoke-branch-tree.sh

138 lines
5.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Local smoke: temp origin with extra branches → tree JSON + stub rebuild → cleanup.
# Does not touch origin on git.ngi-0.eu.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SCRIPT="$ROOT/scripts/dossiers-ngi/branch-status-hourly.py"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/tdpd-smoke-XXXXXX")"
cleanup() { rm -rf "$TMP"; }
trap cleanup EXIT
echo "TMP=$TMP"
BARE="$TMP/origin.git"
CLONE="$TMP/tdpd"
EDITIONS="$TMP/editions"
OUT="$TMP/branch-status.json"
git init --bare "$BARE" >/dev/null
git clone "$BARE" "$CLONE" >/dev/null 2>&1
cd "$CLONE"
git config user.email "smoke@example.test"
git config user.name "smoke"
echo "root" > README
git add README
git commit -m "main root" >/dev/null
git branch -M main
git push -u origin main >/dev/null 2>&1
# ladder: main → feat/a → feat/b
git checkout -b feat/a >/dev/null 2>&1
echo a > a.txt && git add a.txt && git commit -m "feat a" >/dev/null
git push -u origin feat/a >/dev/null 2>&1
git checkout -b feat/b >/dev/null 2>&1
echo b > b.txt && git add b.txt && git commit -m "feat b" >/dev/null
git push -u origin feat/b >/dev/null 2>&1
# sibling off main
git checkout main >/dev/null 2>&1
git checkout -b smoke/temp-sibling >/dev/null 2>&1
echo s > s.txt && git add s.txt && git commit -m "smoke sibling" >/dev/null
git push -u origin smoke/temp-sibling >/dev/null 2>&1
# stub freeze: write website/build/index.html
FREEZE_CMD='mkdir -p "{root}/website/build" && printf "%s\n" "<!DOCTYPE html><html><body><h1>{branch}</h1></body></html>" > "{root}/website/build/index.html"'
mkdir -p "$EDITIONS"
# seed a Makefile so non-stub path would find one; stub overrides via FREEZE_CMD
FETCH=0 TDPD_ROOT="$CLONE" OUT="$OUT" EDITIONS_ROOT="$EDITIONS" \
FREEZE_CMD="$FREEZE_CMD" REBUILD=1 \
python3 "$SCRIPT" --rebuild --no-fetch
python3 - "$OUT" <<'PY'
import json, sys
from pathlib import Path
data = json.loads(Path(sys.argv[1]).read_text())
branches = {b["branch"]: b for b in data["branches"]}
assert "main" in branches
assert "feat/a" in branches
assert "feat/b" in branches
assert "smoke/temp-sibling" in branches
assert branches["feat/a"]["parent"] == "main", branches["feat/a"]
assert branches["feat/b"]["parent"] == "feat/a", branches["feat/b"]
assert branches["smoke/temp-sibling"]["parent"] == "main", branches["smoke/temp-sibling"]
for b in data["branches"]:
assert b.get("freeze_ok") is True, b
# prefixes semi-graphic
prefs = {b["branch"]: b["prefix"] for b in data["branches"]}
assert any("├──" in p or "└──" in p for p in prefs.values()), prefs
# editions built
print("OK tree:")
for b in data["branches"]:
print(f" {b['prefix']}{b['branch']} @ {b['tip']} -> {b['edition']} parent={b['parent']!r}")
print("rebuild", data.get("rebuild"))
PY
# editions exist with stub content
test -f "$EDITIONS/feat-a/index.html"
test -f "$EDITIONS/smoke-temp-sibling/index.html"
grep -q "feat/a" "$EDITIONS/feat-a/index.html"
grep -q "smoke/temp-sibling" "$EDITIONS/smoke-temp-sibling/index.html"
test "$(cat "$EDITIONS/feat-a/.edition-freeze-ok")" = "1"
# red ✗ when last freeze of this tip failed
printf '0\n' > "$EDITIONS/feat-a/.edition-freeze-ok"
printf 'error:smoke\n' > "$EDITIONS/feat-a/.edition-freeze-error"
FETCH=0 TDPD_ROOT="$CLONE" OUT="$OUT" EDITIONS_ROOT="$EDITIONS" \
FREEZE_CMD="$FREEZE_CMD" \
python3 "$SCRIPT" --no-fetch
python3 - "$OUT" <<'PY'
import json, sys
from pathlib import Path
data = json.loads(Path(sys.argv[1]).read_text())
rows = {b["branch"]: b for b in data["branches"]}
assert rows["feat/a"]["freeze_ok"] is False, rows["feat/a"]
assert rows["feat/a"].get("freeze_error") == "error:smoke", rows["feat/a"]
assert rows["main"]["freeze_ok"] is True, rows["main"]
print("OK freeze_ok false after failed mark")
PY
printf '1\n' > "$EDITIONS/feat-a/.edition-freeze-ok"
rm -f "$EDITIONS/feat-a/.edition-freeze-error"
# flask alias: matching tip + stale HTML must copy from DEFAULT_BRANCH
MAIN_TIP="$(tr -d '\n' < "$EDITIONS/main/.edition-tip")"
mkdir -p "$EDITIONS/flask"
printf '%s\n' "$MAIN_TIP" > "$EDITIONS/flask/.edition-tip"
printf '%s\n' '<html>stale-flask</html>' > "$EDITIONS/flask/index.html"
printf '%s\n' '2026-09-30T17:35:26+02:00' > "$EDITIONS/flask/.edition-built-at"
FETCH=0 TDPD_ROOT="$CLONE" OUT="$OUT" EDITIONS_ROOT="$EDITIONS" \
FREEZE_CMD="$FREEZE_CMD" REBUILD=1 DEFAULT_BRANCH=main \
python3 "$SCRIPT" --rebuild --no-fetch
grep -q "main" "$EDITIONS/flask/index.html"
if grep -q "stale-flask" "$EDITIONS/flask/index.html"; then
echo "FAIL flask alias kept stale HTML"
exit 1
fi
test "$(tr -d '\n' < "$EDITIONS/flask/.edition-freeze-ok")" = "1"
test "$(tr -d '\n' < "$EDITIONS/flask/.edition-built-at")" = "$(tr -d '\n' < "$EDITIONS/main/.edition-built-at")"
echo "OK flask alias copied after stale skip"
# delete smoke branch on origin; fetch --prune drops remote-tracking ref
cd "$CLONE"
git push origin --delete smoke/temp-sibling >/dev/null 2>&1
git branch -D smoke/temp-sibling >/dev/null 2>&1 || true
TDPD_ROOT="$CLONE" OUT="$OUT" EDITIONS_ROOT="$EDITIONS" \
FREEZE_CMD="$FREEZE_CMD" \
python3 "$SCRIPT"
python3 - "$OUT" <<'PY'
import json, sys
from pathlib import Path
data = json.loads(Path(sys.argv[1]).read_text())
names = {b["branch"] for b in data["branches"]}
assert "smoke/temp-sibling" not in names, names
assert names == {"main", "feat/a", "feat/b"}, names
print("OK smoke branch removed from tree")
PY
echo "SMOKE_OK"