# dossiers-ngi — dynamic edition bar + 10‑min rebuild SoT for the sticky edition bar on **dossiers.ngi.hacktivism.ch**. Live freezes sit under `editions//` on koopa; cookie `tdpd_edition` selects the build. The bar renders a **semi-graphic parent→children tree** from `/branch-status.json` (all `origin/*` tips, not a hard-coded ladder). | Path | Role | |------|------| | `sites/…/_cascade/` | Sticky bar shell + JS tree renderer | | `editions/` on koopa www | Full freezes switched by the bar | | `sites/…/cascade/` | Legacy tip stubs (optional) | | `scripts/dossiers-ngi/branch-status-hourly.py` | Fetch remotes → tree JSON → on-demand rebuild | | `scripts/dossiers-ngi/smoke-branch-tree.sh` | Temp-branch smoke (creates/deletes local origin only) | | `configs/systemd/user/dossiers-ngi-branch-status.{timer,service}` | Every **10 min** oneshot on koopa (`flock` against overlap) | ## Env | Var | Default | Meaning | |-----|---------|---------| | `TDPD_ROOT` | `$HOME/git/src/ngi/tech-dossier-project-data` | tdpd git checkout (may be git-dir only) | | `TDPD_VENV` | `(TDPD_ROOT)/.venv` | Frozen-Flask venv; symlinked into worktrees | | `OUT` | kal `sites/…/branch-status.json` | write path (live www on koopa) | | `EDITIONS_ROOT` | (none) | `…/editions` — required for `--rebuild` | | `REBUILD` / `--rebuild` | off | rebuild when `.edition-tip` ≠ tip | | `FORCE_REBUILD` / `--force` | off | rebuild every branch (timing) | | `FETCH` / `--no-fetch` | fetch on | `git fetch --prune origin` | | `FREEZE_CMD` | (make/venv freeze) | stub for smoke tests; `{root}` `{branch}` `{slug}` | | `DEFAULT_BRANCH` | `feat/website-views` | behind cookie `flask` | | `HOST` | `https://dossiers.ngi.hacktivism.ch` | public host in JSON | On koopa: `%h/src/koopa/koopa-admin-log` + `~/.config/dossiers-ngi/branch-status.env` for path overrides. NGI clone token lives in **koopa-admin-secrets-enc** (not this tree). ## Manual run ```bash # JSON only python3 scripts/dossiers-ngi/branch-status-hourly.py # rebuild stale freezes + timing block in JSON EDITIONS_ROOT=/var/www/monitoring-sites/dossiers.ngi.hacktivism.ch/editions \ python3 scripts/dossiers-ngi/branch-status-hourly.py --rebuild # force all branches (wall clock → rebuild_timing) … --rebuild --force ``` `rebuild_timing` fields: `wall_seconds`, `built`, `skipped`, `errors`, `built_seconds_sum`, `built_seconds_max`. Per-branch `rebuild[name].seconds`. Per branch in JSON / sticky bar: | Field | Meaning | |-------|---------| | `tip` | short SHA | | `tip_time` | committer time of tip (`%cI`) | | `built_at` | when this edition freeze was written | | `build_seconds` | wall time of that freeze | Rebuild runs **only when** `.edition-tip` ≠ current tip (timer never forces). Each run also: `git fetch --prune`, `worktree prune`, drop stale `editions//` (gone remotes), purge leftover `tdpd-*` tmp dirs. ## Smoke ```bash bash scripts/dossiers-ngi/smoke-branch-tree.sh # → SMOKE_OK (temp origin + smoke/temp-sibling deleted) ``` ## Deploy landing (Freigabe) Do **not** `--delete` the whole www root. Sync: ```bash SITE="$HOME/git/admin-logs/local/koopa-admin-log/sites/dossiers.ngi.hacktivism.ch" WWW=hernani@koopa:/var/www/monitoring-sites/dossiers.ngi.hacktivism.ch rsync -avz --exclude .DS_Store "$SITE/_cascade/" "$WWW/_cascade/" rsync -avz --exclude .DS_Store "$SITE/branch-status.json" "$WWW/branch-status.json" # editions/ from a --rebuild run ``` ## Sticky cascade bar `bar.js` fetches `/branch-status.json` and draws: ``` main @ d2fbd79 ├── feat/sm-content @ … │ └── … └── project_template @ … ``` Each name links to `/_cascade/use/` (cookie + 302 `/`). Caddy map: ``` ~^[A-Za-z0-9_-]+$ {http.request.cookie.tdpd_edition} ``` so new branches need no Caddy edit — only an `editions//` tree. ```bash bash ~/bin/apply-dossiers-ngi-edition-switch.sh sudo ~/bin/caddy-apply ``` Stanza SoT: `scripts/dossiers-ngi/caddy-dossiers-ngi.snippet`.