config mirror: never force-push — keep forge history on rolled-back deploys
This commit is contained in:
parent
7391900b88
commit
f310b1ce5a
2 changed files with 31 additions and 9 deletions
|
|
@ -57,9 +57,11 @@ Two things live in the `agent-configs` Forgejo organization:
|
|||
`main` is branch-protected core-only: only hive-c0re's verify-and-ff-push
|
||||
merge handler lands on `main`, an operator-team approval is required, and
|
||||
the agent can neither push `main` directly nor self-merge. `main` is
|
||||
fast-forward-only — no auto force-push (the merge handler's ff push lands
|
||||
fine; the legacy `push_config` mirror, which force-pushes to re-point status
|
||||
tags and rewind on rollback, runs best-effort until the PR flow retires it).
|
||||
fast-forward-only — hive-c0re never force-pushes (the merge handler's ff
|
||||
push lands fine; the `push_config` mirror pushes `main` + the add-only
|
||||
status tags without force, and treats a non-fast-forward rejection of
|
||||
`main` after a rolled-back deploy as expected — the forge keeps the
|
||||
approved history, the `failed/<id>` tag records the divergence).
|
||||
Repos stay private, so an agent can't read another
|
||||
agent's config. (Agents remain read-only collaborators on `core/meta`.)
|
||||
- The dashboard links each container's "config" anchor to this
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ const CORE_TOKEN_PATH: &str = "/var/lib/hyperhive/forge-core-token";
|
|||
/// branch-protected core-only, so only hive-c0re's verify-and-ff-push merge
|
||||
/// handler lands on it (operator approval required; the agent can't push
|
||||
/// `main` or self-merge). The repos remain private, so an agent still can't
|
||||
/// reach *another* agent's config. `main` is fast-forward-only (no auto
|
||||
/// force-push); the legacy `push_config` mirror runs best-effort until the
|
||||
/// reach *another* agent's config. `main` is fast-forward-only — hive-c0re
|
||||
/// never force-pushes; the `push_config` mirror runs best-effort until the
|
||||
/// PR-merge flow retires it.
|
||||
const CONFIG_ORG: &str = "agent-configs";
|
||||
/// Forgejo org hosting the operator-curated shared docs/skills repo
|
||||
|
|
@ -855,8 +855,20 @@ pub async fn ensure_meta_remote(name: &str) -> Result<()> {
|
|||
/// forge isn't seeded or the applied repo doesn't exist yet.
|
||||
///
|
||||
/// Call this after every hive-c0re mutation of an applied repo's refs
|
||||
/// so the forge copy always reflects what core actually did. `--force`
|
||||
/// because a failed build rolls `main` backwards to the last-good sha.
|
||||
/// so the forge copy always reflects what core actually did.
|
||||
///
|
||||
/// Never force-pushes. The status tags are id-suffixed
|
||||
/// (`proposal/<id>`, `deployed/<id>`, …) and therefore add-only, and
|
||||
/// `main` is published history — after a failed deploy rolls the LOCAL
|
||||
/// applied `main` back to last-good, the forge `main` may legitimately
|
||||
/// be ahead (e.g. an operator-merged config PR whose rebuild failed).
|
||||
/// Rewinding it would erase that merged commit from the forge, which
|
||||
/// is exactly the incident this guards against: the local repo tracks
|
||||
/// "what last built", the forge tracks "what was approved", and the
|
||||
/// `failed/<id>` tag records the divergence. A non-fast-forward
|
||||
/// rejection of `main` is therefore expected + logged at info; the
|
||||
/// tags in the same push still land (git pushes refspecs
|
||||
/// independently). Any other failure is a real error.
|
||||
///
|
||||
/// The tokenised URL is passed straight to `git push` and deliberately
|
||||
/// never stored as a named remote: the applied repo is bind-mounted
|
||||
|
|
@ -875,7 +887,6 @@ pub async fn push_config(name: &str) -> Result<()> {
|
|||
.current_dir(&dir)
|
||||
.args([
|
||||
"push",
|
||||
"--force",
|
||||
&url,
|
||||
"refs/heads/main:refs/heads/main",
|
||||
"refs/tags/*:refs/tags/*",
|
||||
|
|
@ -884,10 +895,19 @@ pub async fn push_config(name: &str) -> Result<()> {
|
|||
.await
|
||||
.context("invoke git push agent-configs")?;
|
||||
if !out.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
if stderr.contains("non-fast-forward") {
|
||||
tracing::info!(
|
||||
%name,
|
||||
"forge: mirror push of main rejected (non-fast-forward) — forge main is \
|
||||
ahead of local applied main (rolled-back deploy); leaving forge history intact"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
anyhow::bail!(
|
||||
"git push {CONFIG_ORG}/{name} failed ({}): {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr).trim()
|
||||
stderr.trim()
|
||||
);
|
||||
}
|
||||
tracing::info!(%name, "forge: mirrored applied config to agent-configs");
|
||||
|
|
|
|||
Loading…
Reference in a new issue