Skip to content

Commit 68f79bf

Browse files
EliahKaganclaude
andcommitted
Add a check that fixture directories are usable by git
Many tests rely on git accepting their fixture directories -- the GitPython working tree and the vendored gitdb and smmap submodule working trees. When git rejects one for "dubious ownership", typically because a CI workflow's safe.directory list is missing an entry, downstream tests fail in opaque ways: IndexError in test_docs::Tutorials::test_submodules, AssertionError in test_repo::TestRepo::test_submodules and test_submodule::TestSubmodule::test_root_module, plus BrokenPipeError or ValueError elsewhere depending on a race between Python's flush and pipe-close on the cached "git cat-file --batch-check" subprocess. test/test_fixture_health.py runs `git rev-parse --show-toplevel` in each fixture directory and asserts both that git is willing to operate there and that it reports the directory as its own toplevel. The failure message names safe.directory and ownership as the likely causes, so a misconfigured environment is recognizable directly from the test output rather than only inferable from the cascade of downstream failures. This commit adds the test only. On the Cygwin CI workflow as configured at this commit, the test fails for gitdb and smmap, demonstrating the bug. Subsequent commits in this chain reproduce the bug at scale and apply the fix. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cb49492 commit 68f79bf

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

test/test_fixture_health.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# This module is part of GitPython and is released under the
2+
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
3+
4+
"""Verify that fixture directories are usable by git.
5+
6+
If a directory the test suite relies on is rejected by git -- typically for
7+
"dubious ownership" because the CI workflow or local clone is missing a
8+
``safe.directory`` entry, or because directory ownership doesn't match the
9+
running user -- many tests downstream fail in confusing ways. The checks here
10+
name the root cause clearly so a misconfigured environment is recognizable
11+
from the test output.
12+
13+
These tests do not exercise GitPython's production code. They verify the
14+
conditions under which production code is exercised are valid.
15+
16+
A check is skipped, rather than failed, if a fixture directory is missing or
17+
has no ``.git`` marker, since that condition is more naturally diagnosed as
18+
"``init-tests-after-clone.sh`` hasn't been run" than as a problem with
19+
``safe.directory``.
20+
"""
21+
22+
import subprocess
23+
from pathlib import Path
24+
25+
import pytest
26+
27+
REPO_ROOT = Path(__file__).resolve().parent.parent
28+
29+
# Directories git must trust for the test suite to operate normally. The
30+
# current set is the GitPython working tree plus the working trees of the
31+
# vendored submodules (gitdb and the nested smmap inside it). New entries
32+
# should be added here whenever the test suite gains a dependency on git
33+
# accepting another directory.
34+
FIXTURE_DIRS = [
35+
pytest.param(REPO_ROOT, id="repo_root"),
36+
pytest.param(REPO_ROOT / "git" / "ext" / "gitdb", id="gitdb"),
37+
pytest.param(
38+
REPO_ROOT / "git" / "ext" / "gitdb" / "gitdb" / "ext" / "smmap",
39+
id="smmap",
40+
),
41+
]
42+
43+
44+
@pytest.mark.parametrize("fixture_dir", FIXTURE_DIRS)
45+
def test_fixture_dir_is_trusted_by_git(fixture_dir: Path) -> None:
46+
"""git accepts ``fixture_dir`` as its own repository owned by a trusted user.
47+
48+
Run ``git -C <fixture_dir> rev-parse --show-toplevel`` and assert it
49+
succeeds and reports ``fixture_dir`` itself as the toplevel. Failure
50+
typically means a missing ``safe.directory`` entry in the CI workflow or
51+
in the developer's local git config, or a clone whose on-disk ownership
52+
doesn't match the running user.
53+
"""
54+
if not fixture_dir.exists():
55+
pytest.skip(
56+
f"{fixture_dir} not present "
57+
"(run ./init-tests-after-clone.sh from the repo root)"
58+
)
59+
if not (fixture_dir / ".git").exists():
60+
pytest.skip(
61+
f"{fixture_dir} has no .git marker "
62+
"(submodule not initialized; run ./init-tests-after-clone.sh "
63+
"from the repo root)"
64+
)
65+
try:
66+
result = subprocess.run(
67+
["git", "-C", str(fixture_dir), "rev-parse", "--show-toplevel"],
68+
capture_output=True,
69+
text=True,
70+
check=False,
71+
)
72+
except FileNotFoundError:
73+
pytest.skip("git is not installed or not on PATH")
74+
assert result.returncode == 0, (
75+
f"git refuses to operate in {fixture_dir}.\n"
76+
f"stderr: {result.stderr.strip()}\n"
77+
"This usually means the CI workflow or local environment is missing "
78+
"a `safe.directory` entry for this path, or that the directory's "
79+
"ownership doesn't match the running user."
80+
)
81+
reported = Path(result.stdout.strip())
82+
assert reported.samefile(fixture_dir), (
83+
f"git reports the toplevel as {reported}, "
84+
f"not as {fixture_dir} itself. "
85+
"This usually means the directory is not an initialized git "
86+
"repository (its `.git` marker may be stale or pointing elsewhere)."
87+
)

0 commit comments

Comments
 (0)