diff --git a/hive-c0re/src/forge/repos.rs b/hive-c0re/src/forge/repos.rs index b2500b48..0985410e 100644 --- a/hive-c0re/src/forge/repos.rs +++ b/hive-c0re/src/forge/repos.rs @@ -2,6 +2,8 @@ //! `agent-configs/`. The typed API-client constructor + org-name //! constants live in the module root (`super`). +use std::path::Path; + use super::{CONFIG_ORG, forge_git_url, reader_token}; /// Seed an agent's `applied` repo, missing its `.git` entirely (first spawn, @@ -15,7 +17,7 @@ use super::{CONFIG_ORG, forge_git_url, reader_token}; /// forge has no `main` for the agent yet (the normal case right after /// `swarm-controller` creates it, before it has pushed anything), or any git /// step fails. Callers fall back to the template or the original hard error. A -/// failed seed leaves no `.git` behind, so a later "is it seeded" check reads +/// failed seed leaves the directory empty, so a later "is it seeded" check reads /// the same as before the attempt. Only `true` once `applied` is seeded and /// tagged `deployed/0`. pub async fn fetch_config_main_into_applied(name: &str) -> bool { @@ -79,7 +81,42 @@ pub async fn fetch_config_main_into_applied(name: &str) -> bool { } .await; if !seeded { - let _ = std::fs::remove_dir_all(applied.join(".git")); + clear_dir(&applied); } seeded } + +/// Empty `dir`, keeping the directory itself: a failed seed may have written +/// the working tree as well as the `.git`, and the template fallback must start +/// from nothing rather than adopt files that were meant for the forge's commit. +fn clear_dir(dir: &Path) { + let Ok(entries) = std::fs::read_dir(dir) else { + return; + }; + for entry in entries.flatten() { + let path = entry.path(); + let _ = if path.is_dir() { + std::fs::remove_dir_all(path) + } else { + std::fs::remove_file(path) + }; + } +} + +#[cfg(test)] +mod tests { + use super::clear_dir; + + #[test] + fn clear_dir_empties_a_dir_but_keeps_it() { + let dir = tempfile::tempdir().expect("tempdir"); + std::fs::create_dir(dir.path().join(".git")).expect("mkdir .git"); + std::fs::write(dir.path().join(".git/HEAD"), "ref").expect("write HEAD"); + std::fs::write(dir.path().join("agent.nix"), "{}").expect("write agent.nix"); + + clear_dir(dir.path()); + + assert!(dir.path().is_dir(), "the directory itself stays"); + assert_eq!(std::fs::read_dir(dir.path()).expect("read_dir").count(), 0); + } +}