Some checks failed
continuous-integration/drone/push Build is failing
Phase 3 complete. U5 gate PASS @2026-05-31T13:13Z: - R6 per-recipe badge endpoint live (custom-html/uptime-kuma level 4, keycloak unknown fallback) - R8 docs/results-ux.md §1-5 complete, no TODOs - R7 render-kill: exit 0, install pass, results.json intact, no card/screenshot (u5-renderkill3) - R7 broad leak scan: 0 real secret values in any artifact or PR comment All R1–R8 verified <24h; STATUS-3 flipped to ## DONE.
7.3 KiB
7.3 KiB
Phase 3 — Beautiful YunoHost-style results — BACKLOG
Single source of truth: /srv/cc-ci/cc-ci-plan/plan-phase3-results-ux.md.
Milestones U0–U5 (plan §5); each ends with an Adversary gate. DoD items R1–R8 (plan §2).
Build backlog
U0 — Results schema + level (R1)
- U0.1 — Pure
level()function (harness/level.py): L0–L6 gap-caps semantics; 15 unit tests (incl L4-pass + L2-cap); Adversary fuzz-clean 729/729 (REVIEW-3 @df54693). - U0.2 — Per-tier pytest emits JUnit XML (parsed by harness/results.py) → results.json per-stage AND per-test ✔/✘ breakdown.
- U0.3 —
run_recipe_ci.pywritesresults.jsonper run (level, cap_reason, rungs, stages, flags) to the run-scoped artifact dir; assembly wrapped so it NEVER changes the verdict (R7). - U0.4 — Artifact hosting path decided + recorded in DECISIONS (
${CCCI_RUNS_DIR:-/var/lib/cc-ci-runs}/ <run_id>/; dashboard serves/runs/<id>/in U2/U4 via host bind-mount). - GATE U0: PASS (Adversary REVIEW-3 @18d2bd1, 2026-05-31) — R1 cold-verified, no inflation, no VETO.
U1 — App screenshot (R4)
- U1.1 — Harness captures a real Playwright screenshot of the deployed app while it is up (default landing page = secret-safe; recipes opt into a post-login view via a SCREENSHOT meta hook, never shoot a credentials page). Wired into run_recipe_ci.py post-healthy, pre-teardown.
- U1.2 — Screenshot saved to run artifact dir (
screenshot.png); results.jsonscreenshotfield set ONLY when capture succeeds; degrades gracefully (capture() swallows all errors → None → field null → run/verdict unaffected, R7). - GATE U1: PASS (Adversary REVIEW-3 @74a6993, 2026-05-31) — R4 cold-verified (real screenshot of working UI, no secrets, R7-safe wiring, graceful degradation), no VETO.
U2 — Summary card + badge (R3, R6)
- U2.1 — HTML results-card (recipe+version, level badge, per-stage/per-test ✔/✘ table, embedded app screenshot) → PNG via Playwright; wired into run_recipe_ci.py, R7-best-effort.
- U2.2 — Per-run SVG level badge (
badge.svg) generated per run (shields-style, colour by level). - U2.3 — Card + badge + screenshot + results.json served at stable URLs
/runs/<id>/{summary.png,badge.svg,screenshot.png,results.json}(allow-list + traversal-guarded; runs dir bind-mounted RO into the dashboard swarm service). LIVE over HTTPS, verified. - GATE U2: PASS (Adversary REVIEW-3 @324d84d, 2026-05-31) — card+badge render correct for pass & fail, served traversal-guarded, never-greener, leak-clean, R7-safe, no VETO. (R3/R6 stay partial until embedded in PR comment (U3) + dashboard (U4) + per-recipe badge (U5).)
- Adversary polish items to fold in (low-sev, not gates): (a) dashboard
/runs/HEAD→501 (no do_HEAD) → add do_HEAD (also enables a cheap bridge existence-check for U3 fallback); (b) per-recipe latest-level badge endpoint → U5.
U3 — YunoHost-style PR comment (R2)
- U3.1 — Bridge posts a placeholder comment on run start (⏳ + live-logs link).
start_comment_body, reuses the marked comment if present (re-!testmerefreshes to placeholder). - U3.2 — On completion, update the SAME comment to 🌻 + level/status badge + summary card image,
both linking to the run/dashboard. Re-
!testmerefreshes it. Fallback to text on render failure (result_comment_body+artifact_availableHEAD check). Deployed (bridge img 6377f9571f3b). - U3.3 — Fold Drone repo activation into the drone reconcile so a DB reset self-heals:
POST /api/repos/recipe-maintainers/cc-ci(idempotent) BEFORE the timeout PATCH in drone.nix. Found during the U3 live demo — the Hetzner-migration DB reset left the repo inactive (bridgedrone trigger failed 404); I reactivated by hand to run the demo. Not a U3 DoD item (cosmetics/comment shape is); robustness hardening — fold in at U5 or flag to operator. - GATE U3: PASS (Adversary REVIEW-3 @778b577, 2026-05-31) — image-forward comment live on custom-html PR#2 (comment 13792), update-in-place cold-reproduced (run 4→7, never stacked), card == results.json (no inflation), no secrets, deployed bridge == source. R2 satisfied; no VETO.
U4 — Dashboard polish (R5)
- U4.1 — Overview grid like
ci-apps.yunohost.org: per-recipe level badge, latest pass/fail, last-tested version, app screenshot/thumbnail, link to history (/recipe/<name>).render_overview+_card(dashboard.py @e1d837e). - U4.2 — Regenerated on build completion; reads results.json artifacts (
_results_for,_build_row; 30s cache + live render over the RO-bind-mounted runs dir). - GATE U4: PASS (Adversary REVIEW-3 @9ca39dc, 2026-05-31) — grid + history cold-verified never-greener vs results.json; honest uptime-kuma #11 failure row; no secrets; deployed == source; 9 tests; no VETO. R5 satisfied, R3 fully satisfied (card in comment + dashboard).
U5 — Badges + docs + hardening (R6, R7, R8)
- U5.1 — Embeddable per-recipe latest-level badge endpoint
/badge/<recipe>.svg(level-coloured, status fallback;render_level_badge, dashboard.py @91a69b8) + README-embed snippet documented. Built + unit-tested; pending live deploy+verify. - U5.2 —
docs/results-ux.md§1-5 complete: level ladder + tier→rung mapping, results.json schema, card/screenshot generation, PR-comment shape, badge endpoints + README embed snippet (R8). - U5.3 — Hardening: render failure degrades to text (comment
artifact_availableHEAD → text, unit-covered) + cosmetic render-kill proven verdict-unaffected (u5-renderkill3: card + screenshot forced to raise → exit 0, install pass, results.json intact, no card/screenshot) + new defense-in-depth try/except on the screenshot call site (799cceb); broad secret scan over ALL published text artifacts + PR comments → zero real secret values (onlyno_secret_leakflag name/label). - GATE U5: PASS (Adversary REVIEW-3 @15b3057, 2026-05-31T13:13Z) — R6 badge live (3 URLs verified),
R8 docs complete (§1-5, no TODOs), R7 render-kill artifacts confirmed + broad leak scan clean
(0 real secret values in any artifact/comment). All R1–R8 verified. STATUS-3
## DONEflipped.
Adversary findings
(Adversary owns this section — Builder does not edit.)
- A3-1 [adversary] —
/runs/<id>/<file>returned 501 to HEAD requests (low severity, polish). CLOSED @2026-05-31T09:34Z — re-tested live, fixed. The dashboardBaseHTTPhandler implemented onlydo_GET, soHEAD /runs/u1-uk-shot/summary.png→HTTP 501 Unsupported method. The Builder added ado_HEADin9a47aa2, now deployed live. Re-verify (cold, from VM):curl -sSI https://ci.commoninternet.net/runs/u1-uk-shot/summary.png→ HTTP/2 200,content-type: image/png,content-length: 69313, and 0-byte body (curl -X HEAD | wc -c= 0 — correct HEAD semantics, headers only). badge.svg HEAD → 200 image/svg+xml. GET still 200/69313. Guards still hold under HEAD:HEAD …/evil.sh→ 404,HEAD …/runs/nonexist-xyz/results.json→ 404 (whitelist + run-id guard not bypassed by method). Resolved; no regression.