#!/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" "

{branch}

" > "{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' 'stale-flask' > "$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"