From 09c51c87aab8571c54bdca181b50688d15bdb8d3 Mon Sep 17 00:00:00 2001 From: damocles Date: Sat, 23 May 2026 14:44:57 +0200 Subject: [PATCH] meta: collapse hyperhive's nixpkgs into meta's via follows (#317) --- hive-c0re/src/meta.rs | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index 980a749..edffa4f 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -242,7 +242,21 @@ fn render_flake( use std::fmt::Write as _; let mut out = String::new(); out.push_str("{\n description = \"hyperhive deployed agents\";\n inputs = {\n"); + // Pin canonical nixpkgs revisions at the meta level so every input + // that pulls a nixpkgs sub-input can `follows = "nixpkgs"` and + // collapse to one shared node (closes #317). hyperhive's own + // flake.nix picks `nixos-25.11`; we mirror that here so meta and + // hyperhive don't diverge into two stable channels by default. + // Operator can override these at the meta layer to slide every + // dependent agent onto a different channel in one move. + out.push_str(" nixpkgs.url = \"github:NixOS/nixpkgs/nixos-25.11\";\n"); + out.push_str(" nixpkgs-unstable.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n"); let _ = writeln!(out, " hyperhive.url = \"{hyperhive_flake}\";"); + // Collapse hyperhive's own `nixpkgs` + `nixpkgs-unstable` inputs + // into meta's. Without this, hyperhive's flake.nix declarations + // become independent `nixpkgs_N` nodes in meta/flake.lock. + out.push_str(" hyperhive.inputs.nixpkgs.follows = \"nixpkgs\";\n"); + out.push_str(" hyperhive.inputs.nixpkgs-unstable.follows = \"nixpkgs-unstable\";\n"); for spec in agents { let _ = writeln!( out, @@ -329,6 +343,52 @@ fn render_flake( out } +#[cfg(test)] +mod tests { + use super::*; + + fn sample_spec(name: &str, is_manager: bool, port: u16) -> AgentSpec { + AgentSpec { + name: name.to_owned(), + is_manager, + port, + } + } + + #[test] + fn render_flake_declares_canonical_nixpkgs() { + let out = render_flake( + "github:example/hyperhive", + 8000, + "she/her", + &std::collections::HashMap::new(), + &[sample_spec("alice", false, 9001)], + ); + // Top-level nixpkgs inputs pinned by meta — every nested + // nixpkgs input can follow these instead of resolving its own + // (closes #317). + assert!(out.contains("nixpkgs.url = \"github:NixOS/nixpkgs/nixos-25.11\"")); + assert!(out.contains("nixpkgs-unstable.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\"")); + } + + #[test] + fn render_flake_collapses_hyperhive_nixpkgs_via_follows() { + let out = render_flake( + "github:example/hyperhive", + 8000, + "she/her", + &std::collections::HashMap::new(), + &[], + ); + // hyperhive's own `nixpkgs` + `nixpkgs-unstable` declarations + // get redirected at meta's. Without these, meta/flake.lock + // ends up with separate `nixpkgs_N` nodes for hyperhive's + // copy (the pre-#317 status quo). + assert!(out.contains("hyperhive.inputs.nixpkgs.follows = \"nixpkgs\"")); + assert!(out.contains("hyperhive.inputs.nixpkgs-unstable.follows = \"nixpkgs-unstable\"")); + } +} + async fn git_is_clean(dir: &Path) -> Result { let out = lifecycle::git_command() .current_dir(dir)