From 2b9f6c6b8305c11a756cbefa456237d7fed8a23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Tue, 29 Sep 2026 23:27:19 +0200 Subject: [PATCH] ops: dossiers-ngi hourly branch-status builder + systemd timer --- .../user/dossiers-ngi-branch-status.service | 19 +++++ .../user/dossiers-ngi-branch-status.timer | 13 +++ scripts/dossiers-ngi/README.md | 42 ++++++++++ scripts/dossiers-ngi/branch-status-hourly.py | 84 +++++++++++++++++++ .../branch-status.json | 37 ++++++++ 5 files changed, 195 insertions(+) create mode 100644 configs/systemd/user/dossiers-ngi-branch-status.service create mode 100644 configs/systemd/user/dossiers-ngi-branch-status.timer create mode 100644 scripts/dossiers-ngi/README.md create mode 100755 scripts/dossiers-ngi/branch-status-hourly.py create mode 100644 sites/dossiers.ngi.hacktivism.ch/branch-status.json diff --git a/configs/systemd/user/dossiers-ngi-branch-status.service b/configs/systemd/user/dossiers-ngi-branch-status.service new file mode 100644 index 0000000..a6ad491 --- /dev/null +++ b/configs/systemd/user/dossiers-ngi-branch-status.service @@ -0,0 +1,19 @@ +[Unit] +Description=Write dossiers.ngi branch-status.json from tdpd tips +Documentation=file:%h/src/koopa/koopa-admin-log/scripts/dossiers-ngi/README.md +After=default.target + +[Service] +Type=oneshot +Nice=10 +RemainAfterExit=no +Environment=TZ=Europe/Zurich +Environment=ADMIN_LOG=%h/src/koopa/koopa-admin-log +# Override on koopa if the tdpd checkout path differs +Environment=TDPD_ROOT=%h/git/src/ngi/tech-dossier-project-data +EnvironmentFile=-%h/.config/dossiers-ngi/branch-status.env +ExecStart=/usr/bin/python3 %h/src/koopa/koopa-admin-log/scripts/dossiers-ngi/branch-status-hourly.py +SuccessExitStatus=0 1 + +[Install] +WantedBy=default.target diff --git a/configs/systemd/user/dossiers-ngi-branch-status.timer b/configs/systemd/user/dossiers-ngi-branch-status.timer new file mode 100644 index 0000000..e6c701b --- /dev/null +++ b/configs/systemd/user/dossiers-ngi-branch-status.timer @@ -0,0 +1,13 @@ +[Unit] +Description=Timer · dossiers.ngi branch-status JSON (hourly) +Documentation=file:%h/src/koopa/koopa-admin-log/scripts/dossiers-ngi/README.md + +[Timer] +OnBootSec=3min +OnUnitActiveSec=1h +AccuracySec=2min +Persistent=true +Unit=dossiers-ngi-branch-status.service + +[Install] +WantedBy=timers.target diff --git a/scripts/dossiers-ngi/README.md b/scripts/dossiers-ngi/README.md new file mode 100644 index 0000000..11da294 --- /dev/null +++ b/scripts/dossiers-ngi/README.md @@ -0,0 +1,42 @@ +# dossiers-ngi — cascade landing + hourly branch-status + +SoT for the blank cascade landing on **dossiers.ngi.hacktivism.ch** +(path roots only; no dossiers.2 / dossiers.3 ownership here). + +| Path | Role | +|------|------| +| `sites/dossiers.ngi.hacktivism.ch/` | Landing HTML (EN, ASCII ladder) | +| `scripts/dossiers-ngi/branch-status-hourly.py` | Read tips from tdpd checkout → `branch-status.json` | +| `configs/systemd/user/dossiers-ngi-branch-status.{timer,service}` | Hourly oneshot on koopa | + +## Env + +| Var | Default | Meaning | +|-----|---------|---------| +| `TDPD_ROOT` | `$HOME/git/src/ngi/tech-dossier-project-data` | tech-dossier-project-data checkout for tip SHAs | +| `ADMIN_LOG` | (script derives from `__file__`) | koopa-admin-log root | + +On koopa the clone often lives at `%h/src/koopa/koopa-admin-log`; set +`TDPD_ROOT` in the service `Environment=` if the tdpd path differs. + +## Manual run + +```bash +python3 scripts/dossiers-ngi/branch-status-hourly.py +# → sites/dossiers.ngi.hacktivism.ch/branch-status.json +``` + +## Deploy landing (Freigabe) + +```text +hernani@koopa:/var/www/monitoring-sites/dossiers.ngi.hacktivism.ch/ +``` + +rsync from `sites/dossiers.ngi.hacktivism.ch/` — Freigabe this session required. +Hourly JSON can sit next to the HTML once the timer is installed on koopa +(Freigabe for host mutation). + +## Ownership + +Hourly builder belongs in **kal** (this tree). Do **not** put +`scripts/branch-status-hourly.py` back into tdpd. diff --git a/scripts/dossiers-ngi/branch-status-hourly.py b/scripts/dossiers-ngi/branch-status-hourly.py new file mode 100755 index 0000000..feae12a --- /dev/null +++ b/scripts/dossiers-ngi/branch-status-hourly.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Emit branch-status.json for the dossiers.ngi path-root ladder. + +Reads tip SHAs from the tech-dossier-project-data git checkout +(TDPD_ROOT / default). Writes under the kal landing SoT: + + sites/dossiers.ngi.hacktivism.ch/branch-status.json + +Path roots only under https://dossiers.ngi.hacktivism.ch/ +(/main/, /feat-sm-content/, /feat-website-features/, + /feat-website-design/, /feat-website-views/). +Single public host only. Hourly builder lives in kal, not tdpd. +""" +from __future__ import annotations + +import json +import os +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +ADMIN_LOG = Path(__file__).resolve().parents[2] +OUT = ADMIN_LOG / "sites" / "dossiers.ngi.hacktivism.ch" / "branch-status.json" +HOST = "https://dossiers.ngi.hacktivism.ch" + +# Default local clone; override with TDPD_ROOT (koopa may use a different path). +DEFAULT_TDPD = Path.home() / "git" / "src" / "ngi" / "tech-dossier-project-data" + +LADDER = [ + ("main", "", f"{HOST}/main/"), + ("feat/sm-content", "main", f"{HOST}/feat-sm-content/"), + ("feat/website-features", "feat/sm-content", f"{HOST}/feat-website-features/"), + ("feat/website-design", "feat/website-features", f"{HOST}/feat-website-design/"), + ("feat/website-views", "feat/website-design", f"{HOST}/feat-website-views/"), +] + + +def tdpd_root() -> Path: + raw = os.environ.get("TDPD_ROOT", "").strip() + return Path(raw).expanduser() if raw else DEFAULT_TDPD + + +def tip_of(repo: Path, branch: str) -> str: + try: + out = subprocess.check_output( + ["git", "-C", str(repo), "rev-parse", "--short", branch], + stderr=subprocess.DEVNULL, + text=True, + ).strip() + return out + except (subprocess.CalledProcessError, FileNotFoundError, OSError): + return "" + + +def main() -> int: + repo = tdpd_root() + if not (repo / ".git").exists() and not repo.is_dir(): + print(f"TDPD_ROOT missing or not a dir: {repo}", file=sys.stderr) + return 1 + branches = [] + for name, parent, url in LADDER: + branches.append( + { + "branch": name, + "tip": tip_of(repo, name), + "parent": parent, + "url": url, + } + ) + payload = { + "generated": datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds"), + "host": HOST, + "tdpd_root": str(repo), + "branches": branches, + } + OUT.parent.mkdir(parents=True, exist_ok=True) + OUT.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + print(f"wrote {OUT}", file=sys.stderr) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/sites/dossiers.ngi.hacktivism.ch/branch-status.json b/sites/dossiers.ngi.hacktivism.ch/branch-status.json new file mode 100644 index 0000000..74c3df4 --- /dev/null +++ b/sites/dossiers.ngi.hacktivism.ch/branch-status.json @@ -0,0 +1,37 @@ +{ + "generated": "2026-09-30T02:30:50+02:00", + "host": "https://dossiers.ngi.hacktivism.ch", + "tdpd_root": "/Users/newkamek/git/src/ngi/tech-dossier-project-data", + "branches": [ + { + "branch": "main", + "tip": "d2fbd79", + "parent": "", + "url": "https://dossiers.ngi.hacktivism.ch/main/" + }, + { + "branch": "feat/sm-content", + "tip": "089ef1f", + "parent": "main", + "url": "https://dossiers.ngi.hacktivism.ch/feat-sm-content/" + }, + { + "branch": "feat/website-features", + "tip": "53a3176", + "parent": "feat/sm-content", + "url": "https://dossiers.ngi.hacktivism.ch/feat-website-features/" + }, + { + "branch": "feat/website-design", + "tip": "2e26c80", + "parent": "feat/website-features", + "url": "https://dossiers.ngi.hacktivism.ch/feat-website-design/" + }, + { + "branch": "feat/website-views", + "tip": "bbafc38", + "parent": "feat/website-design", + "url": "https://dossiers.ngi.hacktivism.ch/feat-website-views/" + } + ] +}