tools (volt): verify.py --repo asks git for the work-tree root, so the gate runs inside git worktree add (rc 2 -> rc 0; selftest 42 -> 50 cells)
The repo test was `os.path.isdir(<repo>/.git)`, which is wrong in both
directions. A LINKED worktree's `.git` is a 47-byte FILE (`gitdir: ...`), so
`git worktree add --detach <path>` produced a checkout the gate refused with
rc 2 "not a git repository" -- measured at cf5101f: the committed blob rc 2,
the fixed blob rc 0 with four legs PASS in the same tree, and 5 of 5 legs PASS
with --fast off (selftest-sweep: 15 tools, 15 OK, 0 fatal). An empty directory
named `.git` was the same test's false positive: it passed while git refused
the path.
`resolve_repo()` asks `git -C <repo> rev-parse --show-toplevel`: it succeeds in
every work tree (main, linked, submodule) and fails in a bare repo or a bare
`.git` directory. `--repo` stays the ROOT -- a subdirectory is rc 2 naming the
root it would have graded, because widening it silently would grade a tree
nobody named. Legs, rcs and --queue/--index semantics are untouched.
Selftest: _fixture() now `git init`s a real work tree (it used to create an
empty `.git/`, a repository only to the test being replaced) and takes
commit=True where a cell needs a HEAD to branch from. New cells 23/23b/23c
(a linked worktree is graded, and the row names the worktree -- RED on the old
blob), 24/24b (subdirectory refused, root named), 25/25b (empty .git refused),
26 (bare repo refused). 42 -> 50 cells, 50/50, rc 0.
No queue item opened or closed; no another-party file edited.
This commit is contained in:
1 parent
cf5101f5aa
commit
f59a592e49
2 files changed
+188
-10
No files matched your search
+130
-10
@@ -116,7 +116,23 @@ TOOLS ARE RESOLVED FROM --repo, NOT FROM THIS FILE'S DIRECTORY
|
||||
docs/queue-index.md) because `queue_append_audit` reads them with
|
||||
`git show <rev>:<path>`, which has no absolute-path form.
|
||||
|
||||
`--selftest` fixture corpus, 42 cells
|
||||
--repo IS A WORK TREE, ASKED OF GIT
|
||||
Until 2026-10-19 the repo test was `os.path.isdir(<repo>/.git)`, and it was
|
||||
wrong in both directions. A LINKED worktree's `.git` is a 47-byte file holding
|
||||
`gitdir: ...`, so from `git worktree add --detach <path>` the gate returned
|
||||
**rc 2 "not a git repository"** against a checkout whose every leg passes --
|
||||
measured that day on `cf5101f`: the old test rc 2, the test that follows rc 0
|
||||
with five legs green, `selftest-sweep` included. That made the gate
|
||||
unrunnable in the one sandbox a lane gets for free and forced the choice
|
||||
between grading unsandboxed in the shared clone and not running it at all.
|
||||
An empty directory named `.git` was the same test's false positive: it passed
|
||||
while git itself refused the path. `resolve_repo` now asks
|
||||
`git rev-parse --show-toplevel`, which succeeds in every work tree (main,
|
||||
linked, submodule) and fails in a bare repository or a bare `.git` directory.
|
||||
The path must still be the ROOT: a subdirectory is rc 2 naming the root,
|
||||
because widening `--repo` silently would grade a tree nobody named.
|
||||
|
||||
`--selftest` fixture corpus, 50 cells
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -222,6 +238,48 @@ def pick_detail(text, limit=150, summary=False):
|
||||
return kept[-1][:limit] if kept else ""
|
||||
|
||||
|
||||
def resolve_repo(path):
|
||||
"""`--repo` is a git WORK TREE ROOT. Return (repo, why-not), asking git.
|
||||
|
||||
Until 2026-10-19 this test was `os.path.isdir(<repo>/.git)`, which is the
|
||||
wrong question in both directions, and both were measured on that day:
|
||||
|
||||
* a LINKED worktree's `.git` is a 47-byte FILE holding `gitdir: ...`, so
|
||||
`git worktree add --detach` produced a checkout every leg would have
|
||||
graded and the gate refused with rc 2 "not a git repository" -- the one
|
||||
sandbox a lane gets for free was the one place the operator gate could
|
||||
not run, and the alternatives were grading unsandboxed in the shared
|
||||
clone or not running the gate at all;
|
||||
* an empty directory named `.git` passed the same test on a path git
|
||||
itself refuses, so the refusal was also not worth the name.
|
||||
|
||||
`git rev-parse --show-toplevel` answers the real question: it succeeds in
|
||||
every work tree (main, linked, submodule) and fails in a bare repository or
|
||||
a directory that merely holds a `.git` entry. The path must still be the
|
||||
ROOT -- a subdirectory is rc 2 with the root named, because widening
|
||||
`--repo` silently would grade a different tree than the caller named.
|
||||
"""
|
||||
repo = os.path.abspath(path)
|
||||
if not os.path.isdir(repo):
|
||||
return None, "cannot read repo: %s is not a directory" % repo
|
||||
try:
|
||||
proc = subprocess.run(["git", "-C", repo, "rev-parse", "--show-toplevel"],
|
||||
capture_output=True, text=True)
|
||||
except OSError as exc:
|
||||
return None, ("cannot read repo: %s (git is not runnable: %s)"
|
||||
% (repo, exc))
|
||||
top = (proc.stdout or "").strip()
|
||||
if proc.returncode != 0 or not top:
|
||||
why = [l for l in (proc.stderr or "").strip().splitlines() if l.strip()]
|
||||
return None, ("cannot read repo: %s is not a git work tree (%s)"
|
||||
% (repo, why[-1] if why else
|
||||
"git rev-parse rc %d" % proc.returncode))
|
||||
if os.path.realpath(top) != os.path.realpath(repo):
|
||||
return None, ("cannot read repo: %s is inside the work tree rooted at "
|
||||
"%s; --repo takes the root" % (repo, top))
|
||||
return repo, None
|
||||
|
||||
|
||||
def newest_checkin_day(repo):
|
||||
"""The newest `<YYYY-MM-DD>` prefix in checkins/ -> (day, why-not).
|
||||
|
||||
@@ -397,12 +455,9 @@ def main(argv=None):
|
||||
if a.selftest:
|
||||
return selftest()
|
||||
|
||||
repo = os.path.abspath(a.repo)
|
||||
if not os.path.isdir(repo):
|
||||
print("cannot read repo: %s is not a directory" % repo, file=sys.stderr)
|
||||
return 2
|
||||
if not os.path.isdir(os.path.join(repo, ".git")):
|
||||
print("cannot read repo: %s is not a git repository" % repo, file=sys.stderr)
|
||||
repo, why = resolve_repo(a.repo)
|
||||
if why:
|
||||
print(why, file=sys.stderr)
|
||||
return 2
|
||||
|
||||
rows = []
|
||||
@@ -444,17 +499,38 @@ def _stub(path, rc, out="", sleep=0.0, marker=None, argv_marker=None):
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
|
||||
def _git_init(repo, commit=False):
|
||||
"""Make `repo` a REAL work tree; with commit=True, one commit at HEAD.
|
||||
|
||||
The fixture used to create an empty `.git/` directory, which is not a
|
||||
repository -- it only looked like one to the `isdir(.git)` test this gate
|
||||
replaced. The fixture is now as real as what it grades, so cell 25 can grade
|
||||
the empty directory as the refusal it always was.
|
||||
"""
|
||||
subprocess.run(["git", "init", "-q", repo], check=True,
|
||||
capture_output=True, text=True)
|
||||
if commit:
|
||||
subprocess.run(["git", "-C", repo, "add", "-A"], check=True,
|
||||
capture_output=True, text=True)
|
||||
subprocess.run(["git", "-C", repo,
|
||||
"-c", "user.name=verify-selftest",
|
||||
"-c", "user.email=selftest@invalid",
|
||||
"-c", "commit.gpgsign=false",
|
||||
"commit", "-q", "-m", "fixture"], check=True,
|
||||
capture_output=True, text=True)
|
||||
|
||||
|
||||
def _fixture(base, rcs, outs=None, sleep=0.0, markers=None, argv_markers=None,
|
||||
checkins=("2026-01-01-fixture.md",)):
|
||||
checkins=("2026-01-01-fixture.md",), commit=False):
|
||||
"""A throwaway repo with stubbed legs. rcs: {tool: rc}.
|
||||
|
||||
`checkins` seeds `checkins/`: one dated file by default (so the check-in leg
|
||||
has a day to grade), a tuple of names for cells that need a specific set,
|
||||
and None for a repo with no `checkins/` directory at all.
|
||||
and None for a repo with no `checkins/` directory at all. `commit` makes one
|
||||
commit at HEAD, which a cell needs before it can `git worktree add`.
|
||||
"""
|
||||
repo = os.path.join(base, "repo")
|
||||
os.makedirs(os.path.join(repo, "tools"))
|
||||
os.makedirs(os.path.join(repo, ".git"))
|
||||
with open(os.path.join(repo, QUEUE_DEFAULT), "w") as fh:
|
||||
fh.write("## Q1 -- fixture\n")
|
||||
os.makedirs(os.path.join(repo, "docs"))
|
||||
@@ -473,6 +549,7 @@ def _fixture(base, rcs, outs=None, sleep=0.0, markers=None, argv_markers=None,
|
||||
out=(outs or {}).get(tool, ""), sleep=sleep,
|
||||
marker=(markers or {}).get(tool),
|
||||
argv_marker=(argv_markers or {}).get(tool))
|
||||
_git_init(repo, commit=commit)
|
||||
return repo
|
||||
|
||||
|
||||
@@ -733,6 +810,49 @@ def selftest():
|
||||
"tool: 1 new fault(s)\n"),
|
||||
"cannot grade: baseline x: No such file")
|
||||
|
||||
# 23. THE SANDBOX THE GATE REFUSED. A linked worktree's `.git` is a FILE
|
||||
# (`gitdir: ...`), not a directory, so `os.path.isdir(repo/.git)` -- the
|
||||
# test this gate used until 2026-10-19 -- answered "not a git repository"
|
||||
# (rc 2) on a checkout every leg would have graded. Measured RED against
|
||||
# the old test on this exact fixture, GREEN here.
|
||||
repo = _fixture(os.path.join(td, "c23"), GREEN5, commit=True)
|
||||
wt = os.path.join(td, "c23-wt")
|
||||
subprocess.run(["git", "-C", repo, "worktree", "add", "--detach", wt,
|
||||
"HEAD"], check=True, capture_output=True, text=True)
|
||||
with open(os.path.join(wt, ".git")) as fh:
|
||||
gitfile = fh.read()
|
||||
check("23 the fixture worktree really carries a .git file, not a dir",
|
||||
gitfile.startswith("gitdir: ") and not os.path.isdir(wt + "/.git"),
|
||||
True)
|
||||
rc, out = _verify("--repo", wt, "--fast")
|
||||
check("23b a linked worktree is graded, not refused -> rc 0", rc, 0)
|
||||
check("23c the row names the worktree, not the tree it links to",
|
||||
"verify -- repo: %s" % os.path.abspath(wt) in out, True)
|
||||
|
||||
# 24. --repo stays the ROOT: a subdirectory is refused with the root
|
||||
# named, because widening it silently would grade a tree nobody asked for
|
||||
rc, out = _verify("--repo", os.path.join(repo, "tools"), "--fast")
|
||||
check("24 a subdirectory is refused, not widened -> rc 2", rc, 2)
|
||||
check("24b the refusal names the root it would have graded",
|
||||
"--repo takes the root" in out and os.path.realpath(repo) in out,
|
||||
True)
|
||||
|
||||
# 25. the OLD test's false positive: an empty `.git` directory is not a
|
||||
# repository, and git says so where `isdir` said otherwise.
|
||||
fake = os.path.join(td, "c25")
|
||||
os.makedirs(os.path.join(fake, ".git"))
|
||||
rc, out = _verify("--repo", fake, "--fast")
|
||||
check("25 an empty '.git' directory is not a repo -> rc 2", rc, 2)
|
||||
check("25b and the refusal says git refused it",
|
||||
"is not a git work tree" in out and "cannot read repo:" in out, True)
|
||||
|
||||
# 26. a bare repository has no work tree to grade
|
||||
bare = os.path.join(td, "c26.git")
|
||||
subprocess.run(["git", "init", "-q", "--bare", bare], check=True,
|
||||
capture_output=True, text=True)
|
||||
rc, out = _verify("--repo", bare, "--fast")
|
||||
check("26 a bare repository -> rc 2", rc, 2)
|
||||
|
||||
print("selftest: %d cells, %d passed, %d failed" % (cells, passed, len(failures)))
|
||||
for f in failures:
|
||||
print(" FAIL %s" % f)
|
||||
|
||||
Reference in new issue
Block a user