| 1 | # CI |
| 2 | |
| 3 | `ci.d/10-gate` is the only CI this repo has. It runs on every push to the |
| 4 | amadan remote, and it is the same script you can run locally — one |
| 5 | definition rather than two that drift. |
| 6 | |
| 7 | It requires a Rust toolchain with the `wasm32-unknown-unknown` target and |
| 8 | the `rustfmt` component, Go with `CGO_ENABLED=0`, and Node with `npm` on |
| 9 | `PATH`. The gate sources `~/.cargo/env` itself and checks every tool up |
| 10 | front, so a missing dependency fails on the first line with its own name |
| 11 | rather than fifty lines in as `cargo: command not found`. |
| 12 | |
| 13 | The runner clones a fresh worktree per branch, so `fixtures/node_modules` |
| 14 | never carries over — the gate runs `npm ci` itself, against |
| 15 | `fixtures/package-lock.json`, so the installed `yjs` is always exactly |
| 16 | 13.6.27 rather than whatever a bare `npm install` would resolve. |
| 17 | |
| 18 | **Known tradeoff:** that `npm ci` needs the npm registry reachable at run |
| 19 | time. It is the right fix for the fresh-worktree-per-branch problem above, |
| 20 | and the lockfile is committed so the *version* resolved never drifts, but |
| 21 | it does mean the gate is not fully offline-capable the way the Rust and Go |
| 22 | steps are once their caches are warm. If the registry is ever unreachable |
| 23 | from the runner, that is the failure this line produces — not a bug in |
| 24 | the gate itself. |
| 25 | |
| 26 | ## The runner |
| 27 | |
| 28 | Jobs are claimed by an `amadan runner` on the machine that has one. Two |
| 29 | things about it were wrong when this repo was created, and both fail |
| 30 | silently rather than loudly: |
| 31 | |
| 32 | - **The runner must be told to serve this repo.** `amadan runner -repo …` |
| 33 | is an allowlist. A repo nobody listed gets `amadan ci status` exit 3 |
| 34 | forever — "no status recorded", which reads like the gate has not run yet |
| 35 | rather than like nothing will ever run it. |
| 36 | - **The runner's `PATH` is the systemd unit's, not your shell's.** |
| 37 | `~/.cargo/bin` is not on it by default. |
| 38 | |
| 39 | Both were fixed on `DOCS-T1` on 2026-08-30 by editing |
| 40 | `~/.config/systemd/user/amadan-runner.service` and restarting. A restart |
| 41 | abandons any job already running, so check it is idle first. |
| 42 | |
| 43 | The gate fails if `internal/core/testdata/docs_core.wasm` differs from a |
| 44 | fresh build, because `go:embed` will otherwise embed a stale module and the |
| 45 | tests will pass against it. |
| 46 | |
| 47 | It also fails if regenerating the Yjs fixtures changes them. Fixtures are |
| 48 | the wire contract, not an output: a deliberate rule change regenerates them |
| 49 | in the same commit as the change. |
| 50 | |
| 51 | ## The app, not just the core |
| 52 | |
| 53 | `go vet ./...` and `go test ./...` already cover `cmd/docs` and |
| 54 | `internal/docs` — the rastrillo app built on top of `internal/core` — but |
| 55 | neither one proves the app actually links into a runnable binary. The gate |
| 56 | also runs `CGO_ENABLED=0 go build ./...` for that. |
| 57 | |
| 58 | There used to be a further step here: a separate `-tags release` build, |
| 59 | run with `-dev-auth` on its command line, asserting it refused to start — |
| 60 | proving a release binary had no code path that could turn a |
| 61 | development-only stand-in identity on. That identity |
| 62 | (`internal/docs/devauth.go`) had zero readers once rastrillo/auth's |
| 63 | `RequireSession` started guarding every document route unconditionally |
| 64 | (a fix round on Task 1, once real per-person sign-in existed), so it was |
| 65 | deleted rather than kept as dead code — and the `release` build tag it |
| 66 | was the only user of went with it. There is no release/non-release |
| 67 | distinction left for this gate to hold apart; `go test` covers the |
| 68 | fail-closed properties that matter now |
| 69 | (`TestNewRefusesMissingInstanceKeyOutsideDevelopment`, |
| 70 | `TestNewRefusesMissingMailOutsideDevelopment`, |
| 71 | `TestSignInRequiredForEverything` — all `internal/docs/auth_test.go`). |
| 72 | `scripts/run-dev.sh` builds and runs the ordinary binary with `-env |
| 73 | development`, a fixed port, and a scratch database — one command to see |
| 74 | the app in a browser, signed in for real via a magic link logged to the |
| 75 | terminal instead of mailed. |
| 76 | |
| 77 | The embedded WASM is built on Linux amd64 using fixed `/work`, |
| 78 | `/usr/local/cargo` and `/usr/local/rustup` paths. Rust/Cargo's crate identities |
| 79 | otherwise change code ordering across hosts, even with remapped source paths. |
| 80 | Linux amd64 uses unprivileged bubblewrap when available; other hosts use the |
| 81 | pinned Rust Docker image. Install Docker on development Macs. Both the artifact |
| 82 | builder and containment tests use this path; the freshness comparison remains |
| 83 | byte-for-byte. Docker caches and writable targets are separate from source. |
| 84 | |