claim(3 U2): summary card + badge generated per-run + served live at /runs/<id>/ (real screenshot embedded; traversal-guarded); gate CLAIMED
This commit is contained in:
@ -116,10 +116,58 @@ unreachable-domain capture.
|
||||
The cardinal Phase-3 invariant for U1: the screenshot is a faithful capture of the live app, never a
|
||||
credentials page, and its presence/absence never changes the verdict.
|
||||
|
||||
---
|
||||
|
||||
## Gate: U2 — CLAIMED, awaiting Adversary (Summary card + badge; R3, R6)
|
||||
|
||||
**WHAT.** Each run now renders a **summary card PNG** (recipe+version, level badge, per-stage/per-test
|
||||
✔/✘ table, embedded **real app screenshot**) and an **SVG level badge**, written into the run artifact
|
||||
dir and **served at stable URLs** `https://ci.commoninternet.net/runs/<run_id>/{summary.png,badge.svg,
|
||||
screenshot.png,results.json}`. The card REPORTS results.json verbatim — it computes nothing, so it can
|
||||
never look greener than the tiers (cardinal invariant). U2 acceptance ("card + badge render correctly
|
||||
for a pass run AND a fail run") demonstrated: a real PASS run served live; a deterministic FAIL render
|
||||
shown honest (L0/red/✘/no-screenshot).
|
||||
|
||||
**WHERE (commits / files).**
|
||||
- `afe5e51` `runner/run_recipe_ci.py` — after results.json is written, a separate best-effort block
|
||||
renders `summary.html`→`summary.png` + `badge.svg` via `harness.card` (passes
|
||||
`screenshot_rel=data["screenshot"]` so the real shot embeds iff present). R7-wrapped — any failure
|
||||
is swallowed, never changes `overall`.
|
||||
- `daa7edd`/`7217e0c`/`8179d3f` `runner/harness/card.py` — pure `render_card_html`, `render_badge_svg`/
|
||||
`level_badge_svg` (deterministic string builders), `render_card_png` (best-effort Playwright). Inline
|
||||
SVG sunflower (headless chromium has no colour-emoji font). `tests/unit/test_card.py` (8 tests).
|
||||
- `fa56f6b` `dashboard/dashboard.py` + `nix/modules/dashboard.nix` — `/runs/<id>/<file>` route
|
||||
(allow-list + `run_id` regex + realpath-inside-runs-dir traversal guard); `/var/lib/cc-ci-runs`
|
||||
bind-mounted READ-ONLY into the dashboard swarm service; `CCCI_RUNS_DIR` env.
|
||||
|
||||
**HOW to verify (cold).** (See ADVERSARY-INBOX for the deploy gotcha — do NOT `nixos-rebuild switch`
|
||||
the live host; `#cc-ci` targets the hetzner migration host. U2.3 was rolled via the dashboard module
|
||||
reconcile only. DECISIONS.md Phase-3/U2 has the `diff-closures` evidence.)
|
||||
1. **Unit tests:** `cc-ci-run -m pytest tests/unit/test_card.py -q` → `8 passed`.
|
||||
2. **PASS card served live (real):**
|
||||
`curl -s -o /tmp/c.png -w '%{http_code} %{content_type} %{size_download}\n'
|
||||
https://ci.commoninternet.net/runs/u1-uk-shot/summary.png` → `200 image/png ~69313`. Eyeball
|
||||
`/tmp/c.png`: uptime-kuma, **orange LEVEL 1**, "capped: L2 upgrade N/A", install/test_serving ✔
|
||||
PASS rows, clean-teardown+no-secret-leak flags, and the **real uptime-kuma screenshot embedded**.
|
||||
Also `…/screenshot.png` (200 ~30858), `…/badge.svg` (200 image/svg+xml), `…/results.json` (200).
|
||||
3. **Traversal/whitelist guard:** `…/runs/u1-uk-shot/../../../etc/passwd`, `…/runs/u1-uk-shot/evil.sh`,
|
||||
`…/runs/nonexist/results.json` → **404** with a **9-byte** body (the dashboard's own "not found",
|
||||
NOT Traefik's 19-byte 404 — proves the request reached the app and the guard rejected it).
|
||||
4. **FAIL render is honest (cardinal invariant):** feed the card a fail dict (cmd in ADVERSARY-INBOX
|
||||
§3) → card shows **level 0**, `level_color(0)` (red), the **✘ FAIL** mark on the install row, and
|
||||
the **"no screenshot"** placeholder — never greener than the data.
|
||||
|
||||
**EXPECTED.** (1) `8 passed`. (2) PASS card 200/image-png/~69KB, embeds the real screenshot, level/marks
|
||||
match results.json (`u1-uk-shot`: level 1, install pass). (3) all three guarded paths 404 with a 9B
|
||||
body. (4) fail render: `>0<` (level 0), red colour, ✘ present, "no screenshot" present — no inflation.
|
||||
|
||||
The cardinal U2 invariant: the rendered card/level/badge are a faithful, never-greener projection of
|
||||
results.json + the actual test outcomes, served at a stable URL, generated best-effort so a render
|
||||
failure never blocks the run.
|
||||
|
||||
## In flight (next, post-gate)
|
||||
- U2 — summary card + badge (HTML→PNG via Playwright; SVG level badge; stable URLs). Render path
|
||||
already de-risked headless on cc-ci for pass+fail fixtures (JOURNAL-3 @06:50Z) — next is wiring the
|
||||
card/badge generation into the run + serving them. Held until U1 PASSes (no advance past the gate).
|
||||
- U3 — YunoHost-style PR comment (marker 🌻 + level/status badge + summary card image, linked;
|
||||
updates on re-run; fallback to text). Held until U2 PASSes (no advance past the gate).
|
||||
|
||||
## Blocked
|
||||
(none)
|
||||
|
||||
Reference in New Issue
Block a user