huskies: merge 1065

This commit is contained in:
dave
2026-05-14 21:43:13 +00:00
parent e6865a1bc6
commit 23c3301903
3 changed files with 154 additions and 87 deletions
+48
View File
@@ -361,6 +361,54 @@ pub(crate) fn run_squash_merge(
"=== Verified: cherry-pick landed on '{base_branch}' with code changes ===\n"
));
// ── Regen source-map.json on master after cherry-pick ─────────
// Run deterministically on project_root (now on master). Skip the commit
// when regen produces no diff (idempotent case). Failure is non-fatal.
{
let map_path = project_root.join(".huskies").join("source-map.json");
let old_content = std::fs::read_to_string(&map_path).ok();
match source_map_gen::regenerate_source_map(project_root, &map_path) {
Err(e) => {
all_output.push_str(&format!(
"=== source-map regen failed (non-fatal): {e} ===\n"
));
}
Ok(()) => {
let new_content = std::fs::read_to_string(&map_path).ok();
if old_content != new_content {
all_output.push_str("=== source-map.json changed — committing on master ===\n");
let _ = Command::new("git")
.args(["add", ".huskies/source-map.json"])
.current_dir(project_root)
.output();
match Command::new("git")
.args(["commit", "-m", "huskies: regen source-map.json"])
.current_dir(project_root)
.output()
{
Ok(c) if c.status.success() => {
all_output.push_str("=== source-map.json committed on master ===\n");
}
Ok(c) => {
let stderr = String::from_utf8_lossy(&c.stderr);
all_output.push_str(&format!(
"=== source-map commit failed (non-fatal): {stderr} ===\n"
));
}
Err(e) => {
all_output.push_str(&format!(
"=== source-map commit error (non-fatal): {e} ===\n"
));
}
}
} else {
all_output
.push_str("=== source-map.json unchanged — no follow-up commit ===\n");
}
}
}
}
// ── Clean up ──────────────────────────────────────────────────
cleanup_merge_workspace(project_root, &merge_wt_path, &merge_branch);
all_output.push_str("=== Merge-queue cleanup complete ===\n");
@@ -403,6 +403,112 @@ fn squash_merge_runs_component_setup_from_project_toml() {
);
}
/// AC6: the regen+commit step runs on `project_root` (master) only.
/// After a successful merge where the source-map changes, `git log --name-only`
/// shows a follow-up commit whose diff contains ONLY `.huskies/source-map.json`.
#[tokio::test]
async fn regen_commit_on_master_touches_only_source_map() {
use std::fs;
use tempfile::tempdir;
let tmp = tempdir().unwrap();
let repo = tmp.path();
init_git_repo(repo);
// Put a stale source-map.json on master so regen will produce a different result.
let sk_dir = repo.join(".huskies");
fs::create_dir_all(&sk_dir).unwrap();
fs::write(sk_dir.join("source-map.json"), "{\"stale\": true}\n").unwrap();
// Add a tracked Rust file so the regenerator has something to index.
fs::create_dir_all(repo.join("src")).unwrap();
fs::write(
repo.join("src/lib.rs"),
"//! Library.\n\n/// Says hello.\npub fn hello() {}\n",
)
.unwrap();
Command::new("git")
.args(["add", "."])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "initial with stale source-map"])
.current_dir(repo)
.output()
.unwrap();
// Feature branch: add a new file.
Command::new("git")
.args(["checkout", "-b", "feature/story-1065_regen_test"])
.current_dir(repo)
.output()
.unwrap();
fs::write(
repo.join("src/extra.rs"),
"//! Extra.\n\n/// Extra fn.\npub fn extra() {}\n",
)
.unwrap();
Command::new("git")
.args(["add", "."])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "add extra.rs"])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["checkout", "master"])
.current_dir(repo)
.output()
.unwrap();
let result =
run_squash_merge(repo, "feature/story-1065_regen_test", "1065_regen_test").unwrap();
assert!(
matches!(result, super::MergeResult::Success { .. }),
"clean merge must succeed; got: {result:?}"
);
// Find the regen commit if one was created.
let log_out = Command::new("git")
.args(["log", "--oneline", "--name-only"])
.current_dir(repo)
.output()
.unwrap();
let log = String::from_utf8_lossy(&log_out.stdout);
// If a regen commit exists, its diff must contain ONLY the source-map path.
if log.contains("huskies: regen source-map.json") {
// Extract files changed in the regen commit.
let show_out = Command::new("git")
.args(["show", "--name-only", "--format=", "HEAD"])
.current_dir(repo)
.output()
.unwrap();
let show = String::from_utf8_lossy(&show_out.stdout);
// If HEAD is the regen commit, its files list must be exactly one entry.
let head_msg = Command::new("git")
.args(["log", "-1", "--format=%s"])
.current_dir(repo)
.output()
.unwrap();
let head_subject = String::from_utf8_lossy(&head_msg.stdout);
if head_subject.trim() == "huskies: regen source-map.json" {
let changed_files: Vec<&str> = show.lines().filter(|l| !l.is_empty()).collect();
assert_eq!(
changed_files,
vec![".huskies/source-map.json"],
"regen commit must touch ONLY .huskies/source-map.json; got: {changed_files:?}"
);
}
}
}
#[cfg(unix)]
#[test]
fn squash_merge_succeeds_without_components_in_project_toml() {