From 4a9aff584ec09b4eaad0b86abf3cf65926ab6f60 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 10 Jul 2026 20:38:26 +0200 Subject: [PATCH] =?UTF-8?q?refactor(#2280):=20packages.default=20=3D=20sym?= =?UTF-8?q?linkJoin=20=E2=80=94=20one=20rustc=20per=20bin,=20no=20double?= =?UTF-8?q?=20builds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each binary now has its own derivation (daemonBins for hive-c0re/hive-ag3nt/MCP servers, hivectlPkg, hiveForgePkg), all sharing cargoArtifacts for a single rustc compilation. packages.default = pkgs.symlinkJoin assembles them without any additional compilation — no rustc is invoked more than once regardless of which packages the operator builds together. NixOS module consumers (pkgs.hyperhive = packages.default) are unaffected: symlinkJoin exposes all binaries at the same /bin/ paths. --- flake.nix | 111 +++++++++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 59 deletions(-) diff --git a/flake.nix b/flake.nix index c27254ee..f8a32275 100644 --- a/flake.nix +++ b/flake.nix @@ -171,66 +171,32 @@ inherit (nixpkgs) lib; inherit (nixpkgs.lib) nixosSystem; }; - in - { - # Build the workspace binaries without running tests. Tests - # are run as a separate check (`checks.cargo-test`) that - # carries the `hyperhive-assets` build input — `hive-ag3nt:: - # prompt::tests` reads the production prompt template at test - # runtime through `$HIVE_ASSETS_DIR`, so wiring the env var - # into the build phase here would make the prompt's hash a - # build input of `default` (defeats the asset-split cache goal: a - # prompt edit would still bust the binary derivation, even - # though no .rs file changed). Keeping tests in a separate - # check derivation localises the asset-rebuild blast radius - # to that one check — `nix flake check` still exercises them. - default = craneLib.buildPackage { + # Daemon + harness + MCP server binaries. Excludes hivectl and + # hive-forge which are their own derivations below — so each bin + # is compiled exactly once. `packages.default` joins all three + # via symlinkJoin; no binary is compiled more than once regardless + # of which packages the operator builds together. + # + # Tests are kept in the separate `checks.cargo-test` derivation + # (carries the hyperhive-assets build input for the prompt-template + # assertions in hive-ag3nt::prompt::tests). Keeping them out of the + # binary derivations means a prompt edit doesn't bust the cargo cache. + daemonBins = craneLib.buildPackage { src = cleanSrc; inherit cargoArtifacts; - # `installShellFiles` provides `installShellCompletion` and - # `makeWrapper` provides `wrapProgram` for the postInstall below; - # appended (not in the shared set) so they're build inputs only of - # this binary derivation. - nativeBuildInputs = nativeBuildInputs ++ [ - pkgs.installShellFiles - pkgs.makeWrapper - ]; - pname = "hyperhive-workspace"; + cargoExtraArgs = "--bin hive-c0re --bin hive-agent --bin hive-agent-mcp --bin hive-agent-wake --bin hive-bash-daemon --bin hive-bash-mcp --bin hive-matrix-daemon --bin hive-matrix-mcp"; + pname = "hyperhive-daemon"; version = "0.1.0"; - meta.description = "hyperhive workspace (hive-c0re, hive-ag3nt, hive-root)"; + meta.description = "hyperhive daemon + agent harness + bash/matrix MCP servers"; doCheck = false; - # Ship hivectl shell completions in the package (the binary's own - # `completions ` verb is the single source of truth, so they - # never drift from the actual verbs). Lands at - # `$out/share/{zsh/site-functions,bash-completion,fish}/…`; an - # operator gets working completion as soon as hivectl is in their - # system/user profile with the shell's completion enabled. - # - # Then wrap hivectl with `wireguard-tools` on PATH so its `wg` - # subcommands (`wg init`/`peer`/`status`) work even before the - # WireGuard mesh is configured — `wg init` is the *first* setup - # step, run before `swarm.wireguard.enable` (which would otherwise - # be what pulls wireguard-tools onto the system). Completion - # generation runs first since wrapProgram renames the real binary. - postInstall = '' - installShellCompletion --cmd hivectl \ - --bash <("$out/bin/hivectl" completions bash) \ - --zsh <("$out/bin/hivectl" completions zsh) \ - --fish <("$out/bin/hivectl" completions fish) - wrapProgram "$out/bin/hivectl" \ - --prefix PATH : ${pkgs.wireguard-tools}/bin - ''; }; - # Operator CLI only — ships `hivectl` (with shell completions - # and the `wg` wrapper) without the daemon binaries. Suitable - # for `nix profile install .#hivectl` / `environment.systemPackages + # Operator CLI — ships `hivectl` (with shell completions and the + # `wg` wrapper) without the daemon binaries. Suitable for + # `nix profile install .#hivectl` / `environment.systemPackages # = [ inputs.hyperhive.packages.${system}.hivectl ]` when the - # operator only wants the admin CLI on their workstation and - # does not want `hive-c0re` or `hive-ag3nt` in PATH. - # Shares `cargoArtifacts` with `packages.default` so there is no - # double-compile cost when both are in the same `nix build` - # invocation. - hivectl = craneLib.buildPackage { + # operator only wants the admin CLI. Shares `cargoArtifacts` with + # `daemonBins` so there is no double-rustc cost. + hivectlPkg = craneLib.buildPackage { src = cleanSrc; inherit cargoArtifacts; cargoExtraArgs = "--bin hivectl"; @@ -238,10 +204,18 @@ version = "0.1.0"; meta.description = "hyperhive operator CLI"; doCheck = false; + # `installShellFiles` + `makeWrapper` scoped to this derivation + # only — daemon bins don't need them. nativeBuildInputs = nativeBuildInputs ++ [ pkgs.installShellFiles pkgs.makeWrapper ]; + # Ship shell completions (the binary's own `completions ` + # verb is the single source of truth, so they never drift from + # the actual verbs). Wrap with wireguard-tools so `hivectl wg` + # subcommands work before `swarm.wireguard.enable` is set (wg + # init is the very first setup step). Completion generation runs + # before wrapProgram since wrapProgram renames the real binary. postInstall = '' installShellCompletion --cmd hivectl \ --bash <("$out/bin/hivectl" completions bash) \ @@ -251,11 +225,11 @@ --prefix PATH : ${pkgs.wireguard-tools}/bin ''; }; - # Forgejo CLI only — ships `hive-forge` without the rest of - # the workspace. Useful for operator workstations / CI - # environments that only need forge access. Shares - # `cargoArtifacts` with `packages.default`. - hive-forge = craneLib.buildPackage { + # Forgejo CLI — ships `hive-forge` without the rest of the + # workspace. Useful for operator workstations / CI environments + # that only need forge access. Shares `cargoArtifacts` with + # `daemonBins`. + hiveForgePkg = craneLib.buildPackage { src = cleanSrc; inherit cargoArtifacts; cargoExtraArgs = "--bin hive-forge"; @@ -264,6 +238,25 @@ meta.description = "hyperhive Forgejo CLI"; doCheck = false; }; + in + { + # All workspace binaries in one derivation via symlinkJoin. + # Each binary is compiled exactly once (one rustc per bin, all + # sharing `cargoArtifacts`); symlinkJoin assembles the outputs + # without any additional compilation. The NixOS module's + # `pkgs.hyperhive` (= this) and `nix build .#` both land here. + default = pkgs.symlinkJoin { + name = "hyperhive"; + paths = [ + daemonBins + hivectlPkg + hiveForgePkg + ]; + }; + # Per-bin split packages — useful for operator workstations that + # only want the CLI(s) without the daemon binaries. + hivectl = hivectlPkg; + hive-forge = hiveForgePkg; # Bundled browser assets — see ./nix/frontend.nix. Output is # $out/{dashboard,agent}/ which the Rust binaries serve via # tower_http::ServeDir.