Skip to content

tests: git spawns inherit the developer's global config #400

Description

@pbean

The test suite spawns git without pinning the global/system config, so a developer's own
~/.gitconfig reaches the fixtures. Measured, not reasoned — with GIT_CONFIG_GLOBAL
naming a file that sets commit.gpgsign = true:

$ GIT_CONFIG_GLOBAL=<hostile> uv run pytest tests/test_install.py \
    -k test_shield_keeps_edge_whitespace_in_the_common_dir
gpg: signing failed: No secret key
fatal: failed to write commit object
FAILED tests/test_install.py::test_shield_keeps_edge_whitespace_in_the_common_dir

$ GIT_CONFIG_GLOBAL=<hostile> uv run pytest tests/test_install.py \
    -k test_shield_refuses_a_repository_shared_between_os_users
subprocess.CalledProcessError: Command '['git', '-C',
  '.../project-template0/sandbox', 'commit', '-q', '-m', 'initial']'
  returned non-zero exit status 128
ERROR (raised in the fixture)

The second one is the reason this is worth an issue rather than a patch to one test: it
never touches a raw git subprocess. It fails inside tests/conftest.py's session-scoped
project template, which every sandbox test in the suite copies from — so the exposure is
suite-wide, not local to any test that happens to call subprocess.run(["git", ...]).

Scope

  • tests/conftest.py sets none of GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM, HOME,
    commit.gpgsign or init.templateDir, and its git() helper spawns bare
    ["git", "-C", str(repo), *args].
  • Eight further raw-subprocess sites in tests/test_install.py, plus
    tests/test_stories_e2e.py.

commit.gpgsign is only the cheapest demonstration; init.templateDir (which can install
hooks into every git init), core.autocrlf, core.hooksPath and a global
core.excludesFile all reach the fixtures the same way. A global core.excludesFile is
worth calling out separately, because several shield tests in test_install.py set that
key themselves
and would be reading the developer's file in any test that does not.

CI is unaffected — hosted runners have clean configs — so this shows up only as a local
suite that fails for people whose git is configured normally, which is a poor first
experience for a new contributor and easy to misread as a real regression.

Suggested shape (not measured, so treat as a starting point)

One autouse session fixture in tests/conftest.py pointing GIT_CONFIG_GLOBAL and
GIT_CONFIG_SYSTEM at empty files under tmp_path_factory, rather than per-test -c
flags — the failure above happens in a fixture, so per-call flags would not have caught it.
Worth checking against test_shield_seeds_xdg_default_when_unset, which deliberately pins
GIT_CONFIG_NOSYSTEM=1, and against the Windows legs: suppressing the system config there
also suppresses Git-for-Windows' core.autocrlf=true, which has already produced one
Linux-invisible failure on PR #385.

Provenance

Raised by CodeRabbit on PR #385 against
test_shield_keeps_edge_whitespace_in_the_common_dir. The mechanism is real; the scope in
that comment is narrower than the defect. Filed separately rather than patched there
because a one-test fix would leave the suite equally exposed while making it look
isolated, and because #385 is already far past the size cap (4592/260, 1530 non-test).
The condition predates that branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions