ops: dossiers-ngi hourly branch-status builder + systemd timer

This commit is contained in:
Hernâni Marques 2026-09-29 23:27:19 +02:00
parent 83998499da
commit 2b9f6c6b83
No known key found for this signature in database
5 changed files with 195 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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())

View file

@ -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/"
}
]
}