lifecycle: pre-wire applied remote in proposed

setup_proposed now lands a git remote named 'applied' on every
proposed/<n>/config pointing at /applied/<n>/.git — the path as
seen from inside the manager container, where the RO bind in
set_nspawn_flags makes the URL resolve. From the manager:

  git fetch applied
  git log applied/main
  git show applied/refs/tags/deployed/<id>
  git diff applied/main HEAD
  git rebase applied/main

all work without manually constructing the path each time. The
RO bind blocks push at the kernel level so the remote can only
fetch. Idempotent — also applied to pre-existing proposed repos
(no-op if the remote is already correct, set-url if drifted)
so the startup migration picks up the wiring on existing
agents.
This commit is contained in:
müde 2026-05-16 00:25:43 +02:00
parent 3d14ddeb7d
commit c42ad1330c

View file

@ -285,25 +285,50 @@ pub async fn list() -> Result<Vec<String>> {
/// manager can't be surprised by hive-c0re commits or working-tree
/// resets.
pub async fn setup_proposed(proposed_dir: &Path, name: &str) -> Result<()> {
if proposed_dir.join(".git").exists() {
return Ok(());
let fresh = !proposed_dir.join(".git").exists();
if fresh {
std::fs::create_dir_all(proposed_dir)
.with_context(|| format!("create {}", proposed_dir.display()))?;
let agent_path = proposed_dir.join("agent.nix");
if !agent_path.exists() {
std::fs::write(&agent_path, initial_agent_nix(name))
.with_context(|| format!("write {}", agent_path.display()))?;
}
let flake_path = proposed_dir.join("flake.nix");
if !flake_path.exists() {
std::fs::write(&flake_path, initial_flake_nix())
.with_context(|| format!("write {}", flake_path.display()))?;
}
git(proposed_dir, &["init", "--initial-branch=main"]).await?;
git(proposed_dir, &["add", "agent.nix", "flake.nix"]).await?;
git_commit(proposed_dir, "hive-c0re init").await?;
}
std::fs::create_dir_all(proposed_dir)
.with_context(|| format!("create {}", proposed_dir.display()))?;
let agent_path = proposed_dir.join("agent.nix");
if !agent_path.exists() {
std::fs::write(&agent_path, initial_agent_nix(name))
.with_context(|| format!("write {}", agent_path.display()))?;
// Idempotently wire the `applied` remote — purely for the
// manager's ergonomics. The URL is the path inside the manager
// container (`/applied/<n>/.git`), where the RO bind in
// `set_nspawn_flags` makes it real. hive-c0re itself never
// dereferences this remote; the host-side fetch in
// `request_apply_commit` uses absolute host paths.
ensure_applied_remote(proposed_dir, name).await
}
async fn ensure_applied_remote(proposed_dir: &Path, name: &str) -> Result<()> {
let want = format!("/applied/{name}/.git");
let existing = git_command()
.current_dir(proposed_dir)
.args(["remote", "get-url", "applied"])
.output()
.await
.with_context(|| format!("git remote get-url applied in {}", proposed_dir.display()))?;
if existing.status.success() {
let current = String::from_utf8_lossy(&existing.stdout).trim().to_owned();
if current == want {
return Ok(());
}
// URL drifted (path scheme changed, etc.) — re-point it.
return git(proposed_dir, &["remote", "set-url", "applied", &want]).await;
}
let flake_path = proposed_dir.join("flake.nix");
if !flake_path.exists() {
std::fs::write(&flake_path, initial_flake_nix())
.with_context(|| format!("write {}", flake_path.display()))?;
}
git(proposed_dir, &["init", "--initial-branch=main"]).await?;
git(proposed_dir, &["add", "agent.nix", "flake.nix"]).await?;
git_commit(proposed_dir, "hive-c0re init").await?;
Ok(())
git(proposed_dir, &["remote", "add", "applied", &want]).await
}
/// Set up the applied repo. First-spawn only: init the repo, pull