use super::*;
fn git(dir: &Path, args: &[&str]) {
let output = crate::git_backend::git_command().args(args).current_dir(dir).output().expect("git runs");
assert!(output.status.success(), "git {args:?} in {}: {}", dir.display(), String::from_utf8_lossy(&output.stderr));
}
fn fleet() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("a scratch directory");
let root = dir.path();
for name in ["a", "b"] {
let repo = root.join(name);
std::fs::create_dir_all(repo.join(".wsb")).unwrap();
git(&repo, &["init", "--quiet", "-b", "main"]);
std::fs::write(repo.join("file.txt"), name).unwrap();
std::fs::write(
repo.join(".wsb").join("repo.json"),
format!(
r#"{{"name":"{name}","alias":"{name}","templates_root":"..","version":{{"major":1,"file":"version.txt","detect_manifests":true}}}}"#
),
)
.unwrap();
git(&repo, &["add", "file.txt", ".wsb/repo.json"]);
git(&repo, &["-c", "user.name=t", "-c", "user.email=t@t", "commit", "--quiet", "-m", name]);
}
git(root, &["init", "--quiet", "-b", "main"]);
std::fs::write(
root.join(".gitmodules"),
"[submodule \"a\"]\n\tpath = a\n\turl = ./a\n[submodule \"b\"]\n\tpath = b\n\turl = ./b\n",
)
.unwrap();
dir
}
fn order(root: &Path) -> std::result::Result<Vec<String>, String> {
let tree = SubmoduleTree::discover(root).map_err(|e| e.to_string())?;
commit_order(&tree, root)
.map(|nodes| nodes.iter().map(|node| node.label().to_string()).collect())
.map_err(|e| e.to_string())
}
#[test]
fn test12578_a_repository_commits_after_those_it_reads() {
let fleet = fleet();
let root = fleet.path();
let a_reads_b = root.join("a").join("pin.txt.wstemplate");
let b_reads_a = root.join("b").join("pin.txt.wstemplate");
std::fs::write(&a_reads_b, "{{ projects.b.version }}\n").unwrap();
assert_eq!(order(root).unwrap(), vec!["b", "a"], "a was committed before the b it reads");
std::fs::remove_file(&a_reads_b).unwrap();
std::fs::write(&b_reads_a, "{{ projects.a.version }}\n").unwrap();
assert_eq!(order(root).unwrap(), vec!["a", "b"], "b was committed before the a it reads");
std::fs::write(&a_reads_b, "{{ projects.b.version }}\n").unwrap();
let refused = order(root).unwrap_err();
assert!(refused.contains("a") && refused.contains("b") && refused.contains("no order"), "{refused}");
}
fn head(dir: &Path) -> String {
let output = crate::git_backend::git_command().args(["rev-parse", "HEAD"]).current_dir(dir).output().expect("git runs");
String::from_utf8_lossy(&output.stdout).trim().to_string()
}
#[test]
fn test12583_a_clean_superproject_is_not_committed() {
let fleet = fleet();
let root = fleet.path();
git(root, &["config", "user.name", "t"]);
git(root, &["config", "user.email", "t@t"]);
git(root, &["add", ".gitmodules", "a", "b"]);
git(root, &["commit", "--quiet", "-m", "fleet"]);
let hook = root.join(".git").join("hooks").join("pre-commit");
std::fs::create_dir_all(hook.parent().unwrap()).unwrap();
std::fs::write(&hook, "#!/bin/sh\ndate +%s%N > version.txt\ngit add version.txt\n").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&hook, std::fs::Permissions::from_mode(0o755)).unwrap();
}
let before = head(root);
execute(root, "nothing", false, true, &[], &[], false, false, false).expect("the walk runs");
assert_eq!(head(root), before, "a clean superproject was committed");
assert!(!root.join("version.txt").exists(), "the hook ran for a commit that had nothing to record");
std::fs::write(root.join("notes.txt"), "an edit at the root\n").unwrap();
execute(root, "an edit", false, true, &[], &[], false, false, false).expect("the walk runs");
assert_ne!(head(root), before, "the superproject's own edit was not committed");
}