collin/anvil · 84b3fd31
docs: check off the TODO list; refresh session notes
Collin Richards · 2026-06-10 09:17 UTC · 84b3fd313100976e39f487119aa74182315ba92d · parent c4d49193 · browse files
modifiedTODO.md+84 −23
| ⋯ 5 unchanged lines | |||
| 6 | 6 | and tree pages, "browse files" link on the commit page, percent-encoded | |
| 7 | 7 | ref names so branches with `/` work, and an unborn-HEAD fallback so a | |
| 8 | 8 | repo whose HEAD names a missing branch no longer renders as empty. | |
| 9 | - | - [ ] implement the CI artifact system _(designed — see `docs/ci-artifacts.md`; | |
| 10 | - | implementation order is at the bottom of that doc)_ | |
| 9 | + | - [x] implement the CI artifact system _(done per `docs/ci-artifacts.md`: | |
| 10 | + | `artifacts:` in ci.yml with in-container meta extractors, broker | |
| 11 | + | collection via `download_from_container`, run-page list, downloads + | |
| 12 | + | `/{owner}/{repo}/artifacts/{rev}/{name}` alias + `browse: true` static | |
| 13 | + | sites, quota GC with branch-tip pinning, and a schema shim so the table | |
| 14 | + | appears on existing DBs. Verified end-to-end against real Docker incl. | |
| 15 | + | the GC path. Repo-deletion cleanup deferred: repo deletion doesn't exist.)_ | |
| 11 | 16 | - every push triggers CI on the tip commit (already true); a run should be | |
| 12 | 17 | able to produce artifacts | |
| 13 | 18 | - artifacts are stored per-commit and served from anvil (download from the | |
| ⋯ 7 unchanged lines | |||
| 21 | 26 | - use rustdoc as an example when testing the feature: it generates a big | |
| 22 | 27 | HTML subtree, so support rendering/serving a whole HTML artifact subtree | |
| 23 | 28 | the way the pages feature does (rustdoc output as a served site) | |
| 24 | - | - [ ] repo mirroring to/from GitHub | |
| 25 | - | - push mirror: pushes to an anvil repo get forwarded to a configured | |
| 26 | - | GitHub remote | |
| 27 | - | - pull mirror (maybe): a repo that virtually mirrors a GitHub repo and | |
| 28 | - | just displays it here — periodically fetched, read-only on the anvil | |
| 29 | - | side | |
| 30 | - | - [ ] per-repository issue tracker | |
| 29 | + | - [x] repo mirroring to GitHub (push mirror) _(per-repo "Mirror push URL" in | |
| 30 | + | settings (`repositories.mirror_url`, column shim for existing DBs); | |
| 31 | + | after every successful push over HTTP or SSH a background | |
| 32 | + | `git push --mirror` fires — shells out to git (gix can't push yet; | |
| 33 | + | runtime image now installs git). Credentials ride in the URL | |
| 34 | + | (`https://x-access-token:<token>@github.com/...`), stored as-is and | |
| 35 | + | redacted from logs. Verified e2e against a local bare "github".)_ | |
| 36 | + | - pull mirror (maybe, NOT done): a repo that virtually mirrors a GitHub | |
| 37 | + | repo and just displays it here — periodically fetched, read-only on | |
| 38 | + | the anvil side | |
| 39 | + | - [x] per-repository issue tracker _(`Issue`/`IssueComment` models with | |
| 40 | + | schema shims; per-repo numbering (#1, #2, …); list page with | |
| 41 | + | open/closed tabs, new-issue form, detail page with markdown bodies and | |
| 42 | + | comments, close/reopen (issue author or repo writer). Any logged-in | |
| 43 | + | reader can open issues and comment; visibility follows the repo. | |
| 44 | + | "Issues" link in the repo nav. CSRF on all forms. Verified e2e through | |
| 45 | + | the login + form flow. No labels/assignees/editing yet — deliberately | |
| 46 | + | minimal.)_ | |
| 47 | + | - [x] render a root `README.md` below the file tree on the repo page _(any | |
| 48 | + | case of `readme.md` at the root; reuses `render_markdown`, links to the | |
| 49 | + | blob view from the box header)_ | |
| 50 | + | - [x] push-to-create: `git push` to a repo that doesn't exist yet creates it | |
| 51 | + | _(it did NOT already work — both transports 404'd. Policy in | |
| 52 | + | `repos::create_on_push`: pusher must own the namespace or be admin; | |
| 53 | + | created repos are private. Over HTTP a missing repo now answers the | |
| 54 | + | receive-pack advertisement with a Basic challenge so git prompts. | |
| 55 | + | Verified e2e over both HTTP and SSH, incl. the cross-namespace denial.)_ | |
| 56 | + | - [x] make SSH the default clone selection in the clone pill buttons, and put | |
| 57 | + | it first (before HTTP) _(only when `[ssh] enabled`; HTTP stays the lone | |
| 58 | + | pill otherwise)_ | |
| 31 | 59 | - [x] rename the `anvil` crate to `anvil_cli` _(named it `anvil-cli` to match | |
| 32 | 60 | the workspace's hyphenated crate names; the binary is still `anvild`, so | |
| 33 | 61 | deploy scripts and docs needed no changes)_ | |
| ⋯ 51 unchanged lines | |||
| 85 | 113 | ||
| 86 | 114 | # Session notes / resume point | |
| 87 | 115 | ||
| 88 | - | _Last updated: 2026-06-10 (second session). Working state is clean: `cargo | |
| 89 | - | build`, `cargo clippy --workspace`, `cargo fmt --all`, and `cargo test | |
| 90 | - | --workspace` all pass. Everything below is UNCOMMITTED (repo convention: | |
| 91 | - | commit only when asked). **All top-of-file TODO items are done.**_ | |
| 116 | + | _Last updated: 2026-06-10 (third session). Working state is clean: build, | |
| 117 | + | clippy, fmt, sort-derives, and `cargo test --workspace` all pass (the | |
| 118 | + | pre-commit hook runs all of these). Third-session work below is UNCOMMITTED | |
| 119 | + | (repo convention: commit only when asked). **All top-of-file TODO items are | |
| 120 | + | done** except the pull-mirror "maybe"._ | |
| 92 | 121 | ||
| 93 | - | ## Done this session (2026-06-10, second session) | |
| 122 | + | ## Done this session (2026-06-10, third session) | |
| 94 | 123 | ||
| 124 | + | All six remaining TODO items — see the checked-off entries at the top of this | |
| 125 | + | file for the details. Headlines: | |
| 126 | + | ||
| 127 | + | - **CI artifacts** (`docs/ci-artifacts.md` is the canonical reference): | |
| 128 | + | `artifacts:` in `.anvil/ci.yml`; broker pulls them out of the stopped | |
| 129 | + | container via `download_from_container` (inverse of the checkout upload — | |
| 130 | + | still no mounts); meta extractors run *inside* the sandbox, one file per | |
| 131 | + | value under `/tmp/anvil-meta`; `browse: true` directories are served like | |
| 132 | + | pages (rustdoc-ready); `/{owner}/{repo}/artifacts/{rev}/{name}` is the | |
| 133 | + | latest-on-branch alias; per-repo quota GC pins branch tips. New table | |
| 134 | + | `ci_artifacts` + `[ci] artifact_*_mb` caps. | |
| 135 | + | - **Schema shims for existing DBs** (`anvil-core/src/db.rs`): Toasty still | |
| 136 | + | only pushes schema on a fresh file, so new tables/columns ship as | |
| 137 | + | idempotent DDL applied on connect (`SCHEMA_SHIMS`/`COLUMN_SHIMS`), with | |
| 138 | + | tests asserting fresh and migrated databases converge. New deps: rusqlite | |
| 139 | + | (pinned to toasty's), flate2. | |
| 140 | + | - **Issues** (`anvil-core/src/issues.rs`, `anvil-web/src/issues.rs`): | |
| 141 | + | minimal GitHub-shaped tracker; tables `issues` + `issue_comments`. | |
| 142 | + | - **Push mirroring** (`anvil-git/src/mirror.rs`): `repositories.mirror_url` | |
| 143 | + | → background `git push --mirror` after each push; Dockerfile now installs | |
| 144 | + | git (the one thing gix can't do yet is push). | |
| 145 | + | - **Push-to-create** (`repos::create_on_push` + both transports), **README | |
| 146 | + | on repo page**, **SSH-first clone pills**, **rev-switcher/browse-at-rev UI** | |
| 147 | + | (committed earlier this session, along with the `anvil-cli` rename). | |
| 148 | + | ||
| 149 | + | ## Done earlier (2026-06-10, second session) | |
| 150 | + | ||
| 95 | 151 | - **UI quick wins** (`crates/anvil-web/src/ui.rs`) — latest-commit bar on the | |
| 96 | 152 | repo page (sha + subject + author/time, attached above the file box, links | |
| 97 | 153 | to the commit); profile page no longer shows the email; repo header reads | |
| ⋯ 59 unchanged lines | |||
| 157 | 213 | - **CSRF header consumption:** `hx-headers` sends the token as a `csrf` header, | |
| 158 | 214 | but `verify_csrf` only reads the form field. When we add a tokenless htmx | |
| 159 | 215 | action (raw `hx-post`/`hx-delete`, no `<form>`), also read the `csrf` header. | |
| 160 | - | - **Toasty migrations:** schema only pushed on a fresh DB (`db::connect`); new | |
| 161 | - | columns won't apply to an existing DB until migrations land. (The | |
| 162 | - | `data_dir/csrf_secret` file is created automatically — no DB change.) | |
| 163 | - | - **Suggested commits when ready:** (1) CI UI, (2) CD webhook + deploy wiring, | |
| 164 | - | (3) ci.rs ORM cleanup + test, (4) CSRF + cookies, (5) UI quick wins | |
| 165 | - | (latest-commit bar, profile email, breadcrumb), (6) CI sandbox + threat-model | |
| 166 | - | doc, (7) pages hosting. Trailer: `Co-Authored-By: Claude ...`. | |
| 216 | + | - **Toasty migrations:** still no real migration system; the shim approach in | |
| 217 | + | `db::connect` (`SCHEMA_SHIMS` for tables, `COLUMN_SHIMS` for columns, both | |
| 218 | + | test-verified against a fresh `push_schema`) covers what we've needed so | |
| 219 | + | far. New columns must be declared last in the model. | |
| 220 | + | - **Mirror URL secrecy:** `repositories.mirror_url` may embed a token and is | |
| 221 | + | stored plaintext in SQLite (logged redacted). Fine single-tenant; revisit | |
| 222 | + | if the DB ever leaves the box. | |
| 167 | 223 | - **Pages caveats (single-tenant-acceptable):** served from the forge origin — | |
| 168 | 224 | move to a separate origin before untrusted users (threat model §2); whole | |
| 169 | - | blobs load into memory per request (fine at our scale). | |
| 225 | + | blobs load into memory per request (fine at our scale). The same applies to | |
| 226 | + | `browse: true` CI artifacts. | |
| 227 | + | - **Issue tracker minimalism:** no labels, assignees, milestones, or | |
| 228 | + | editing/deleting of posts. Per-repo numbering assumes a single server | |
| 229 | + | process (matches deployment; noted in `models.rs`). | |
| 170 | 230 | ||
| 171 | 231 | ## Remaining roadmap (plan milestones beyond a/b/c) | |
| 172 | 232 | ||
| 173 | - | 8. Issues · 9. Pull requests (gix merge) · 10. Webhooks (mind the SSRF item). | |
| 233 | + | 9. Pull requests (gix merge) · 10. Webhooks (mind the SSRF item). | |
| 234 | + | (8. Issues shipped 2026-06-10.) | |