From b140ce7bc5e922d50b2060ada786269d93378081 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Mon, 27 Apr 2026 15:29:38 +0000 Subject: [PATCH] Push actual refs to mirror instead of hardcoding main The post-receive hook now parses stdin to find which refs were updated and pushes only those. This handles repos with any default branch name, not just main. Deleted refs (all-zero new sha) are skipped. Assisted-by: GLM-5.1 via pi --- src/bin/quire/commands/hook.rs | 27 +++++++++++++++++++++++++-- src/quire.rs | 11 +++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/bin/quire/commands/hook.rs b/src/bin/quire/commands/hook.rs index dc654e9..1c1cf17 100644 --- a/src/bin/quire/commands/hook.rs +++ b/src/bin/quire/commands/hook.rs @@ -58,8 +58,31 @@ fn post_receive(quire: &Quire) -> Result<()> { .reveal() .context("failed to resolve GitHub token")?; - tracing::info!(url = %mirror.url, "pushing to mirror"); - repo.push_to_mirror(&mirror, token)?; + // Parse pushed refs from stdin. Each line is: + // + // Only push refs that were actually updated (new sha is not all zeros). + let stdin = io::stdin(); + let mut refs: Vec = Vec::new(); + for line in stdin.lines() { + let line = line.map_err(|e| miette!("failed to read hook stdin: {e}"))?; + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() != 3 { + continue; + } + let new_sha = parts[1]; + if new_sha == "0000000000000000000000000000000000000000" { + continue; + } + refs.push(parts[2].to_string()); + } + + if refs.is_empty() { + return Ok(()); + } + + let ref_slices: Vec<&str> = refs.iter().map(|s| s.as_str()).collect(); + tracing::info!(url = %mirror.url, refs = ?ref_slices, "pushing to mirror"); + repo.push_to_mirror(&mirror, token, &ref_slices)?; tracing::info!(url = %mirror.url, "mirror push complete"); Ok(()) } diff --git a/src/quire.rs b/src/quire.rs index 398fe66..563dc9e 100644 --- a/src/quire.rs +++ b/src/quire.rs @@ -93,9 +93,12 @@ impl Repo { /// but it remains visible in `/proc//environ` to anything running /// as the same uid for the lifetime of the push. Acceptable today /// (single-user container, no CI runner yet); revisit when CI lands. - pub fn push_to_mirror(&self, mirror: &MirrorConfig, token: &str) -> crate::Result<()> { + pub fn push_to_mirror(&self, mirror: &MirrorConfig, token: &str, refs: &[&str]) -> crate::Result<()> { + let mut args = vec!["push", "--porcelain", &mirror.url]; + args.extend(refs); + let status = self - .git(&["push", "--porcelain", &mirror.url, "main"]) + .git(&args) .env("GIT_CONFIG_COUNT", "1") .env("GIT_CONFIG_KEY_0", "http.extraHeader") .env( @@ -717,7 +720,7 @@ mod tests { let mirror = MirrorConfig { url: format!("file://{}", target.display()), }; - repo.push_to_mirror(&mirror, "ignored-for-file-url") + repo.push_to_mirror(&mirror, "ignored-for-file-url", &["main"]) .expect("push should succeed"); assert_eq!(rev_parse(&source, "main"), rev_parse(&target, "main")); @@ -735,7 +738,7 @@ mod tests { let mirror = MirrorConfig { url: "file:///nonexistent/quire-test/target.git".to_string(), }; - let err = repo.push_to_mirror(&mirror, "x").unwrap_err(); + let err = repo.push_to_mirror(&mirror, "x", &["main"]).unwrap_err(); assert!( matches!(err, crate::Error::Git(_)), "expected Git error, got {err:?}" -- 2.54.0