Compare commits
3 changed files with 1 additions and 100 deletions
|
|
@ -18,8 +18,7 @@ pub use pr_merge::{
|
||||||
pub use reconcile::{reconcile_config_apply, reconcile_config_status};
|
pub use reconcile::{reconcile_config_apply, reconcile_config_status};
|
||||||
pub use repos::{
|
pub use repos::{
|
||||||
create_agent_repo, ensure_config_repo, ensure_knowledge_repo, ensure_meta_remote, ensure_repo,
|
create_agent_repo, ensure_config_repo, ensure_knowledge_repo, ensure_meta_remote, ensure_repo,
|
||||||
ensure_shared_docs_repo, fetch_config_main_into_applied, meta_read_access, push_config,
|
ensure_shared_docs_repo, meta_read_access, push_config, push_meta, shared_docs_access,
|
||||||
push_meta, shared_docs_access,
|
|
||||||
};
|
};
|
||||||
pub use users::{core_token, ensure_user_for, provision_user_token};
|
pub use users::{core_token, ensure_user_for, provision_user_token};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -519,80 +519,6 @@ pub async fn push_config(name: &str) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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, [`push_config`]'s mirror target. Unlike
|
|
||||||
/// `proposed` (seeded once at first spawn and never touched again — see
|
|
||||||
/// `lifecycle::setup_proposed`'s own doc comment), the forge mirror is
|
|
||||||
/// kept current after every deploy, so this reflects the agent's actual
|
|
||||||
/// last-deployed state rather than a stale creation-time snapshot.
|
|
||||||
///
|
|
||||||
/// Best-effort, mirroring [`push_config`]'s own shape: returns `false`
|
|
||||||
/// (not an error) rather than bailing when the forge is absent, the core
|
|
||||||
/// token isn't minted yet, `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 {
|
|
||||||
if !is_present().await {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let Some(token) = core_token() 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 = core_auth_header(&token);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run a single `git push <url> <refspec>` in the applied repo `dir` and
|
/// Run a single `git push <url> <refspec>` in the applied repo `dir` and
|
||||||
/// return the raw output for the caller to classify. Split out so
|
/// return the raw output for the caller to classify. Split out so
|
||||||
/// [`push_config`] can push tags and `main` as independent pushes.
|
/// [`push_config`] can push tags and `main` as independent pushes.
|
||||||
|
|
|
||||||
|
|
@ -250,20 +250,6 @@ pub async fn create_container(name: &str, hive: &HiveEnv, paths: &AgentPaths) ->
|
||||||
/// Rebuild-path preamble, run by the job queue's `Prebuild` node: fail fast on
|
/// Rebuild-path preamble, run by the job queue's `Prebuild` node: fail fast on
|
||||||
/// a port collision, then make sure the applied repo + state dirs exist.
|
/// a port collision, then make sure the applied repo + state dirs exist.
|
||||||
/// Container untouched.
|
/// Container untouched.
|
||||||
///
|
|
||||||
/// `applied` is normally already the right shape here (this runs against a
|
|
||||||
/// previously-provisioned agent), but a `destroy --purge` removes it
|
|
||||||
/// (`job_queue::exec::run_purge_state`) — a later revive of the *same*
|
|
||||||
/// agent name is still a rebuild, not a first-spawn, so it lands here, not
|
|
||||||
/// in `provision_container`. Recover from `agent-configs/<name>` on the
|
|
||||||
/// forge when that's the case (kept current after every deploy by
|
|
||||||
/// `forge::push_config` — see `forge::fetch_config_main_into_applied`'s own
|
|
||||||
/// doc comment) rather than bailing outright. Deliberately **no fallback to
|
|
||||||
/// `proposed`**: that repo is seeded once at first spawn and never touched
|
|
||||||
/// again (`setup_proposed`'s own doc comment), so it can be arbitrarily
|
|
||||||
/// stale for a long-lived agent — restoring from it would silently revive
|
|
||||||
/// the *wrong* config rather than recovering the right one, which is worse
|
|
||||||
/// than the clear bail this replaces.
|
|
||||||
pub async fn prepare_rebuild_dirs(name: &str, paths: &AgentPaths) -> Result<()> {
|
pub async fn prepare_rebuild_dirs(name: &str, paths: &AgentPaths) -> Result<()> {
|
||||||
validate(name)?;
|
validate(name)?;
|
||||||
if let Some(other) = port_collision(name).await {
|
if let Some(other) = port_collision(name).await {
|
||||||
|
|
@ -272,16 +258,6 @@ pub async fn prepare_rebuild_dirs(name: &str, paths: &AgentPaths) -> Result<()>
|
||||||
agent_web_port(name)
|
agent_web_port(name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !paths.applied.join(".git").exists()
|
|
||||||
&& !crate::forge::fetch_config_main_into_applied(name).await
|
|
||||||
{
|
|
||||||
bail!(
|
|
||||||
"applied repo at {} is missing its .git directory and could not be reseeded from \
|
|
||||||
the forge (agent-configs/{name} unreachable, not yet mirrored, or absent); \
|
|
||||||
destroy --purge and re-spawn this agent.",
|
|
||||||
paths.applied.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setup_applied(&paths.applied, None, name).await?;
|
setup_applied(&paths.applied, None, name).await?;
|
||||||
ensure_agent_state_subvolume(name).await?;
|
ensure_agent_state_subvolume(name).await?;
|
||||||
ensure_claude_dir(&paths.claude)?;
|
ensure_claude_dir(&paths.claude)?;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue