refactor(#2416): remove the non-pr config-change flow (request_apply_commit / applycommit)

This commit is contained in:
damocles 2026-07-15 20:45:29 +02:00 committed by mara
commit c2bd7db998
34 changed files with 293 additions and 1635 deletions

View file

@ -12,7 +12,7 @@ const GIT_EMAIL: &str = "c0re@hyperhive.local";
/// Return the SHA of the root (oldest, no-parent) commit in a repo.
/// Used to seed the applied repo at the template baseline rather than at
/// `main`, so the first `ApplyCommit` diff shows the manager's real changes.
/// `main`, so `deployed/0` records the template, not the manager's first commit.
pub(super) async fn git_root_commit(dir: &Path) -> Result<String> {
let out = git_command()
.current_dir(dir)
@ -71,51 +71,6 @@ pub async fn git(dir: &Path, args: &[&str]) -> Result<()> {
Ok(())
}
/// Fetch the commit `sha` from the `src` git repo into `dst` and pin
/// it as `refs/tags/<tag>`. Used at `request_apply_commit` time so
/// hive-c0re captures an immutable handle on the manager's commit;
/// subsequent amendments / force-pushes in `src` no longer affect
/// what gets built. Returns the resolved full sha.
///
/// `sha` must be a commit sha (short or full) — the caller
/// (`submit_apply_commit`) shape-checks it first. We resolve it
/// LOCALLY against `src` rather than asking the remote to resolve
/// it: `git fetch <remote> <sha>:<dst>` treats the left side as a
/// remote *ref name*, and a bare sha is not one ("couldn't find
/// remote ref ..."). Fetching by sha would need a full 40-hex sha
/// plus `uploadpack.allow*SHA1InWant` on the remote, which the
/// proposed repos don't set. hive-c0re has direct read access to
/// `src`, so a local `rev-parse` + a branch-glob fetch sidesteps
/// the whole sha-want negotiation.
pub async fn git_fetch_to_tag(dst: &Path, src: &Path, sha: &str, tag: &str) -> Result<String> {
let src_str = src.display().to_string();
// Resolve the (short-or-full) sha to a full sha against the
// source repo. The `^{commit}` peel + non-zero exit on a missing
// object means a typo'd / stale sha fails loudly right here.
let full = git_rev_parse(src, &format!("{sha}^{{commit}}"))
.await
.with_context(|| format!("commit '{sha}' not found in proposed repo {src_str}"))?;
// Bring src's objects into dst. Fetching every head pulls the
// wanted commit's history (always reachable from a branch in the
// manager's flow) into dst's object db without sha-want.
git(
dst,
&[
"fetch",
"--no-tags",
&src_str,
"+refs/heads/*:refs/remotes/proposal-src/*",
],
)
.await?;
// Pin the exact commit as the proposal tag. The objects are now
// local so this resolves without touching the remote.
git(dst, &["tag", tag, &full]).await.with_context(|| {
format!("tag {tag} at {full}: commit not reachable from any branch in proposed repo")
})?;
Ok(full)
}
/// Resolve `refname` (a tag, branch, or sha) in `dir` to its full sha.
pub async fn git_rev_parse(dir: &Path, refname: &str) -> Result<String> {
let out = git_command()

View file

@ -7,8 +7,8 @@ mod setup;
mod tests;
pub use git::{
git, git_command, git_fetch_to_tag, git_read_tree_reset, git_rev_parse, git_tag,
git_tag_annotated, git_update_ref,
git, git_command, git_read_tree_reset, git_rev_parse, git_tag, git_tag_annotated,
git_update_ref,
};
pub use host_config::{
CONTAINER_MANAGER_AGENTS_MOUNT, CONTAINER_MANAGER_APPLIED_MOUNT, write_dropins,
@ -345,14 +345,6 @@ async fn agents_after_spawn(name: &str) -> Result<Vec<crate::meta::AgentSpec>> {
agents_for_meta(Some(name)).await
}
/// Like `agents_for_meta_listing` but with an extra agent added (for a
/// container that doesn't exist yet). Used by the first-spawn path in
/// `actions::run_apply_commit` to register the new agent in meta before
/// `prepare_deploy` tries to update its input lock.
pub async fn agents_for_meta_listing_with(extra: &str) -> Result<Vec<crate::meta::AgentSpec>> {
agents_for_meta(Some(extra)).await
}
/// Public enumeration of currently-existing agents (whatever
/// `nixos-container list` says), sorted, no extras. For callers
/// outside this module that need to reseed meta after lifecycle

View file

@ -44,8 +44,7 @@ pub async fn setup_proposed(proposed_dir: &Path, name: &str) -> Result<()> {
// manager's ergonomics. The URL is the path inside the manager
// container (`/applied/<n>/.git`), where the RO bind in
// `set_nspawn_flags` makes it real. hive-c0re itself never
// dereferences this remote; the host-side fetch in
// `request_apply_commit` uses absolute host paths.
// dereferences this remote.
ensure_applied_remote(proposed_dir, name).await
}
@ -71,9 +70,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 proposals are fetched at `request_apply_commit`
/// time and tagged `proposal/<id>` (see `actions::approve` for the
/// tag state machine).
/// agent — subsequent config changes are fetched from the reviewed
/// forge PR head at merge time (see `actions::run_merge_config_pr`).
///
/// `proposed_dir` is `None` on rebuild paths where the repo already
/// exists — we just verify it's the right shape and bail otherwise.
@ -100,10 +98,8 @@ pub async fn setup_applied(
git(applied_dir, &["init", "--initial-branch=main"]).await?;
let proposed_str = proposed.display().to_string();
// Seed the applied repo at the root (template) commit of proposed,
// not at `main`. This ensures `deployed/0` is the template baseline
// so the first ApplyCommit diff shows the manager's real changes
// rather than an empty diff (which happens when the manager has
// already committed their config and proposed/main == proposal/<id>).
// not at `main`, so `deployed/0` is the template baseline rather
// than whatever the manager may have already committed on top.
let root_sha = git_root_commit(proposed).await?;
git(
applied_dir,