Watch
0
0
Fork
You've already forked hyperhive
0

config PRs: remove the hive's config-PR webhook, poll and core merge

An operator's merge on the forge deploys a config PR through
swarm-controller's DeployRequest{rev}. The hive-side path that queued a
MergeConfigPr approval and merged the PR as `core` goes:

- the `/webhook/config-pr` receiver, its HMAC secret, the WebhookRegister
  boot node and the org-hook registration; the hive vhost's `/webhook/`
  location
- the 5-minute config-PR poll
- ApprovalKind::MergeConfigPr, its dashboard card, and the deploy DAG it
  drove (DeployWindow, MergeVerify, DeployApply, FinalizeDeploy,
  DeployTail), with verify_commit, the two-phase meta deploy, rollback
  refs, the PR-failure comment and forge/pr_merge.rs
- `fetched_sha`, `sha_short`/`pr_number` on approval events, and
  `sha`/`tag` on HelperEvent::ApprovalResolved: only the merge path set
  them

`config_repo`, `merged_pr_for_commit` and `post_pr_comment` move to
forge/pr_comment.rs for the merged-rev deploy's refusal comment.
Approvals v5 drops stored `merge_config_pr` rows; a test reopens a v4
database holding them.

Closes #4850
This commit is contained in:
atlas 2026-10-02 22:32:17 +02:00
commit efbfec6d01
39 changed files with 286 additions and 3199 deletions

View file

@ -135,55 +135,6 @@ pub async fn git_tag(dir: &Path, name: &str, target: &str) -> Result<()> {
git(dir, &["tag", name, target]).await
}
/// Plant an annotated tag with `body` as the message. Used for
/// `failed/<id>` (body = build error) and `denied/<id>` (body =
/// operator note). Multi-line bodies handled via stdin so we don't
/// have to escape anything.
pub async fn git_tag_annotated(dir: &Path, name: &str, target: &str, body: &str) -> Result<()> {
use tokio::io::AsyncWriteExt;
// Annotated tags are git objects, so they need a tagger identity
// (same constraint as a commit). Pass the hive-c0re identity
// inline rather than relying on a global git config — applied
// repos are hive-c0re-owned and the host's user might not have
// user.email set.
let mut child = git_command()
.current_dir(dir)
.args([
"-c",
&format!("user.name={GIT_NAME}"),
"-c",
&format!("user.email={GIT_EMAIL}"),
"tag",
"-a",
name,
target,
"-F",
"-",
])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.with_context(|| format!("spawn git tag -a {name} in {}", dir.display()))?;
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(body.as_bytes())
.await
.context("write tag body to git stdin")?;
// Drop closes stdin so git can finish reading.
drop(stdin);
}
let out = child.wait_with_output().await.context("wait git tag -a")?;
if !out.status.success() {
bail!(
"git tag -a {name} failed ({}): {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
);
}
Ok(())
}
/// Replace working tree + index with the tree at `target` without
/// moving HEAD. `applied/main` stays pointing at the last known-good
/// `deployed/*` while we let `nixos-container update` evaluate the
@ -252,13 +203,3 @@ pub async fn git_is_ancestor(dir: &Path, ancestor: &str, descendant: &str) -> Re
),
}
}
/// Delete a ref. The counterpart to [`git_update_ref`] for the bookkeeping
/// refs a deploy parks in the applied repo (`refs/hyperhive/rollback/<id>`,
/// which records the pre-merge `main` so the deploy tail can compensate a
/// merge that landed but never finalized). `update-ref -d` is a no-op-free
/// delete: it errors if the ref does not exist, so callers that treat absence
/// as "nothing to undo" should check with [`git_rev_parse`] first.
pub async fn git_delete_ref(dir: &Path, refname: &str) -> Result<()> {
git(dir, &["update-ref", "-d", refname]).await
}

View file

@ -8,9 +8,8 @@ mod setup;
mod tests;
pub use git::{
git, git_authed, git_command, git_command_authed, git_delete_ref, git_is_ancestor,
git_read_tree_reset, git_rev_parse, git_tag, git_tag_annotated, git_update_ref,
git_update_ref_cas,
git, git_authed, git_command, git_command_authed, git_is_ancestor, git_read_tree_reset,
git_rev_parse, git_tag, git_update_ref, git_update_ref_cas,
};
pub use host_config::write_dropins;
pub use setup::{

View file

@ -98,8 +98,8 @@ async fn ensure_applied_remote(proposed_dir: &Path, name: &str) -> Result<()> {
/// Set up the applied repo. First-spawn only: init the repo, pull
/// proposed's initial commit in via `git fetch`, tag it `deployed/0`.
/// This is the *only* time hive-c0re reads from `proposed` for an
/// agent — subsequent config changes are fetched from the reviewed
/// forge PR head at merge time (see `actions::run_deploy_apply`).
/// agent — subsequent config changes are fetched from the forge's `main`
/// (see `actions::advance_applied_to_rev`).
///
/// `proposed_dir` is `None` on rebuild paths where the repo already
/// exists — we just verify it's the right shape and bail otherwise.