fix(#2393): ff-merge config prs via forge api instead of pushing main

This commit is contained in:
damocles 2026-07-13 11:18:43 +02:00
commit 6e0f6893cf
4 changed files with 135 additions and 197 deletions

View file

@ -290,33 +290,27 @@ async fn run_merge_config_pr(
Err(e) => return (Err(anyhow::anyhow!("read applied/main: {e:#}")), None),
};
// 4. THE merge: fast-forward the forge repo's main to the reviewed head.
coord.set_queue_step(queue_entry_id, "fast-forward forge main");
match crate::forge::ff_push_to_main(&repo, &reviewed).await {
// 4. THE merge: fast-forward-only merge the reviewed head to `main` via the
// forge API, pinned to the reviewed sha (`head_commit_id`). This one call
// both advances `main` to the reviewed head and marks the PR merged — no
// direct push to the protected branch. Unlike the old push-then-mark split,
// a failure here means `main` was NOT advanced, so it's fatal: we must not
// deploy a head the forge didn't merge.
coord.set_queue_step(queue_entry_id, "fast-forward-merge PR");
match crate::forge::merge_config_pr_ff(&repo, pr, &reviewed).await {
Ok(()) => {}
Err(crate::forge::ForgeMergeError::NotFastForward { .. }) => {
Err(crate::forge::ForgeMergeError::HeadDrift { expected, actual }) => {
return (
Err(anyhow::anyhow!(
"PR #{pr}: forge main raced ahead of reviewed {reviewed}; re-review before merging"
"PR #{pr} head drifted before merge (reviewed {expected}, now {actual}); re-review before merging"
)),
None,
);
}
Err(e) => return (Err(anyhow::anyhow!("ff-push PR #{pr} to main: {e}")), None),
Err(e) => return (Err(anyhow::anyhow!("ff-merge PR #{pr}: {e}")), None),
}
// 5. Mark the PR merged. Best-effort: main is already at the reviewed
// head, so the deploy is correct regardless — a failure here (incl.
// HeadDrift vs the forge PR record) is logged, not fatal.
coord.set_queue_step(queue_entry_id, "mark PR merged");
if let Err(e) = crate::forge::mark_pr_merged(&repo, pr, &reviewed).await {
tracing::warn!(
agent = %approval.agent, %id, %pr, error = ?e,
"mark PR merged failed; main already at reviewed head, continuing to deploy"
);
}
// 6. Shared deploy tail. target == finalize == the reviewed head;
// 5. Shared deploy tail. target == finalize == the reviewed head;
// never a first spawn (the agent already exists).
deploy_applied_target(
coord,