Watch
0
0
Fork
You've already forked hyperhive
0

pin internal/meta's hyperhive on the controller's rev and rebuild agents when it moves

The swarm controller offers its own running rev (services.hyperhive.c0re.
hyperhiveRev) as the hyperhive pin of internal/meta and takes it only when
the forge compare API shows it descends from the committed pin; merges to
hyperhive main no longer move the pin, and the first commit pins main's head.
When a commit moves the pin, every placed agent gets a TriggerDeploy.

A deploy after a meta update no longer waits on it succeeding (after_any), so
a down builder does not hold back hives that render locally.
This commit is contained in:
atlas 2026-10-24 14:29:12 +02:00
commit 22e2d30581
10 changed files with 1027 additions and 148 deletions

View file

@ -421,3 +421,135 @@ pub fn agent_web_port(name: &str) -> u16 {
// Modulo of a u32 by a u16's value is guaranteed < u16::MAX, so try_from never fails.
WEB_PORT_BASE + u16::try_from(fnv1a(name) % u32::from(WEB_PORT_RANGE)).unwrap_or(0)
}
/// Whether `rev` is a full git commit id: 40 lowercase hex digits.
#[must_use]
pub fn is_rev(rev: &str) -> bool {
rev.len() == 40 && rev.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
}
/// The commit `input` is pinned to in a flake [`render_flake`] rendered:
/// the `<rev>` of its `<input>.url = "git+<url>?rev=<rev>";` line. `None` when
/// the flake has no such input, or its rev is not a commit id.
#[must_use]
pub fn pinned_rev<'a>(flake_nix: &'a str, input: &str) -> Option<&'a str> {
let prefix = format!(" {input}.url = \"");
let url = flake_nix
.lines()
.find_map(|l| l.strip_prefix(prefix.as_str()))?
.split_once('"')?
.0;
let (_, rev) = url.split_once("?rev=")?;
let rev = rev.split('&').next()?;
is_rev(rev).then_some(rev)
}
/// Whether a flake [`render_flake`] rendered builds agent `name` from the
/// config commit `config_head`. `None` is an archetype agent, which has no
/// config input to compare.
#[must_use]
pub fn builds_agent_at(flake_nix: &str, name: &str, config_head: Option<&str>) -> bool {
let attr = format!("\n {name} = mkAgent {{ name = \"{name}\";");
if !flake_nix.contains(&attr) {
return false;
}
config_head.is_none_or(|head| pinned_rev(flake_nix, &format!("agent-{name}")) == Some(head))
}
#[cfg(test)]
mod tests {
use swarm_wire::agent_archetype::ArchetypeRef;
use super::*;
const HYPERHIVE: &str = "1111111111111111111111111111111111111111";
const ARGUS: &str = "2222222222222222222222222222222222222222";
fn spec(name: &str, archetype: Option<ArchetypeRef>, pin: Option<AgentPin>) -> AgentSpec {
AgentSpec {
name: name.to_owned(),
port: agent_web_port(name),
archetype,
pin,
}
}
fn rendered() -> String {
let url = |repo: &str, rev: &str| {
pinned_git_url(&format!("https://f.t/{repo}.git"), Some(rev)).unwrap()
};
render_flake(
&url("hyperhive/hyperhive", HYPERHIVE),
&[
spec(
"argus",
None,
Some(AgentPin {
url: url("agent-configs/argus", ARGUS),
canonical_inputs: vec![],
}),
),
spec(
"rev",
Some(ArchetypeRef {
source: "hyperhive".to_owned(),
name: "reviewer".to_owned(),
}),
None,
),
],
&[],
false,
)
.unwrap()
}
#[test]
fn a_rendered_flake_names_the_commit_each_input_is_pinned_to() {
let flake = rendered();
assert_eq!(pinned_rev(&flake, "hyperhive"), Some(HYPERHIVE));
assert_eq!(pinned_rev(&flake, "agent-argus"), Some(ARGUS));
assert_eq!(
pinned_rev(&flake, "agent-rev"),
None,
"an archetype has no input"
);
assert_eq!(pinned_rev(&flake, "agent-ghost"), None);
}
#[test]
fn a_pin_that_is_not_a_commit_id_is_no_pin() {
let flake = " hyperhive.url = \"git+https://f.t/h.git?rev=main\";\n";
assert_eq!(pinned_rev(flake, "hyperhive"), None);
assert_eq!(
pinned_rev(
" hyperhive.url = \"git+https://f.t/h.git\";\n",
"hyperhive"
),
None
);
}
#[test]
fn an_agent_is_built_only_at_the_config_head_the_flake_pins() {
let flake = rendered();
assert!(builds_agent_at(&flake, "argus", Some(ARGUS)));
assert!(
!builds_agent_at(&flake, "argus", Some(HYPERHIVE)),
"a stale pin"
);
assert!(
!builds_agent_at(&flake, "ghost", Some(ARGUS)),
"an agent the flake lacks"
);
assert!(builds_agent_at(&flake, "rev", None));
assert!(!builds_agent_at(&flake, "ghost", None));
}
#[test]
fn an_agent_whose_name_ends_another_s_is_not_that_agent() {
let flake = rendered();
assert!(!builds_agent_at(&flake, "gus", Some(ARGUS)));
assert!(!builds_agent_at(&flake, "v", None));
}
}