macOS is no longer deferred — built natively on a Tart (Apple-Silicon) runner (real hardware → licensed SDK, no grey area). Amendment documents release-macos.yaml (dispatch-only, needs main), the libiconv de-nix + ad-hoc re-sign, the runner-label `:host` backend nuance, generation-based cache pruning, and D2-on-macOS (system libs only). All six D1 targets now produce artifacts. Updates the deferred list + index entry.
7.0 KiB
CI / Build Architecture Decision Records
Decision records for the continuous-integration + release pipeline
subproject — the Gitea Actions workflows under .gitea/, the nix CI image,
and the release tooling. These are kept in their own namespace, separate
from the project-wide ADRs in docs/adr/, so CI
decisions never compete with the main global ADR sequence for numbers — the
same split the website subproject uses (docs/website/adr/, on the website
branch), and for the same reason (see
ADR-0000 "Numbering discipline").
Numbering. Files are named <date>-adr-ci-<NNN>.md and referenced in
prose as ADR-ci-NNN. The <date> (the ADR's accepted/created day,
YYYYMMDD) plus the ci segment keeps the namespace disjoint from main's
integers. Assign the next free NNN from this index. Every ADR change
updates this index in the same edit (the ADR-0000 index-upkeep rule applies
here too).
Index
- ADR-ci-001 — CI + release pipeline on Gitea Actions — Accepted 2026-06-12 (implemented the same day on the
cibranch). Establishes the CI/release pipeline on the self-hosted Gitea instance's Actions runner (ci-public). Runner model (established by a throwaway probe): jobs execute inside a container (catthehacker/ubuntu:act-22.04by default), as root, so the runner host's nix is not reachable from steps. Toolchain delivery: a baked CI image —node:22-bookworm-slim(satisfies the act_runner job-container contract:/bin/sleepkeep-alive,bash,nodefor JS actions; a barenixos/niximage lacks these and won't start) + single-user nix + the flake's devShell pre-warmed — built bybuild-ci-image.yamlvia DinD and pushed to the Gitea container registry as a public package, so CI runsnix develop -c …against the same pinned toolchain as dev (the flake, ADR-ci-002) with a warm store (~1.4 s to a ready toolchain). Gate (ci.yaml):clippy -D warnings+cargo testinside that image on branch pushes + PRs; fmt deliberately not gated (the tree isn't stock-rustfmt-clean — user decision, revisit onmain; see ADR-ci-002). Release (release.yaml): on av*tag, runs the tests, builds the staticx86_64-unknown-linux-muslbinary (D2: single static binary, no runtime deps — the glibc/nix build is non-portable), strips it, and publishes it + a.sha256to a Gitea release via the API and the auto-providedGITEA_TOKEN. Triggers: gate + image-build are scoped to branch pushes (branches: ['**']) so a release tag doesn't spuriously re-run them; the image-build additionally path-filters to its inputs (Dockerfile/flake/toolchain); the release owns tags. Auth: a dedicated PAT (REGISTRY_USERNAME/REGISTRY_TOKENsecrets) pushes the image; the autoGITEA_TOKENpublishes releases. Scope: the original release job was Linux x86_64 only; it's now the four non-macOS D1 targets (Linux + Windows × x86_64/aarch64) cross-built via cargo-zigbuild — see ADR-ci-003. macOS, D3 package-manager manifests, CI-speed dependency caching, and the website's static→Cloudflare deploy remain deferred, added step by step. Verified live: probe → runner facts; image built + checked locally; gate green (2424 tests); release exercised end-to-end (v0.0.0-citest2published with binary + checksum). Builds on ADR-ci-002 (the nix flake, relocated here from main's ADR-0049 to avoid exactly this cross-branch collision). - ADR-ci-002 — Nix flake for a reproducible dev + build environment — Accepted 2026-06-12 (relocated from main's ADR-0049 on the same day — content unchanged — to keep CI/dev-env decisions out of
main's integer sequence). The single, version-pinned declaration of the dev and build toolchain so CI never relies on whatever Rust is on the build machine — mirroring datamage ADR 0046, but far simpler (pure-Rust TUI). Root Nix flake with two outputs:devShells.default(pinned Rust 1.95.0 viarust-toolchain.toml+rust-overlay,cargo-sweep, and the musl cc for the static release build) andpackages.default(rustPlatform.buildRustPackagefrom the committedCargo.lock;doCheck = false). Exact-pin (not floatingstable) sonix flake updatecan't surprise-bump clippy past the-D warningsgate. System inputs near-empty by design (libsqlite3-sys bundled→ stdenv cc only;arboard→x11rbpure-Rust)..envrc(use flake) for direnv parity. Verified through the flake:nix buildyields a working binary, clippy clean, 2424 tests pass / 0 fail / 1 intentional ignored doctest. Consumed by ADR-ci-001 (the pipeline). Alternatives rejected: dev-shell-only; a standardrust:1.95CI image (a second toolchain definition = drift);rustupon the build host (non-reproducible). - ADR-ci-003 — Cross-platform release builds (the D1 matrix) — Accepted 2026-06-13 (implemented + a real matrix release verified the same day — tag
v.0.0.0-citest3published 8 assets). Cross-compiles the four non-macOS D1 targets from the Linux x86_64 runner withcargo-zigbuild(Zig's bundled clang + libc as one universal cross cc/linker, incl. rusqlite's bundled SQLite C; added to the flake devShell, replacing the single-target musl cc):x86_64/aarch64-unknown-linux-musl(musl + crt-static → fully static, D2) andx86_64-pc-windows-gnu/aarch64-pc-windows-gnullvm(Zig statically links libc → standalone.exe). Windowssynchronizationstub: Rust std links-lsynchronization(WaitOnAddress thread-parking), an import lib rust-overlay's toolchain doesn't ship and Zig's mingw lacks; the symbols are forwarded bykernel32, so an empty 8-byte stublibsynchronization.a(ci/winstub/, wired via.cargo/config.tomlfor the Windows targets only) satisfies the linker. Workflow:release.yaml=testonce (host) →buildmatrix over the four targets (needs: test,fail-fast: false); each job packages binary (.exefor Windows) +.sha256and uploads to the shared release via idempotent create-or-get. macOS (2026-06-14 amendment) — built natively on a Tart (Apple-Silicon) runner (runs-on: macos), which makes the SDK fully licensed and dissolves the grey-area/public-image problem;release-macos.yamlis dispatch-only (intermittent runner; becomes triggerable once CI is onmain), de-nixes the binary's libiconv load path (install_name_tool→/usr/lib) + re-signs ad-hoc, and uploads to the tagged release. D1 complete (all six targets). Alternatives rejected:cross(no macOS, needs DinD), per-target nix cross (Windows-aarch64 unpackaged, macOS-from-Linux unsupported), a reallibsynchronization.a(more machinery, doesn't cover Windows-aarch64). Runtime-verified by the user (2026-06-13): Linux x86_64 + Windows aarch64 run correctly; Linux aarch64 + Windows x86_64 are the outstanding runtime checks. Builds on ADR-ci-002 (flake) and fills in ADR-ci-001 §3 (Release).