feat(cli): scaffolds make the initial commit — provenance resolves from run one
aura new and aura nodes new follow their git init with a best-effort initial commit (inline committer identity, warning-never-fatal, skipped whenever the init itself was skipped per #204), so a fresh project's first run stamps a real commit instead of an empty provenance block. closes #243
This commit is contained in:
@@ -283,7 +283,9 @@ pub fn scaffold_project(spec: &ProjectScaffoldSpec) -> Result<(), String> {
|
||||
return Ok(());
|
||||
}
|
||||
match std::process::Command::new("git").arg("init").current_dir(&spec.target_dir).output() {
|
||||
Ok(o) if o.status.success() => {}
|
||||
Ok(o) if o.status.success() => {
|
||||
commit_all(&spec.target_dir, "aura new scaffold");
|
||||
}
|
||||
_ => eprintln!("aura: warning: git init failed (project created without a repo)"),
|
||||
}
|
||||
Ok(())
|
||||
@@ -322,7 +324,9 @@ pub fn scaffold_node_crate(spec: &ScaffoldSpec) -> Result<(), String> {
|
||||
return Ok(());
|
||||
}
|
||||
match std::process::Command::new("git").arg("init").current_dir(&spec.target_dir).output() {
|
||||
Ok(o) if o.status.success() => {}
|
||||
Ok(o) if o.status.success() => {
|
||||
commit_all(&spec.target_dir, "aura nodes new scaffold");
|
||||
}
|
||||
_ => eprintln!("aura: warning: git init failed (node crate created without a repo)"),
|
||||
}
|
||||
Ok(())
|
||||
@@ -344,6 +348,41 @@ pub fn append_nodes_pointer(project_root: &Path, pointer: &str) -> Result<(), St
|
||||
.map_err(|e| format!("writing `{}`: {e}", path.display()))
|
||||
}
|
||||
|
||||
/// Stage everything and make the initial commit in a freshly-`git init`ed
|
||||
/// scaffold, so `git rev-parse HEAD` resolves from the very first run
|
||||
/// (otherwise HEAD stays unborn and provenance stamping sees no commit). A
|
||||
/// committer identity is passed inline via `-c` so this works on a box with
|
||||
/// no global git config; best-effort — a failure is a warning, never fatal,
|
||||
/// and never runs when `git init` itself was skipped or failed.
|
||||
fn commit_all(dir: &std::path::Path, message: &str) {
|
||||
let add = std::process::Command::new("git")
|
||||
.args(["add", "-A"])
|
||||
.current_dir(dir)
|
||||
.output();
|
||||
if !matches!(add, Ok(o) if o.status.success()) {
|
||||
eprintln!("aura: warning: git add failed (scaffold created without an initial commit)");
|
||||
return;
|
||||
}
|
||||
let commit = std::process::Command::new("git")
|
||||
.args([
|
||||
"-c",
|
||||
"user.email=aura@localhost",
|
||||
"-c",
|
||||
"user.name=aura",
|
||||
"commit",
|
||||
"-q",
|
||||
"-m",
|
||||
message,
|
||||
])
|
||||
.current_dir(dir)
|
||||
.output();
|
||||
if !matches!(commit, Ok(o) if o.status.success()) {
|
||||
eprintln!(
|
||||
"aura: warning: git commit failed (scaffold created without an initial commit)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `dir` already sits inside a git work tree (`git rev-parse`, the
|
||||
/// same oracle cargo-new consults). A missing git binary or any failure reads
|
||||
/// as "not in a work tree" — the init stays best-effort either way.
|
||||
|
||||
@@ -37,6 +37,34 @@ fn nodes_new_scaffolds_a_sibling_crate_and_attaches_it() {
|
||||
assert!(toml.contains("../lab-nodes"), "{toml}");
|
||||
}
|
||||
|
||||
/// Property (#243): a freshly scaffolded node crate stamps resolvable
|
||||
/// provenance too — `aura nodes new` follows the same `git init` with an
|
||||
/// initial commit as `aura new` (`scaffold_node_crate`, `scaffold.rs`), so
|
||||
/// `git rev-parse HEAD` resolves from the crate's very first run rather than
|
||||
/// leaving HEAD unborn. This half of #243's property runs and is asserted
|
||||
/// separately from `new_outside_a_work_tree_leaves_a_resolvable_head`
|
||||
/// (`project_new.rs`), which covers the `aura new` project half only.
|
||||
#[test]
|
||||
fn nodes_new_leaves_a_resolvable_head() {
|
||||
let cwd = temp_cwd("resolvable-head");
|
||||
assert!(aura(&["new", "lab"], &cwd).status.success());
|
||||
let proj = cwd.join("lab");
|
||||
let engine = env!("CARGO_MANIFEST_DIR").to_string() + "/../..";
|
||||
let out = aura(&["nodes", "new", "lab-nodes", "--engine-path", &engine], &proj);
|
||||
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
||||
let crate_dir = cwd.join("lab-nodes");
|
||||
let head = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(&crate_dir)
|
||||
.output()
|
||||
.expect("run git rev-parse HEAD");
|
||||
assert!(
|
||||
head.status.success(),
|
||||
"fresh node-crate scaffold has no HEAD (`git rev-parse HEAD` failed): {}",
|
||||
String::from_utf8_lossy(&head.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#241): `--namespace` sets the vocabulary prefix independently
|
||||
/// of the crate's own (directory) name — a crate can be named anything, but
|
||||
/// the blueprints that reference its nodes address them under the namespace
|
||||
|
||||
Reference in New Issue
Block a user