Skip to content

Commit fa0d9bc

Browse files
EliahKaganclaude
andcommitted
Add a test that fixture directories are usable by git
Many tests rely on git accepting their fixture directories: the GitPython repository itself, and the gitdb and smmap submodules used by the test suite as direct and nested submodule fixtures. When git rejects one for "dubious ownership" (typically because a CI workflow's `safe.directory` list is missing an entry), three submodule-related tests fail in opaque ways. The exact failure type depends on which side of a race wins in the flush to the cached `git cat-file --batch-check` subprocess: - When Python wins, `ValueError` reaches the test directly. - When git wins, `BrokenPipeError` is caught internally as `IOError` and `iter_items` returns early. Each test then fails on the empty-or-short result in its own way: - `test_docs::Tutorials::test_submodules` indexes the empty `sm.children()` list with `[0]` and raises `IndexError`. - `test_repo::TestRepo::test_submodules` and `test_submodule::TestSubmodule::test_root_module` see a length-1 (not length-2) submodule list from their recursive traversals and fail their length assertion with `AssertionError`. This commit adds `test/test_fixture_health.py`, which 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 needing to be pieced together from a cascade of failing tests not conceptually related to safe directory protections. This adds the test only. Further replication and a fix are forthcoming. On the Cygwin CI workflow now, the test fails for `[gitdb]` and passes for `[repo_root]` and `[smmap]`. The asymmetry between the two submodules relates to NTFS ownership inherited from `actions/checkout`'s clone of the main tree. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9c5b57b commit fa0d9bc

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

test/test_fixture_health.py

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

0 commit comments

Comments
 (0)