fix(#2380): push_meta drop --force + dashboard warning for non-fast-forward
If the remote is ahead of our local mirror (non-fast-forward), the old code used --force which silently destroyed remote history. Fix: - Drop --force from the git push invocation. - On non-ff exit, detect the condition and return Ok(()) instead of bailing (intentional no-op; leaving remote history intact is correct). - Raise a persistent dashboard warning banner via crate::warnings so the operator sees it in the UI rather than having to grep the journal. - Clear the banner on the next successful push. Closes #2380.
This commit is contained in:
parent
46007008a9
commit
c222418d76
1 changed files with 37 additions and 6 deletions
|
|
@ -5,6 +5,7 @@
|
|||
//! constructor + org-name constants live in the module root (`super`).
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use forgejo_api::structs::{
|
||||
|
|
@ -188,6 +189,11 @@ async fn ensure_org_repo(org: &str, name: &str, token: &str) -> Result<()> {
|
|||
created_or_exists(res, &format!("{org}/{name}"))
|
||||
}
|
||||
|
||||
/// Dashboard-warning guard for the meta non-fast-forward condition.
|
||||
/// Held while `core/meta` remote is ahead of our local mirror; cleared
|
||||
/// automatically when the next push succeeds.
|
||||
static META_NON_FF_GUARD: Mutex<Option<crate::warnings::WarningGuard>> = Mutex::new(None);
|
||||
|
||||
/// Push `dir` (the meta repo) to `core/meta` on the local forge.
|
||||
/// Best-effort: returns Err which callers log + ignore. No-op when
|
||||
/// the core token isn't present yet (forge container not provisioned).
|
||||
|
|
@ -198,20 +204,47 @@ pub async fn push_meta(dir: &Path) -> Result<()> {
|
|||
// Token-in-URL push. Forgejo accepts `oauth2:<token>` or just
|
||||
// any-username:<token>; using `core` matches the owner so the
|
||||
// remote name is self-describing.
|
||||
//
|
||||
// No --force: `meta.rs` uses regular `git commit` (append-only),
|
||||
// so the push is always fast-forward in normal operation. Per
|
||||
// operator directive, hive-c0re must not force-push anywhere. If
|
||||
// the remote is somehow ahead (e.g. split-brain or manual push) we
|
||||
// raise a dashboard warning and leave the remote intact rather than
|
||||
// silently erasing its history — consistent with `push_config`'s
|
||||
// non-fast-forward handling.
|
||||
let url = forge_git_url(&token, "core/meta");
|
||||
let out = Command::new("git")
|
||||
.current_dir(dir)
|
||||
.args(["push", "--force", &url, "HEAD:main"])
|
||||
.args(["push", &url, "HEAD:main"])
|
||||
.output()
|
||||
.await
|
||||
.context("invoke git push core/meta")?;
|
||||
if !out.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
if stderr.contains("non-fast-forward") || stderr.contains("fetch first") {
|
||||
let msg = "forge/core/meta push rejected (non-fast-forward): \
|
||||
remote is ahead of local; leaving remote history intact";
|
||||
tracing::warn!("{msg}");
|
||||
// Raise a persistent dashboard banner; cleared on next successful push.
|
||||
if let Ok(mut g) = META_NON_FF_GUARD.lock() {
|
||||
*g = Some(crate::warnings::set_warning(
|
||||
"forge-meta-non-ff",
|
||||
"warn",
|
||||
msg,
|
||||
));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
anyhow::bail!(
|
||||
"git push core/meta failed ({}): {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr).trim()
|
||||
stderr.trim()
|
||||
);
|
||||
}
|
||||
// Successful push: clear any outstanding non-ff warning.
|
||||
if let Ok(mut g) = META_NON_FF_GUARD.lock() {
|
||||
*g = None;
|
||||
}
|
||||
tracing::info!("forge: pushed meta to core/meta");
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -770,10 +803,8 @@ async fn apply_operator_branch_protection(repo: &str, token: &str) -> Result<()>
|
|||
/// the team) cannot self-approve.
|
||||
/// - **force-pushing `main` stays impossible** — `main` only ever advances by
|
||||
/// fast-forward. The merge handler's `ff_push_to_main` is already a
|
||||
/// non-force push, so it lands fine. The legacy `push_config` mirror DOES
|
||||
/// force-push (it re-points status tags and rewinds `main` on a failed-build
|
||||
/// rollback), so the protection rejects those non-ff updates — that
|
||||
/// mirror runs best-effort until the agent-opened PR-merge flow retires it.
|
||||
/// non-force push, so it lands fine. The legacy `push_config` mirror is
|
||||
/// also ff-only (non-fast-forward is caught and silently skipped).
|
||||
/// (Auto force-push is intentionally not allowed: per operator directive a
|
||||
/// silent force-push is a bug, not a feature. The raw-HTTP predecessor
|
||||
/// sent `"enable_force_push":false` + `"allow_manual_merge":true` in this
|
||||
|
|
|
|||
Loading…
Reference in a new issue