c0re: address argus review nits on #434
This commit is contained in:
parent
008aad0bc5
commit
57a753d80a
2 changed files with 18 additions and 8 deletions
|
|
@ -53,14 +53,15 @@ async fn read_lock_at_tag(repo: &Path, tag: &str) -> Result<Option<String>> {
|
||||||
.with_context(|| format!("git show {spec} in {}", repo.display()))?;
|
.with_context(|| format!("git show {spec} in {}", repo.display()))?;
|
||||||
if !out.status.success() {
|
if !out.status.success() {
|
||||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||||
// git uses different messages for "path not in tree" depending
|
// git uses two different messages for "path not in tree"
|
||||||
// on version + whether the path collides with an on-disk file.
|
// depending on whether the path also collides with an on-disk
|
||||||
// Treat any of them as "no flake.lock in this commit" rather
|
// file. Both translate to "no flake.lock in this commit" —
|
||||||
// than a hard failure — an agent with empty `inputs = { }` is
|
// a legitimate, dedup-clean state for an agent with empty
|
||||||
// a legitimate, dedup-clean state.
|
// `inputs = { }`. Any other git failure (permission denied,
|
||||||
|
// ref-not-found, etc.) propagates as a hard error rather than
|
||||||
|
// being silently swallowed.
|
||||||
if stderr.contains("does not exist")
|
if stderr.contains("does not exist")
|
||||||
|| stderr.contains("exists on disk, but not in")
|
|| stderr.contains("exists on disk, but not in")
|
||||||
|| stderr.contains("Path '")
|
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
@ -242,6 +243,12 @@ async fn lock_in_sync_inner(worktree: &Path) -> Result<()> {
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
// An agent that declares inputs in flake.nix but ships no
|
||||||
|
// flake.lock at all hits this branch (committed = None,
|
||||||
|
// regenerated = Some(...)). That's a deliberate reject: every
|
||||||
|
// agent with inputs MUST commit its lock, otherwise meta's
|
||||||
|
// dedup pass has nothing to introspect and the broken state
|
||||||
|
// leaks downstream. Treated identically to a stale lock.
|
||||||
if committed.as_deref() != regenerated.as_deref() {
|
if committed.as_deref() != regenerated.as_deref() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"flake.lock is out of sync with flake.nix — `nix flake lock` produces a different lock. \
|
"flake.lock is out of sync with flake.nix — `nix flake lock` produces a different lock. \
|
||||||
|
|
|
||||||
|
|
@ -635,8 +635,11 @@ async fn submit_apply_commit(
|
||||||
.map_err(|e| anyhow::anyhow!("persist fetched_sha: {e:#}"))?;
|
.map_err(|e| anyhow::anyhow!("persist fetched_sha: {e:#}"))?;
|
||||||
// #317 pre-flight gates: both reject the apply before approval if
|
// #317 pre-flight gates: both reject the apply before approval if
|
||||||
// the agent's flake state would inflate meta's lock with duplicates
|
// the agent's flake state would inflate meta's lock with duplicates
|
||||||
// or lie about what nix will fetch. Order matters — sync first so
|
// or lie about what nix will fetch. Both checks independently read
|
||||||
// the dedup pass acts on the lock nix would actually produce.
|
// `<tag>:flake.lock` via git — they don't share state. Order matters
|
||||||
|
// only for early-exit + messaging: sync first means a stale lock
|
||||||
|
// bails with the actionable "run `nix flake lock`" hint rather than
|
||||||
|
// a dedup pass on a lock nix would never produce.
|
||||||
//
|
//
|
||||||
// Runs after `set_fetched_sha` so the failed row carries the sha
|
// Runs after `set_fetched_sha` so the failed row carries the sha
|
||||||
// that broke. Both failure paths mark + emit, then bail.
|
// that broke. Both failure paths mark + emit, then bail.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue