config PRs: an operator's Forgejo merge deploys the merged rev
A config PR merged in the Forgejo UI changed nothing on the hive: the hive's webhook ignores `closed`, its poll then cancels the dashboard card, and `applied/main` stays where it was. swarm-controller reads `merged`/`merge_commit_sha` off the `pull_request` delivery it already receives for `agent-configs`, finds the hive placing the agent by scanning every hive's wanted state (the scan `declarations_elsewhere` already ran, factored out), and queues a `TriggerDeploy` carrying the rev. Zero or several claimants deploy nothing and log the claimants. `DeployRequest` gains `rev: Option<String>` with `serde(default)`, so rev-less payloads from either side keep decoding. hive-c0re, given a rev for an agent it runs: a no-op when `applied/main` already is the rev (a dashboard merge deploys its own PR); otherwise it fetches the forge `main` with the core token, requires the rev to descend from `applied/main` (the ancestry gate, factored out of `run_deploy_merge_verify`), fast-forwards by CAS and queues the usual relocking rebuild. No eval-verify on this path, per mara (#4850 c90075). A refusal is commented on the PR that merged the rev, found by commit. swarm-controller's forge-objects pass converges every config repo's `main` rule to merge whitelist `operators` + `core` and approval whitelist `operators`. The hive's boot PATCH stops forcing `enable_approvals_whitelist` off, so the two do not fight. Refs #4850
This commit is contained in:
parent
9224c0bd15
commit
f1c695c212
11 changed files with 732 additions and 76 deletions
|
|
@ -144,14 +144,14 @@ pub fn spawn(
|
|||
/// Listen on the swarm's event subjects and act on what arrives.
|
||||
///
|
||||
/// The controller decides *what a forge delivery means* and addresses the
|
||||
/// result here; this end does not know a forge exists. Two events today:
|
||||
/// result here; this end reads no forge delivery. Two events today:
|
||||
/// **the knowledge repository changed**, answered by the pull this daemon
|
||||
/// already runs at boot, and **deploy this agent**, answered by the same
|
||||
/// rebuild insert the operator's own verb makes.
|
||||
///
|
||||
/// Only the deploy event carries a payload, and only the agent name: its
|
||||
/// subject already names the hive, so it listens on its own rather than a
|
||||
/// swarm-wide feed. Even then it is a trigger, never the config git owns.
|
||||
/// Only the deploy event carries a payload: the agent name, and the config
|
||||
/// commit to deploy when the swarm names one. Its subject already names the
|
||||
/// hive, so it listens on its own rather than a swarm-wide feed.
|
||||
///
|
||||
/// # A missed message costs the two events very differently
|
||||
///
|
||||
|
|
@ -264,7 +264,7 @@ async fn handle_deploy_request(
|
|||
return;
|
||||
}
|
||||
};
|
||||
let agent = request.agent;
|
||||
let swarm_queue_client::DeployRequest { agent, rev } = request;
|
||||
|
||||
// Which of the two meanings this request has is decided here, and the
|
||||
// predicate is "does a container exist", not "is one running":
|
||||
|
|
@ -283,6 +283,15 @@ async fn handle_deploy_request(
|
|||
}
|
||||
};
|
||||
|
||||
// Only a rebuild applies `rev`. A first deploy ignores it and builds the
|
||||
// `applied` repo it seeds from the forge's `main`, or the one it finds.
|
||||
if known
|
||||
&& let Some(rev) = rev.as_deref()
|
||||
&& !apply_rev(&agent, rev).await
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let inserted = if known {
|
||||
// The same insert the operator's own `rebuild` verb makes, relock and
|
||||
// all: "deploy this agent" means here exactly what it already meant, and
|
||||
|
|
@ -303,6 +312,29 @@ async fn handle_deploy_request(
|
|||
}
|
||||
}
|
||||
|
||||
/// Move `agent`'s `applied/main` to `rev` ahead of its rebuild, and say
|
||||
/// whether that rebuild should be queued. A `rev` already applied is taken as
|
||||
/// deployed or deploying — a dashboard merge deploys the PR it merges — so
|
||||
/// no second rebuild is queued. That also skips a rev put in place by
|
||||
/// `hivectl forge reconcile-config`, which does not deploy.
|
||||
///
|
||||
/// A refusal is commented on the PR that merged `rev`. A failed rebuild is
|
||||
/// not: it surfaces where every rebuild failure does, on this hive.
|
||||
async fn apply_rev(agent: &str, rev: &str) -> bool {
|
||||
match crate::actions::advance_applied_to_rev(agent, rev).await {
|
||||
Ok(crate::actions::RevAdvance::Advanced) => true,
|
||||
Ok(crate::actions::RevAdvance::AlreadyApplied) => {
|
||||
tracing::info!(%agent, %rev, "swarm events: deploy requested for the applied rev; nothing queued");
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(%agent, %rev, error = %format!("{e:#}"), "swarm events: rev not applied; nothing queued");
|
||||
crate::actions::post_rev_deploy_failure_to_pr(agent, rev, &e).await;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue the first deploy of an agent this hive does not have yet: seed its
|
||||
/// power intent, then insert the DAG.
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in a new issue