fix(missions): commit under the pipeline's own git identity
Mission 019fc450 lost its first phase to: git commit → exit 128: Author identity unknown The server container has no git identity — `git config --global user.email` exits 1 — so any commit fails unless one is supplied. This is the third consecutive failure whose trigger was agent behaviour rather than our code. Earlier missions committed only because an agent had happened to run `git config user.email` in the checkout, leaving a local identity the server inherited. Alongside the object-permission split and the reset, the pattern is the same: delivery depended on incidental side effects of what an agent chose to do, so identical missions succeeded or failed for reasons invisible in our code. Supplied via GIT_AUTHOR_*/GIT_COMMITTER_* env on every git call, which overrides config without a leaked string per invocation and names the committer as the pipeline. Agents' own commits keep the identity they set. The test asserts the identity *overrides* an existing local config rather than trying to unset the developer's global — an override necessarily also applies when config is absent, and it does not race parallel tests. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
08b2adae23
commit
25d9805806
@@ -736,3 +736,60 @@ async fn a_checkout_is_writable_by_both_uids_that_share_it() {
|
||||
}
|
||||
assert!(checked > 0, "no object directories were created to check");
|
||||
}
|
||||
|
||||
/// Delivery must commit without depending on the checkout's git identity.
|
||||
///
|
||||
/// The server container has no identity of its own (`git config --global
|
||||
/// user.email` exits 1), so `git commit` fails with "Author identity unknown"
|
||||
/// unless one is supplied. Mission `019fc450` lost its first phase that way,
|
||||
/// while earlier missions committed fine — because their agents had happened
|
||||
/// to run `git config user.email` in the checkout first.
|
||||
///
|
||||
/// A test process cannot unset the developer's global git config without
|
||||
/// racing every other test, so this asserts the stronger, deterministic
|
||||
/// property: the pipeline's identity is used even when the checkout already
|
||||
/// has a different one. An identity that overrides existing config is
|
||||
/// necessarily also present when config is absent.
|
||||
#[tokio::test]
|
||||
async fn delivery_commits_under_its_own_identity() {
|
||||
let pool = cm_testkit::test_pool().await;
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mission = Uuid::now_v7();
|
||||
let repo = seed_repo(tmp.path(), mission);
|
||||
let (_, phase) = seed_mission_phase(&pool, mission).await;
|
||||
|
||||
// seed_repo configures "Test <[email protected]>" locally; the base
|
||||
// commit therefore carries it, and the delivery commit must not.
|
||||
std::fs::write(repo.join("IDENTITY_PROBE.md"), "PROBE\n").unwrap();
|
||||
let cap = capture(&pool, tmp.path(), mission, phase)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let commit = cap.committed.expect("delivery committed");
|
||||
|
||||
let author = Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&repo)
|
||||
.args(["log", "-1", "--format=%an <%ae>", &commit.sha])
|
||||
.output()
|
||||
.unwrap();
|
||||
let author = String::from_utf8_lossy(&author.stdout).trim().to_string();
|
||||
assert_eq!(
|
||||
author, "ClawMates delivery <[email protected]>",
|
||||
"delivery must supply its own identity, not inherit whatever the \
|
||||
checkout happens to have configured"
|
||||
);
|
||||
|
||||
let base_author = Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&repo)
|
||||
.args(["log", "-1", "--format=%an", "HEAD~1"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&base_author.stdout).trim(),
|
||||
"Test",
|
||||
"the pre-existing local identity is still configured, so the \
|
||||
assertion above proves an override rather than an absence"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user