From d7b12e8c48ed875b6c2bb32720b8b659a892a934 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 11 Aug 2026 21:52:27 +0200 Subject: [PATCH] perf(3168): the deploy path stops compiling test binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crane's `buildDepsOnly` defaults `doCheck = true`, which adds `--all-targets` and a `cargo test --no-run` — so the dep cache compiles every dev-dependency and test harness in the tree. The checks need exactly that. `workspaceBuild` never runs a test and never links one, and was sitting on the same cache. Split into two, one per audience. Not a duplication of something shared: CI evaluates against hyperhive's own nixpkgs pin while a host overrides it — security patches cannot wait on a lock bump upstream — so the two closures already differ and neither substitutes for the other. Giving each its own cache therefore costs nobody a second build; it stops the deploy compiling artifacts only CI consumes. Both still build from crane's dummy source, so both hashes key on Cargo.toml/Cargo.lock: a code edit rebuilds neither, a dependency or toolchain bump rebuilds both. That bump is the recurring cost this targets — on a host overriding nixpkgs for security patches, it is every patch. ⚠️ The first build after this lands is cold: the derivation hashes move. --- nix/packages/default.nix | 15 +++++++++++---- nix/rust.nix | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/nix/packages/default.nix b/nix/packages/default.nix index d91e54bd..9f44d686 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -1,5 +1,5 @@ # All flake package outputs. Imported per system from flake.nix; the -# shared rust build wiring (cleanSrc / cargoArtifacts / +# shared rust build wiring (cleanSrc / cargoArtifactsBinOnly / # nativeBuildInputs) comes in via `rust` (see ../rust.nix). { pkgs, @@ -10,7 +10,7 @@ }: let inherit (pkgs) lib; - inherit (rust) cleanSrc cargoArtifacts nativeBuildInputs; + inherit (rust) cleanSrc cargoArtifactsBinOnly nativeBuildInputs; docsAttrs = import ../docs { inherit pkgs self; @@ -43,7 +43,7 @@ let }; # ONE compile of the whole workspace (every bin, sharing the - # prebuilt `cargoArtifacts` dep cache). The per-bin packages below + # prebuilt `cargoArtifactsBinOnly` dep cache). The per-bin packages below # are cheap copy-extractors over this, so workspace lib crates # (hive-sh4re, hive-claude, …) compile exactly once instead of once # per bin derivation. @@ -52,9 +52,16 @@ let # (carries the hyperhive-assets build input for the prompt-template # assertions in hive-agent::prompt::tests). Keeping them out of this # derivation means a prompt edit doesn't bust the cargo cache. + # + # …and the dep cache underneath is the one WITHOUT test targets. This + # derivation has never run a test — `doCheck = false` below says so — + # but it used to sit on a cache that compiled every dev-dependency and + # test harness anyway, work whose only consumers are the checks. A + # deploy paid for it on every toolchain or dependency change. workspaceBuild = craneLib.buildPackage { src = cleanSrc; - inherit cargoArtifacts nativeBuildInputs; + cargoArtifacts = cargoArtifactsBinOnly; + inherit nativeBuildInputs; pname = "hyperhive-workspace"; version = "0.1.0"; doCheck = false; diff --git a/nix/rust.nix b/nix/rust.nix index 5d15fae8..99ab07a5 100644 --- a/nix/rust.nix +++ b/nix/rust.nix @@ -45,6 +45,23 @@ rec { # `inherit cargoArtifacts;` so a workspace-only edit doesn't rebuild # deps. All consumers use the same `cleanSrc` so the input hash # stays consistent across the chain. + # Two dep caches, one per audience, and the split is not premature: + # crane's `buildDepsOnly` defaults `doCheck = true`, which adds + # `--all-targets` to the check and a `cargo test --no-run`, so the cache + # compiles every dev-dependency and test harness in the tree. The CHECKS + # need exactly that. A DEPLOY never runs a test binary and never links + # one, so on the deploy path that work is compiled and thrown away. + # + # They are separate rather than shared because they were never actually + # shared: CI evaluates against hyperhive's own nixpkgs pin, while a host + # overrides it (security patches cannot wait on a lock bump upstream), + # so the two closures differ and neither substitutes for the other. One + # cache per audience therefore costs nobody a second build — it just + # stops the deploy paying for artifacts only CI consumes. + # + # ⚠️ `buildDepsOnly` builds from `mkDummySrc`, so both hashes key on + # Cargo.toml/Cargo.lock rather than on `.rs` files: a code edit rebuilds + # neither, a dependency or toolchain change rebuilds both. cargoArtifacts = craneLib.buildDepsOnly { src = cleanSrc; # Workspace Cargo.toml is virtual (no `[package].name`), so crane @@ -56,4 +73,23 @@ rec { version = "0.1.0"; inherit nativeBuildInputs; }; + + # The deploy path's cache: same deps, no test targets. `doCheck = false` + # drops crane's `--all-targets` and its `cargo test --no-run`, so + # dev-dependencies and test harnesses are never compiled here. + # + # Only `packages` consume this. Anything that needs to *run* a test — + # `checks.cargo-test` — or lint one — `checks.clippy --all-targets` — + # takes `cargoArtifacts` above instead, and would rebuild what it needs + # if it were pointed here by mistake. + cargoArtifactsBinOnly = craneLib.buildDepsOnly { + src = cleanSrc; + # Distinct pname so the two are told apart in build logs and store + # paths; a shared name would make the useful question ("which cache + # is this rebuild?") unanswerable at a glance. + pname = "hyperhive-workspace-bin"; + version = "0.1.0"; + doCheck = false; + inherit nativeBuildInputs; + }; }