fix(dossiers-ngi): update README and scripts for branch status

This commit is contained in:
Hernâni Marques 2026-10-01 11:39:45 +02:00
parent 7397cddb83
commit 261c25c87f
No known key found for this signature in database
8 changed files with 188 additions and 15 deletions

View file

@ -301,6 +301,42 @@ def write_build_meta(editions: Path, slug: str, seconds: float) -> None:
(dest / ".edition-build-seconds").write_text(f"{seconds:.3f}\n", encoding="utf-8")
def write_freeze_status(editions: Path, slug: str, ok: bool, error: str = "") -> None:
dest = editions / slug
dest.mkdir(parents=True, exist_ok=True)
(dest / ".edition-freeze-ok").write_text("1\n" if ok else "0\n", encoding="utf-8")
errp = dest / ".edition-freeze-error"
if ok or not error:
if errp.exists():
errp.unlink()
return
errp.write_text(error[:500] + "\n", encoding="utf-8")
def freeze_ok_for(editions: Path | None, slug: str, tip_full: str) -> tuple[bool, str]:
"""Current origin tip freeze succeeded (green ✓) or not (red ✗)."""
if editions is None or not tip_full:
return False, ""
dest = editions / slug
err = ""
errp = dest / ".edition-freeze-error"
if errp.is_file():
err = errp.read_text(encoding="utf-8").strip()
recorded = tip_recorded(editions, slug)
okp = dest / ".edition-freeze-ok"
if okp.is_file():
file_ok = okp.read_text(encoding="utf-8").strip().lower() in (
"1",
"true",
"ok",
"yes",
)
else:
file_ok = (dest / ".edition-built-at").is_file()
ok = bool(file_ok and recorded == tip_full and (dest / "index.html").is_file())
return ok, err
def cleanup_stale_editions(editions: Path, tips: dict[str, str]) -> list[str]:
"""Drop edition dirs whose branch tip is gone (keep flask alias)."""
keep = {edition_slug(b) for b in tips} | {"flask"}
@ -424,12 +460,10 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict:
dest = editions / slug
t0 = time.perf_counter()
force = os.environ.get("FORCE_REBUILD", "").strip() in ("1", "true", "yes")
if (
not force
and tip_recorded(editions, slug) == tip
and (dest / "index.html").is_file()
):
if not force and freeze_ok_for(editions, slug, tip)[0]:
inject_bar(dest)
if not (dest / ".edition-freeze-ok").is_file():
write_freeze_status(editions, slug, True)
return {"status": "skipped", "seconds": round(time.perf_counter() - t0, 3)}
wt: Path | None = None
@ -441,10 +475,12 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict:
git(repo, "worktree", "prune", check=False)
cp = git(repo, "worktree", "add", "--detach", str(wt), tip, check=False)
if cp.returncode != 0:
st = f"error:worktree:{cp.stderr.strip()[:200]}"
ensure_placeholder(editions, slug, tip)
write_freeze_status(editions, slug, False, st)
inject_bar(dest)
return {
"status": f"error:worktree:{cp.stderr.strip()[:200]}",
"status": st,
"seconds": round(time.perf_counter() - t0, 3),
}
@ -452,6 +488,7 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict:
build = wt / "website" / "build"
if not build.is_dir():
ensure_placeholder(editions, slug, tip)
write_freeze_status(editions, slug, False, "error:no-build")
inject_bar(dest)
return {"status": "error:no-build", "seconds": round(time.perf_counter() - t0, 3)}
@ -460,6 +497,8 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict:
".edition-tip",
".edition-built-at",
".edition-build-seconds",
".edition-freeze-ok",
".edition-freeze-error",
}
for child in list(dest.iterdir()):
if child.name in keep_meta:
@ -477,13 +516,16 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict:
secs = round(time.perf_counter() - t0, 3)
(dest / ".edition-tip").write_text(tip + "\n", encoding="utf-8")
write_build_meta(editions, slug, secs)
write_freeze_status(editions, slug, True)
inject_bar(dest)
return {"status": "built", "seconds": secs}
except Exception as exc: # noqa: BLE001 — hourly must continue
st = f"error:{type(exc).__name__}:{exc}"
ensure_placeholder(editions, slug, tip)
write_freeze_status(editions, slug, False, st)
inject_bar(dest)
return {
"status": f"error:{type(exc).__name__}:{exc}",
"status": st,
"seconds": round(time.perf_counter() - t0, 3),
}
finally:
@ -510,10 +552,19 @@ def sync_flask_alias(editions: Path, tips: dict[str, str]) -> None:
shutil.copytree(src, dst)
(dst / ".edition-tip").write_text(tip + "\n", encoding="utf-8")
# reuse src build meta if present
for name in (".edition-built-at", ".edition-build-seconds"):
for name in (
".edition-built-at",
".edition-build-seconds",
".edition-freeze-ok",
".edition-freeze-error",
):
s = src / name
if s.is_file():
shutil.copy2(s, dst / name)
else:
p = dst / name
if p.exists():
p.unlink()
inject_bar(dst)
@ -570,6 +621,7 @@ def build_payload(repo: Path, tips: dict[str, str], rebuild: bool) -> dict:
slug = edition_slug(name)
if not (ed_root / slug / "index.html").is_file():
ensure_placeholder(ed_root, slug, tip)
write_freeze_status(ed_root, slug, False, "placeholder")
rebuild_status[name] = {"status": "placeholder", "seconds": 0.0}
if not (ed_root / "flask" / "index.html").is_file():
sync_flask_alias(ed_root, tips)
@ -601,6 +653,15 @@ def build_payload(repo: Path, tips: dict[str, str], rebuild: bool) -> dict:
entry["built_at"] = meta["built_at"]
if "build_seconds" in meta:
entry["build_seconds"] = meta["build_seconds"]
freeze_ok, freeze_err = freeze_ok_for(ed_root, slug, tips[name])
st = str(rb.get("status") or "")
if st == "built":
freeze_ok, freeze_err = True, ""
elif st.startswith("error") or st == "placeholder":
freeze_ok, freeze_err = False, st
entry["freeze_ok"] = freeze_ok
if freeze_err:
entry["freeze_error"] = freeze_err
# sticky bar: +grün/−rot vs parent tip; gray loc when no parent in tree
parent = row["parent"]
if parent and parent in tips: