docs(web): document the card/executions/template-delete changes
CLAUDE.md's web/app.py summary, WEB-UI.md's dashboard-layout and Executions sections, and a new design-history spec covering the tile removal, the compacted Host card, the dropped level-word strip, and the four cards' shared <dl> grammar.
This commit is contained in:
+88
-15
@@ -1,7 +1,7 @@
|
||||
# Web UI — Management Dashboard
|
||||
|
||||
Server-rendered dashboard (`ci web serve`) for the local CI/CD system: recent
|
||||
CI executions with their live VMs (§8), cargo cache, disk, and act_runner
|
||||
CI executions with their live VMs (§9), cargo cache, disk, and act_runner
|
||||
status, plus confirm-gated destructive actions (VM remove/cleanup, cache
|
||||
purge, template backup) with an audit trail. In-process only — it imports
|
||||
`operations/` and `backends.load_backend` directly and never shells out to
|
||||
@@ -106,7 +106,7 @@ application **before** enabling `[web.oidc]`:
|
||||
5. Add the Gitea username(s) allowed to log in to `[web].allowed_users`.
|
||||
**The allowlist is fail-closed**: an empty list denies everyone, it
|
||||
never means "allow all".
|
||||
6. For the Executions card (§8), add an API read scope to
|
||||
6. For the Executions card (§9), add an API read scope to
|
||||
`[web.oidc].scopes` — `read:repository`, plus `read:admin` if the account
|
||||
is a Gitea admin and you want runs from every repo on the instance. New
|
||||
scopes take effect after one fresh login (Gitea shows the consent screen
|
||||
@@ -152,10 +152,15 @@ reinstalls the `[web]` extra and restarts the service in one step (steps
|
||||
address while `[web.oidc]` is absent — an unauthenticated dashboard that
|
||||
can destroy VMs and purge caches must never be reachable from the LAN.
|
||||
Configure `[web.oidc]` first, or bind `127.0.0.1`.
|
||||
- Every mutating route (VM remove/cleanup, cache purge, template backup) is
|
||||
behind a synchronizer-token CSRF check and a two-step confirm gate (a
|
||||
preview/dry-run, then an explicit "Confirm" that actually executes) —
|
||||
there is no one-click destroy.
|
||||
- Every mutating route (VM remove/cleanup, cache purge, template backup,
|
||||
template delete, backup delete) is behind a synchronizer-token CSRF check
|
||||
and a two-step confirm gate (a preview/dry-run, then an explicit "Confirm"
|
||||
that actually executes) — there is no one-click destroy.
|
||||
- **Template delete has no backup-required safety rail.** It removes the
|
||||
entire template directory — the golden image VM clones are made from, not
|
||||
a single backup archive — behind the same confirm gate as everything else,
|
||||
but nothing stops you from deleting a template with zero backups. Use the
|
||||
adjacent Backup button first if you want a way back.
|
||||
- Every mutating action appends exactly one entry to the audit log
|
||||
(`[web].audit_log`, default `/var/lib/ci/logs/web-audit.jsonl`), viewable
|
||||
at `/audit` in the dashboard.
|
||||
@@ -280,13 +285,73 @@ on the live host with `sudo visudo -c` before relying on the panel.
|
||||
|
||||
---
|
||||
|
||||
## 8. Executions card (recent Gitea Actions runs)
|
||||
## 8. Dashboard layout — one card per subject, no tile row
|
||||
|
||||
The dashboard opens with four compact info cards, each the **only** place its
|
||||
subject is reported:
|
||||
|
||||
| Card | Route | Refresh | Contents |
|
||||
|---|---|---|---|
|
||||
| **Host** | `GET /host` | 5s | `System` (hostname · kernel), `CPU` (model · cores), CPU / RAM / disk with gauges; uptime in the header bar, load average under the CPU gauge |
|
||||
| **VMware modules** | `GET /vmware` | 15s | `vmmon`, `vmnet`, `vmware-host.service` |
|
||||
| **Runners** | `GET /runners` | 5s | each `act-runner*` unit with its sub-state |
|
||||
| **Cargo cache** | `GET /cache` | 5s | root, total size, entries, oldest entry, retention |
|
||||
|
||||
Then the wide tables: Executions (§9), Templates, Artifacts, Services,
|
||||
Maintenance.
|
||||
|
||||
All four info cards render the same `<dl>` grammar at the page's plain body
|
||||
size — one `<dt>`/`<dd>` pair per fact. VMware and Runners used to be a
|
||||
bordered `<table>` at a smaller font (`0.9rem` body text, uppercase `0.75rem`
|
||||
column headers), which read as a visibly different style sitting next to
|
||||
Host and Cargo cache's plain label/value lists. They carry the same two data
|
||||
points as before per row (a status badge, a sub-state) — just as `<dt>{{
|
||||
unit }}</dt><dd>{{ badge }} {{ sub_state }}</dd>` instead of a table row —
|
||||
so no reading was dropped, and no card in this row uses `<table>` any more.
|
||||
|
||||
There is **no status-header tile row**. It used to sit above these cards
|
||||
showing VMs / Runners / CPU / RAM / Disk / Cache, which meant runner state was
|
||||
displayed in three places at once (tile, Runners card, Services card) and the
|
||||
core count, RAM total, cache size and entry count in two. The readouts that
|
||||
only the tiles had — CPU%, RAM used, disk used/free — moved into the Host card,
|
||||
where the rest of the host data already was. The VM count went away with the
|
||||
tiles; running VMs are listed in the Executions card, and the orphan sweep is
|
||||
in Maintenance.
|
||||
|
||||
The Host card carries the most data of the four, so it is packed to match
|
||||
their height: the near-static facts pair up (`System`, `CPU`), uptime rides the
|
||||
header bar (spelled out — it has no `<dt>` to label it), and the load average
|
||||
is the CPU gauge's sub-line. Five `<dt>` rows total. The CPU model is trimmed
|
||||
by the `cpu_short` filter (`Intel(R) Core(TM) i9-13900K CPU @ 3.00GHz` →
|
||||
`Intel Core i9-13900K`); the untrimmed name is the `title` of that row.
|
||||
|
||||
**Health does not depend on color.** Each gauge states its value as text
|
||||
directly above the bar (`42%`, `20.0 / 62 GB`, `40% · 60 GB free`), and the
|
||||
level word — `ok` / `elevated` / `critical` / `unknown` — is part of the
|
||||
gauge's `aria-label`, so a screen reader announces it. There is no visible
|
||||
level strip: the words repeated what the numbers already said on every row.
|
||||
Thresholds behind the fill colour and the announced word: CPU and RAM `> 75%`
|
||||
amber, `> 90%` red; disk `> 70%` amber, `> 85%` red; a source that could not
|
||||
be read is a neutral `unknown`, never a false green — and when the disk probe
|
||||
fails outright, `unknown` is printed as the row's value, since there is no
|
||||
gauge left to carry it.
|
||||
|
||||
---
|
||||
|
||||
## 9. Executions card (recent Gitea Actions runs)
|
||||
|
||||
The dashboard's top wide table is **Executions**: the most recent workflow
|
||||
runs, the live ones included, each with its status, workflow, branch/commit,
|
||||
trigger, duration, and the build VM(s) that run spawned (with their `Remove`
|
||||
button). It replaced the old read-only "Live VMs" table — a VM alone could
|
||||
never answer "did the last build pass?".
|
||||
runs, the live ones included, with the build VM(s) that run spawned (with
|
||||
their `Remove` button). It replaced the old read-only "Live VMs" table — a VM
|
||||
alone could never answer "did the last build pass?".
|
||||
|
||||
Every fact is its own column — Run, Repo, Workflow, Branch, Commit, Trigger,
|
||||
Actor, Status, Duration, Started, VM, IP / Uptime, Actions. No cell stacks two
|
||||
facts on separate lines with `<br>`; a run's repo, the commit hash, who
|
||||
triggered it, and when it started each get their own column instead. The
|
||||
table is wide as a result — 13 columns — and scrolls horizontally inside the
|
||||
card's own `.table-wrap` rather than the page, same as the other wide tables
|
||||
(Templates, Services).
|
||||
|
||||
Runs come from Gitea's Actions API: `/api/v1/admin/actions/runs` (every repo
|
||||
on the instance, admin token) with `/api/v1/user/actions/runs` as the
|
||||
@@ -294,6 +359,15 @@ non-admin fallback. Results are cached for 10s, so the card's 5s refresh costs
|
||||
Gitea at most one call per 10s window; the VM columns are re-read on every
|
||||
refresh.
|
||||
|
||||
The Status pill carries a check/✕ icon for the two outcomes that are a real
|
||||
verdict (`success`/`failure`, via `run_badge_icon`); `queued`/`running`/
|
||||
`cancelled`/`skipped` stay plain text since they are not a pass/fail on the
|
||||
run. Every pill in the column — outcome pills and the VM-only `orphan`/
|
||||
`running` pills on unmatched-VM rows — is forced to the same width
|
||||
(`#executions-card td:nth-child(8)` — Status is the 8th column, see above —
|
||||
sized to fit `cancelled`, the longest word) with left-aligned content, so the
|
||||
column reads as a clean list instead of ragged pill widths.
|
||||
|
||||
Rows are ordered **newest first** (a queued run, which has no start time yet,
|
||||
sorts above everything; ties break on Gitea's run id). The row-count dropdown at
|
||||
the right end of the card's header bar picks how many to list — 10 / 25 / 50 /
|
||||
@@ -340,13 +414,12 @@ passes. Do not remove that header.
|
||||
**Orphaned VMs are not handled in this card.** A powered-on VM whose run is no
|
||||
longer in the window (or that no run ever claimed) keeps a row of its own
|
||||
flagged `no run`, with its `Remove` button — but the orphan *sweep* lives in
|
||||
the **Maintenance** card ("Cleanup orphaned VMs") and the orphan *count* in
|
||||
the status-header VMs tile. Those two are the places to look at orphans; the
|
||||
Executions card only avoids hiding them.
|
||||
the **Maintenance** card ("Cleanup orphaned VMs"). That is the place to act on
|
||||
orphans; the Executions card only avoids hiding them.
|
||||
|
||||
---
|
||||
|
||||
## 9. Troubleshooting
|
||||
## 10. Troubleshooting
|
||||
|
||||
**Login loop (bounces back to `/login` after authenticating with Gitea)**
|
||||
: The registered Redirect URI in Gitea does not exactly match
|
||||
|
||||
Reference in New Issue
Block a user