Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-c0re/src/forge/repos.rs
atlas b6b568e97e drop the hive's push of the legacy core/meta repo
Nothing reads core/meta: builds come from internal/meta. Remove push_meta, ensure_meta_remote, meta_read_access, ensure_repo, the per-agent forge sync that only served them, and the git commit path in meta.rs; remove_permission_files now just deletes the seed files.
2026-10-25 16:58:38 +01:00

139 lines
5.8 KiB
Rust

//! Repo plumbing on the local Forgejo: reseeding an agent's `applied` and
//! `proposed` repos from `agent-configs/<name>`. 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};
/// Reseed a revived agent's `applied` repo — missing its `.git` entirely,
/// e.g. after `destroy --purge` — by fetching `agent-configs/<name>`'s
/// `main` from the forge. Unlike `proposed` (seeded once at first spawn and
/// never touched again — see `lifecycle::setup_proposed`'s own doc
/// comment), forge `main` advances with every operator-merged config PR, so
/// this reflects the last merged config rather than a stale creation-time
/// snapshot. A config deployed through hive-c0re without a forge merge never
/// reaches it.
///
/// Best-effort: returns `false` (not an error) rather than bailing when there
/// is no [`reader_token`], `applied` already has a `.git` (nothing to do), or
/// any git step fails — callers fall back to a less-current source or the
/// original hard error. Only `true` once `applied` is
/// actually seeded and tagged `deployed/0`, mirroring first-spawn's own
/// `setup_applied` seed shape exactly.
pub async fn fetch_config_main_into_applied(name: &str) -> bool {
let Some(reader) = reader_token().await else {
return false;
};
let applied = crate::paths::applied_dir(name);
if applied.join(".git").exists() {
return false;
}
if let Err(e) = std::fs::create_dir_all(&applied) {
tracing::warn!(%name, error = ?e, "forge: applied reseed mkdir failed");
return false;
}
if let Err(e) = crate::lifecycle::git(&applied, &["init", "--initial-branch=main"]).await {
tracing::warn!(%name, error = ?e, "forge: applied reseed init failed");
return false;
}
let url = forge_git_url(&format!("{CONFIG_ORG}/{name}"));
let auth = reader.auth_header();
let out = crate::lifecycle::git_command_authed(&auth)
.current_dir(&applied)
.args([
"fetch",
"--no-tags",
"--update-head-ok",
&url,
"refs/heads/main:refs/heads/main",
])
.output()
.await;
match out {
Ok(o) if o.status.success() => {}
Ok(o) => {
tracing::warn!(
%name,
stderr = %String::from_utf8_lossy(&o.stderr).trim(),
"forge: applied reseed fetch failed"
);
return false;
}
Err(e) => {
tracing::warn!(%name, error = ?e, "forge: applied reseed fetch failed");
return false;
}
}
if let Err(e) = crate::lifecycle::git_read_tree_reset(&applied, "refs/heads/main").await {
tracing::warn!(%name, error = ?e, "forge: applied reseed read-tree failed");
return false;
}
if let Err(e) = crate::lifecycle::git_tag(&applied, "deployed/0", "refs/heads/main").await {
tracing::warn!(%name, error = ?e, "forge: applied reseed tag failed");
return false;
}
tracing::info!(%name, "forge: reseeded applied repo from agent-configs main");
true
}
/// Seed an agent's `proposed` repo from `agent-configs/<name>` on the forge.
///
/// When the swarm creates an agent it writes that agent's config to the forge
/// before any hive is told to deploy it, so the hive's job here is to **take**
/// that config rather than author a second one beside it. The local template
/// is the fallback, not the default.
///
/// Best-effort like [`fetch_config_main_into_applied`], and `false` is an
/// ordinary answer rather than an error: there is no [`reader_token`], the
/// forge is unreachable, or `agent-configs/<name>` has no `main` — the last of
/// which is the *normal* case right after `swarm-controller` creates the
/// agent, before it has pushed anything into the repo it just made.
///
/// `clone` rather than `init` + `fetch`: git removes a directory it created
/// when the clone fails, and cloning into an existing empty dir leaves no
/// `.git` behind either — so the caller's "does this repo already exist"
/// check reads exactly the same before and after a failed attempt.
pub async fn clone_config_into_proposed(dir: &Path, name: &str) -> bool {
let Some(reader) = reader_token().await else {
return false;
};
let url = forge_git_url(&format!("{CONFIG_ORG}/{name}"));
// `--branch main` fails outright against a repo with no commits, so an
// empty repo reads as "nothing to take" instead of cloning to an unborn
// HEAD that would then look like a seeded checkout.
let out = crate::lifecycle::git_command_authed(&reader.auth_header())
.args([
"clone",
"--branch",
"main",
&url,
&dir.display().to_string(),
])
.output()
.await;
match out {
Ok(o) if o.status.success() => {
tracing::info!(%name, "forge: seeded proposed repo from agent-configs main");
true
}
Ok(o) => {
// `info`, not `warn`: the normal case right after
// `swarm-controller` creates the agent, before it has pushed
// anything into the repo it just made, and nothing is wrong.
// Logged at all because "where did this agent's config come
// from" is worth answering from the journal, and proposed is
// seeded exactly once.
tracing::info!(
%name,
stderr = %String::from_utf8_lossy(&o.stderr).trim(),
"forge: no config to clone for this agent; seeding the template"
);
false
}
Err(e) => {
tracing::warn!(%name, error = ?e, "forge: proposed clone failed to run");
false
}
}
}