Files
rdbms-playground/docs/ci/handoff/20260615-handoff-ci-01.md
claude@clouddev1 5f28de8ac3 docs(ci): move CI handoff into docs/ci/handoff (avoid main collision)
main independently wrote its own docs/handoff/20260615-handoff-70.md the
same day, so my global-sequence handoff-70 was an add/add conflict waiting
to merge. Relocate it to docs/ci/handoff/20260615-handoff-ci-01.md (its own
namespace, like docs/ci/adr) + a README index. main's handoff-70 is
untouched; the merge becomes conflict-free.
2026-06-15 16:56:50 +00:00

6.0 KiB

CI subproject handoff — 2026-06-15 (ci-01)

First handover for the CI / release subproject (the ci branch). Kept in docs/ci/handoff/, a namespace separate from the project's global docs/handoff/ session sequence so it can't collide with main's numbering — the same split as docs/ci/adr/, and needed for the same reason: main independently wrote its own handoff-70 this same day (just as it took ADR-0049), which would have collided.

A dedicated infrastructure session that built the project's entire CI/CD pipeline on the self-hosted Gitea Actions runner — from nothing to a live gate plus a six-target cross-platform release. Net: the CI / requirements.md TT5 item and D1/D2 are now done; D3 and a couple of TT5 tails remain. Decisions are recorded in the sibling ADR namespace docs/ci/adr/ (ADR-ci-001/002/003).

§1. State at handoff

Branch: ci (worktree). main has been merged into ci (commit 138e766, clean — ci and main touched disjoint files) so the gate runs against current main before CI lands there. Working tree clean except the in-progress doc updates from this handoff. Pushes/promotion are the user's step.

Gate verified locally on the merged code: clippy -D warnings clean; cargo test 2488 passing / 0 failing / 1 ignored (the long-standing friendly doctest). main's features came in with their tests (2424 → 2488).

Pipeline (.gitea/workflows/):

  • build-ci-image.yaml — builds + pushes the CI image (node:22-bookworm-slim
    • single-user nix + the flake's devShell pre-warmed) to the Gitea registry. Triggers only on image-input changes (Dockerfile / flake / toolchain).
  • ci.yaml — the gate: clippy -D warnings + cargo test, branch pushes + PRs (docs-only changes skipped).
  • release.yaml — on a v* tag: testbuild matrix over the four non-macOS targets via cargo-zigbuild, upload to the Gitea release.
  • release-macos.yamlworkflow_dispatch (tag input) on the Tart Apple-Silicon runner (runs-on: macos): test → build both *-apple-darwin → de-nix libiconv + ad-hoc re-sign → upload.

Verified live this session: the 4-target release published 8 assets (binary + .sha256 each) for tag v.0.0.0-citest3; the macOS build was proven portable (system-only deps) + signed + launches on the runner.

§2. What was built (and the non-obvious bits)

  • Nix flake (ADR-ci-002, relocated from a would-be main ADR-0049): one pinned toolchain (1.95.0) for dev and CI; cargo-zigbuild + zig (Linux only) for the cross targets; apple-sdk on darwin.
  • Runner facts (ADR-ci-001): jobs run inside a container (ci-publiccatthehacker/ubuntu), so host nix is unreachable — hence the baked image. The Mac runner is host execution; its label is macos (:host in the registration is the act_runner backend, not part of the label).
  • Cross-compile (ADR-ci-003): cargo-zigbuild for the 4 non-macOS targets. Windows needs an empty libsynchronization.a stub (ci/winstub/, wired via .cargo/config.toml) — std links -lsynchronization, absent from rust-overlay's toolchain + zig's mingw, but forwarded by kernel32.
  • macOS (ADR-ci-003 amendment): built on real Apple hardware (Tart), so the SDK is fully licensed — no osxcross grey area. The darwin stdenv bakes a /nix/store libiconv path into the binary; the build rewrites it to /usr/lib/libiconv.2.dylib (install_name_tool) and re-signs ad-hoc (codesign -f -s -; install_name_tool invalidates the signature, arm64 refuses unsigned). A guard fails the build on any remaining /nix/store dep.
  • Cache hygiene (Mac): the runner wipes the workspace each run, so cargo target/ never accumulates; the persistent nix store is bounded by generation (record the devShell in a persistent profile, keep the 2 newest via nix-env --delete-generations +2, GC the rest). First sweep reclaimed a ~3.8 GB one-time backlog of build scaffolding (source + build-only deps, not re-installed toolchains).

§3. Immediate next steps (user)

  1. Push ci → the gate re-runs in CI (should be green; no image rebuild — the merge didn't touch the flake/Dockerfile).
  2. Promote: git checkout main && git merge ci — a fast-forward (ci already contains main) — then push main. CI goes live; release-macos becomes dispatchable (workflow_dispatch needs the default branch).
  3. First real release: tag v0.1.0 (auto-builds the 4 Linux/Windows assets), then dispatch release-macos for v0.1.0 with the Mac up (adds the 2 macOS assets) → a full 6-binary release.
  4. Cleanup: delete the v.0.0.0-citest* test tags + their releases.
  5. Runner-side: add min-free/max-free to the Mac's /etc/nix/nix.conf as a hands-off nix-store backstop.

§4. Known gaps / follow-ups

  • Versioning is not wired into the binary (flagged by the user). The release git tag is nowhere in the produced binary — there is no --version flag, no CARGO_PKG_VERSION use anywhere in src/, and the release workflows use the tag only for the release name + asset filenames (rdbms-playground-<tag>-<target>). Cargo.toml is a static version = "0.1.0", decoupled from the tag. So a v0.5.0 tag yields a …-v0.5.0-… asset whose binary knows nothing of "0.5.0". To fix later: add a --version flag, and inject the tag at build time (e.g. a build.rs reading a CI-provided env, or bumping Cargo.toml as part of tagging) so the binary and the release agree.
  • D3 packaging — Homebrew / Scoop / winget / cargo binstall manifests (asset naming is already binstall-friendly).
  • TT5 tails — Windows is build-only (no execution runner); Tier-4 PTY (TT4) is unwired in CI.
  • fmt gate — deliberately off (tree isn't stock-rustfmt-clean); revisit on main.
  • Website → Cloudflare deploy — the separate, simpler workflow, still to do.