hyperhive/nix/checks.nix
iris 7fc426b4dd swarmctl: add CLI reference docs, same pattern as hivectl
Adds swarmctl markdown-docs (a hidden Verb, same clap-markdown +
hide=true shape as hivectl markdown-docs) and generates
docs/tools/swarmctl-cli.md from it. Wires a swarmctl-docs freshness
check into nix/checks.nix, same shape as hivectl-docs, diffing against
packages.swarmctl.

One real gotcha: PathArgs::resolve() reads required
SWARMCTL_AUTHELIA_* deployment env vars and errors if unset -
swarmctl markdown-docs must not go through that path (it needs none of
those vars, and the docs build runs it outside any real deployment).
Restructured main() so resolve() only runs for the User arm, not
unconditionally before the match.

Also links the new doc from docs/tools/README.md (new 'for the swarm
operator' section), CLAUDE.md's swarmctl bullet, and
docs/conventions.md's flake-check list.

Verified: cargo check/clippy -D warnings/test/fmt -p swarmctl all
clean; swarmctl markdown-docs diffs clean against the committed doc
(checked against both a plain cargo build and the actual nix build.
#swarmctl output); scripts/check-issue-refs.sh clean.
2026-08-11 21:55:56 +02:00

157 lines
6.9 KiB
Nix

# Flake checks: formatting, the clippy gate, the workspace test run,
# the nix-options docs eval, and the hivectl CLI-reference freshness
# check. Imported per system from flake.nix.
{
pkgs,
craneLib,
rust,
self,
system,
treefmt-eval,
}:
let
inherit (rust) cleanSrc cargoArtifacts nativeBuildInputs;
in
{
formatting = treefmt-eval.config.build.check self;
# Clippy via crane's first-class `cargoClippy` builder. Reuses the
# shared `cargoArtifacts` (deps already built) and runs
# `cargo clippy --workspace --all-targets` directly.
#
# `-D warnings` makes every rustc/clippy warning a hard CI gate.
# Pedantic is gated too, deliberately: the workspace lint table
# (Cargo.toml) sets `pedantic = deny`, so pedantic lints are errors
# both locally and here. We want that — a toolchain bump that adds a
# new pedantic lint reds the build until the code is updated, rather
# than sliding in unnoticed. (The lint table allows a few noisy
# pedantic lints explicitly, e.g. `must_use_candidate`; those keep
# their allow via higher priority.)
clippy = craneLib.cargoClippy {
src = cleanSrc;
inherit cargoArtifacts nativeBuildInputs;
pname = "hyperhive-workspace";
version = "0.1.0";
cargoClippyExtraArgs = "--workspace --all-targets -- -D warnings";
};
# `cargo test --workspace` lifted out of the package builds so the
# `hyperhive-assets` dep (which `hive-agent::prompt::tests`
# needs via `HIVE_ASSETS_DIR` to assert against the actual
# production prompt template) is scoped to this one check
# instead of bleeding into the binary derivations' input
# hash. Net: editing `hive-agent/prompts/system.md` still
# rebuilds this test check (correct — the tests assert
# against its wording), but `packages.default` and the
# per-container toplevels stay fully cached.
cargo-test = craneLib.cargoTest {
src = cleanSrc;
inherit cargoArtifacts nativeBuildInputs;
pname = "hyperhive-workspace";
version = "0.1.0";
cargoTestExtraArgs = "--workspace";
HIVE_ASSETS_DIR = "${self.packages.${system}.assets}/share/hyperhive";
};
# Nix options docs evaluation. Cheap: pulls in `nixosOptionsDoc` +
# the host module's stub eval, no rust or frontend deps. CI fails
# fast if a module change breaks option declarations or the doc
# rendering. Reuses the `packages.<system>.docs` derivation so the
# per-system eval of nix/docs/default.nix happens once.
inherit (self.packages.${system}) docs;
# Frontend build. Builds the `hyperhive-frontend` npm package, whose
# fixed-output npm-deps derivation pins `npmDepsHash`
# (nix/packages/frontend.nix). No other check exercises that FOD:
# `cargo-test` forces `.#assets`, but `assets` carries no frontend
# dependency, so without this check a stale `npmDepsHash` after a
# `package-lock.json` bump that forgets to recompute it sails through
# every PR and only breaks when the host toplevel is built on deploy.
# Reusing the already-defined `packages.<system>.frontend` derivation
# makes such a hash mismatch a red PR instead of a red main.
#
# `swarm-ui` alongside it for the same FOD-staleness reason (shares
# the same lockfile / `npmDepsHash`, see nix/packages/swarm-ui.nix) —
# plus, since it's not in `packages.default`'s dependency closure like
# `frontend` is, this is also the only thing that actually *builds*
# it in CI rather than just evaluating it (`nix flake check` doesn't
# build every `packages.<system>.*` output on its own).
inherit (self.packages.${system}) frontend swarm-ui;
# TypeScript type-check for `swarm-ui` (`npm run typecheck`, i.e.
# `tsc --noEmit`). esbuild only strips types without checking them
# (see `frontend/packages/swarm-ui/build.mjs`'s own comment), so
# without this a real type error would still build clean and pass
# every other check. Argus flagged the gap on this PR's review;
# wiring it in now per mara's follow-up ask on the same PR, rather
# than leaving it as a someday-issue.
#
# A separate `buildNpmPackage` derivation rather than folding into
# `swarm-ui` itself: this one's whole job is to *fail loudly* on a
# type error, and `npm ci` + `tsc` needs `typescript` in
# `node_modules`, which `swarm-ui.nix`'s actual build doesn't need
# (esbuild transpiles TS without it) — no reason to make the real
# build depend on the dev-only type-checker.
swarm-ui-typecheck = pkgs.buildNpmPackage {
pname = "hyperhive-swarm-ui-typecheck";
version = "0.0.0";
src = ../frontend;
# Same lockfile as `frontend`/`swarm-ui` above — recompute in
# lockstep with those two whenever `frontend/package-lock.json`
# changes (`prefetch-npm-deps frontend/package-lock.json`).
npmDepsHash = "sha256-LIwW5Nn9cSqJHCm1czPIVxQdP5OqWk7U/4kvkwaj2ts=";
buildPhase = ''
runHook preBuild
npm run typecheck --workspace=packages/swarm-ui
runHook postBuild
'';
dontNpmInstall = true;
installPhase = ''
runHook preInstall
mkdir -p $out
touch $out/ok
runHook postInstall
'';
};
# `hivectl` CLI reference freshness check. The committed
# markdown at `docs/tools/hivectl-cli.md` is the rendered
# output of the hidden `hivectl markdown-docs` subcommand
# (clap-markdown walks the binary's own command tree). This
# check regenerates it from the built binary and fails if the
# committed copy drifted — so a verb / flag / help-string edit
# that forgets to refresh the doc is caught in CI. Reuses the
# already-built `packages.<system>.default` (no extra compile).
# Regenerate locally with:
# nix build .#default
# ./result/bin/hivectl markdown-docs > docs/tools/hivectl-cli.md
hivectl-docs = pkgs.runCommand "hivectl-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; } ''
${self.packages.${system}.default}/bin/hivectl markdown-docs > generated.md
if ! diff -u ${../docs/tools/hivectl-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/hivectl-cli.md is out of date regenerate it:" >&2
echo " nix build .#default && ./result/bin/hivectl markdown-docs > docs/tools/hivectl-cli.md" >&2
exit 1
fi
touch "$out"
'';
# `swarmctl` CLI reference freshness check — same shape as
# `hivectl-docs` above, `swarmctl markdown-docs` (clap-markdown over
# its own command tree) instead. Reuses `packages.<system>.swarmctl`
# (already built as its own package, out of `daemonBins` — see
# nix/packages/default.nix's comment on it).
swarmctl-docs = pkgs.runCommand "swarmctl-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; } ''
${self.packages.${system}.swarmctl}/bin/swarmctl markdown-docs > generated.md
if ! diff -u ${../docs/tools/swarmctl-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/swarmctl-cli.md is out of date regenerate it:" >&2
echo " nix build .#swarmctl && ./result/bin/swarmctl markdown-docs > docs/tools/swarmctl-cli.md" >&2
exit 1
fi
touch "$out"
'';
}