Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/pytest_gpu_proof/gitutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ def get_branch() -> Optional[str]:


def is_dirty() -> bool:
# --ignore-submodules=untracked: untracked files INSIDE a submodule (build
# deps, caches) cannot change the code a receipt attests — the submodule's
# content is pinned by the parent's gitlink, and a *pin* change (a different
# or new commit checked out in the submodule) still reports as modified
# under this flag. Without it, a consumer whose submodule carries build
# artifacts can never produce a clean receipt (first hit: GATO's sqpcpu
# baseline submodule).
try:
result = subprocess.run(
["git", "status", "--porcelain"],
["git", "status", "--porcelain", "--ignore-submodules=untracked"],
capture_output=True,
text=True,
check=True,
Expand Down
69 changes: 69 additions & 0 deletions tests/test_gitutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""gitutils.is_dirty: submodule-aware dirty detection.

The dirty flag guards receipt honesty (allow_dirty policy), so its semantics
matter: anything that can change the attested code must count as dirty, and
nothing else. Submodules are the subtle case — the parent's gitlink pins their
content, so untracked files INSIDE a submodule (build deps, caches) are noise,
while a moved pin is a real change.
"""

import subprocess

from pytest_gpu_proof.gitutils import is_dirty


def _git(*args, cwd):
subprocess.run(
["git", "-c", "protocol.file.allow=always", *args],
cwd=cwd,
check=True,
capture_output=True,
text=True,
)


def _init_repo(path):
path.mkdir(exist_ok=True)
_git("init", "-q", cwd=path)
_git("config", "user.email", "t@t", cwd=path)
_git("config", "user.name", "t", cwd=path)


def _commit_all(path, msg="c"):
_git("add", "-A", cwd=path)
_git("commit", "-q", "-m", msg, cwd=path)


def test_is_dirty_submodule_semantics(tmp_path, monkeypatch):
child = tmp_path / "child"
_init_repo(child)
(child / "f.txt").write_text("x")
_commit_all(child)

parent = tmp_path / "parent"
_init_repo(parent)
(parent / "code.py").write_text("print('hi')")
_commit_all(parent)
_git("submodule", "add", "-q", str(child), "sub", cwd=parent)
_commit_all(parent, "add submodule")

monkeypatch.chdir(parent)
assert is_dirty() is False, "freshly committed tree must be clean"

# Untracked file INSIDE the submodule: pinned content unchanged -> clean.
(parent / "sub" / "build_artifact.o").write_text("junk")
assert is_dirty() is False, "untracked submodule content cannot change the attested code"

# Untracked file in the PARENT repo: dirty.
(parent / "stray.txt").write_text("y")
assert is_dirty() is True
(parent / "stray.txt").unlink()
assert is_dirty() is False

# A moved submodule PIN is a real change: dirty.
# (the submodule clone needs its own identity — CI runners have no global one)
_git("config", "user.email", "t@t", cwd=parent / "sub")
_git("config", "user.name", "t", cwd=parent / "sub")
(parent / "sub" / "f.txt").write_text("changed")
_commit_all(parent / "sub", "advance pin")
assert is_dirty() is True
Loading