docs(ci): establish docs/ci/adr namespace (ci-001 pipeline, ci-002 flake)
ci / gate (push) Successful in 2m33s
ci / gate (push) Successful in 2m33s
Records the CI/release pipeline as ADR-ci-001 and relocates the nix-flake ADR from main's ADR-0049 to ADR-ci-002 (content unchanged, history note added). Both live in docs/ci/adr/ with a README index — a dated, ci-segmented namespace disjoint from main's integer ADR sequence, the same split the website subproject uses to avoid cross-branch number collisions. Drops the ADR-0049 entry from docs/adr/README. ci-001 covers the runner model, the baked nix CI image, the clippy+test gate, the static-musl release on tag, trigger hygiene, auth, and scope.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# ADR-ci-002: Nix flake for a reproducible dev + build environment
|
||||
|
||||
## Status
|
||||
|
||||
**Accepted (2026-06-12).** Implemented the same day on the `ci` branch:
|
||||
`flake.nix`, `flake.lock`, `rust-toolchain.toml`, `.envrc`. Verified
|
||||
end-to-end before acceptance — `nix develop` provides the pinned
|
||||
toolchain; `nix build .#default` produces a working binary; `cargo
|
||||
clippy --all-targets -- -D warnings` is clean and `cargo test` is
|
||||
**2424 passed / 0 failed / 1 ignored** (the ignored item is the
|
||||
intentional ```` ```ignore ```` doctest at `src/friendly/mod.rs:21`),
|
||||
all run *through the flake*. This ADR is the dev/build-environment
|
||||
foundation; the CI **pipeline** that consumes it (runner model, image,
|
||||
gate, release) is **ADR-ci-001**.
|
||||
|
||||
> **History.** Created as **ADR-0049** in `main`'s integer ADR namespace
|
||||
> (`docs/adr/`); moved here to **ADR-ci-002** on 2026-06-12 to keep the
|
||||
> CI/dev-env decisions out of `main`'s sequence and end the cross-branch
|
||||
> number collision (`main` independently reaches for the next integer too —
|
||||
> the same problem the website subproject hit). Content is otherwise
|
||||
> unchanged. See ADR-0000 "Numbering discipline".
|
||||
|
||||
## Context
|
||||
|
||||
The project is near feature-complete and CI is finally being set up
|
||||
(`requirements.md` **TT5**, **CI** in the deferred list). CI must not
|
||||
depend on whatever Rust/toolchain happens to be installed on the build
|
||||
machine — that is neither reproducible nor honest about what the build
|
||||
needs.
|
||||
|
||||
The sibling project **datamage** already solved this with a Nix flake
|
||||
(its ADR 0046): the flake is the single, version-pinned declaration of
|
||||
the toolchain, and both the dev shell and CI go through it so they
|
||||
cannot drift. We adopt the same pattern here. Ours is dramatically
|
||||
simpler than datamage's — this is a pure-Rust TUI with no Tauri /
|
||||
WebKitGTK / Node / WASM surface — so the flake carries almost no system
|
||||
dependencies.
|
||||
|
||||
Two build facts drove the (tiny) dependency set, confirmed from
|
||||
`Cargo.lock`:
|
||||
|
||||
- **`libsqlite3-sys` is built with `bundled`** → SQLite is compiled
|
||||
from vendored C, which needs a C compiler. `nixpkgs`' `stdenv`
|
||||
provides one automatically; nothing is declared for it.
|
||||
- **`arboard`'s clipboard backend is `x11rb`** — a pure-Rust socket
|
||||
XCB client that links *no* C X11 libraries. So no X11/`pkg-config`
|
||||
system inputs are needed to build or test. A live X server is only
|
||||
required at *runtime* to actually copy; headless sessions fall back
|
||||
to OSC 52.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a **Nix flake** at the repository root as the canonical
|
||||
declaration of the dev *and* build environment.
|
||||
|
||||
- **`flake.nix`** exposes two outputs (user-chosen 2026-06-12 over a
|
||||
dev-shell-only variant):
|
||||
- **`devShells.default`** — the pinned Rust toolchain (from
|
||||
`rust-toolchain.toml` via `rust-overlay`) plus `cargo-sweep` for
|
||||
the `target/` build-hygiene discipline (CLAUDE.md / the datamage
|
||||
ADR 0050 equivalent).
|
||||
- **`packages.default`** (= `packages.rdbms-playground`) — a
|
||||
`rustPlatform.buildRustPackage` that produces the binary
|
||||
reproducibly from the pinned toolchain and the committed
|
||||
`Cargo.lock` (`cargoLock.lockFile` → `importCargoLock`, which
|
||||
fetches each dependency by its lockfile checksum: offline,
|
||||
deterministic, no `cargoHash` to churn). `nix build` yields the
|
||||
artifact CI's gate/release can consume.
|
||||
- **`rust-toolchain.toml`** pins an **exact stable release**
|
||||
(`1.95.0`), not the floating `stable` channel, so `nix flake update`
|
||||
cannot surprise-bump Rust into new clippy lints that would fail the
|
||||
`-D warnings` gate (same reasoning as datamage ADR 0046). Components:
|
||||
`rustfmt` + `clippy`. No coverage/WASM tooling and no
|
||||
cross-compilation targets yet — those are added when the release
|
||||
matrix needs them, not before.
|
||||
- **`flake.lock`** pins every input (`nixpkgs` `nixos-26.05`,
|
||||
`rust-overlay`, `flake-utils`) to a commit, making the env
|
||||
bit-reproducible.
|
||||
- **`.envrc`** contains `use flake` for direnv auto-activation, kept
|
||||
for parity with datamage even though direnv is not installed on the
|
||||
current dev VM (entry is via `nix develop`).
|
||||
- **`packages.default` sets `doCheck = false`.** The test suite is
|
||||
*not* run during `nix build` — the Nix build sandbox has no `HOME`
|
||||
and no X server, which fights the project-directory / clipboard
|
||||
paths the tests touch. Tests run as their own CI stage via
|
||||
`nix develop -c cargo test`, keeping "build the artifact" and "run
|
||||
the suite" cleanly separate.
|
||||
- **The package version is read from `Cargo.toml`** via
|
||||
`builtins.fromTOML`, so it never drifts from the crate metadata.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **One toolchain definition.** Dev and CI share the exact pinned
|
||||
toolchain; they cannot drift. New contributors run `nix develop`
|
||||
(or get auto-activation via direnv) and have the same Rust as CI.
|
||||
- **D2 (static binary) is unaffected and still pending.** The
|
||||
`nix build` artifact links the Nix-store glibc *dynamically* — it is
|
||||
a reproducible build/test artifact, **not** the single static
|
||||
release binary D2 calls for. Release binaries will target a static
|
||||
toolchain (e.g. `x86_64-unknown-linux-musl`) in the forthcoming CI
|
||||
release work; that is a release-step concern, not a dev-shell one.
|
||||
- **`fmt` is deliberately *not* gated yet.** The tree is not clean
|
||||
under stock `rustfmt` (~100 files would change; no `rustfmt.toml` is
|
||||
committed and the code was shaped by something other than default
|
||||
`rustfmt`). Reformatting churns blame across every file and would
|
||||
conflict with the in-flight website branch and ongoing `main` work,
|
||||
so — user decision 2026-06-12 — the `fmt` gate is left out for now
|
||||
and revisited on `main`. The CI gate is `clippy` + `test`.
|
||||
- **Engine-name posture (CLAUDE.md) is respected.** The flake's
|
||||
comments may name SQLite/`rusqlite` where technically necessary
|
||||
(build-input rationale); no user-facing string is affected.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Dev-shell only (no build package).** Matches datamage exactly; CI
|
||||
would `cargo build` inside `nix develop -c`. Rejected (user choice):
|
||||
a `nix build` package gives a reproducible release artifact straight
|
||||
from the pinned toolchain, which the release job wants.
|
||||
- **A standard `rust:1.95` image in CI, flake for dev only.** Simpler
|
||||
in CI (no nix-in-CI caching to solve), but it is a *second* place
|
||||
that defines the toolchain — exactly the drift this ADR exists to
|
||||
prevent. Rejected for the unified-env goal; the nix-in-CI caching
|
||||
cost is solved in the CI pipeline work instead.
|
||||
- **`rustup` on the build machine.** The status quo CI would replace —
|
||||
non-reproducible, machine-dependent, the thing we are eliminating.
|
||||
|
||||
## Relationship to other decisions
|
||||
|
||||
- Mirrors **datamage ADR 0046** (nix flake dev env) and its build
|
||||
hygiene companion. This is the rdbms-playground analogue, scoped to
|
||||
a pure-Rust project.
|
||||
- Feeds **ADR-ci-001** (the CI + release pipeline), which consumes this
|
||||
flake for `requirements.md` **TT5** (CI runs the tiers) and the
|
||||
**D1/D2/D3** distribution items (the release uses a static musl target
|
||||
built through this flake).
|
||||
Reference in New Issue
Block a user