fix(tests): make scan-project fixtures hermetic against host global gitignore (#427)#448
fix(tests): make scan-project fixtures hermetic against host global gitignore (#427)#448tirth8205 wants to merge 3 commits into
Conversation
…itignore (Egonex-AI#427) scan-project.mjs enumerates files via `git ls-files -co --exclude-standard`, which honors the host's core.excludesFile (global gitignore). Contributors who keep `.env`/`.env.local` in their global gitignore had the fixture dotfiles hidden from the scanner, so byPath('.env') returned undefined and the dotfile configs test died with "Cannot read properties of undefined (reading 'fileCategory')". CI (ubuntu-latest) has no global gitignore so it never saw it. Add a HERMETIC_GIT_ENV constant that nulls GIT_CONFIG_GLOBAL/SYSTEM and, crucially, overrides core.excludesFile (which defaults to $XDG_CONFIG_HOME/git/ignore even with no config file). Pass it as `env` to both the `git init` and the `node scan-project.mjs` spawnSync calls so the fixtures are hermetic against any host config. Also guard the dotfile test's lookup with toBeDefined() for a clearer failure message. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x-AI#427 reproduces on any host The HERMETIC_GIT_ENV fix makes scan-project fixtures independent of the host global gitignore, but the dotfile-config fixture was dotfile-only. When a host global gitignore hides every dotfile, `git ls-files` returns an empty list, which makes scan-project fall back to its (gitignore-unaware) recursive walker and silently re-discover the dotfiles — masking the Egonex-AI#427 leak even without the hermetic env. Adding a non-dotfile sibling (`keep.ts`) keeps `git ls-files` output non-empty so the git enumeration path is actually exercised. With this, the test reliably FAILS without the hermetic env under a simulated global gitignore and PASSES with it (verified both with and without a simulated `XDG_CONFIG_HOME` ignore). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thejesh23
left a comment
There was a problem hiding this comment.
1. /dev/null paths are not portable to Windows contributors
The PR's stated purpose is unbreaking local contributor runs, but /dev/null doesn't exist on Windows — git on Windows will fail to open it as the core.excludesFile (and as the global/system config). On Windows the hermetic env will itself error out git ls-files, silently triggering the walker fallback (which is what the fix tries to avoid). Consider os.devNull (NUL on win32) or an empty tmp file created once per process.
2. GIT_CONFIG_NOSYSTEM not set; system include paths still resolvable
GIT_CONFIG_SYSTEM=/dev/null only redirects the primary system file; it does not stop git from reading $(prefix)/etc/gitconfig includes or honoring core.attributesFile / core.hooksPath from a system install. The canonical fully-hermetic recipe is GIT_CONFIG_NOSYSTEM=1 plus GIT_CONFIG_GLOBAL=/dev/null plus the GIT_CONFIG_COUNT override. Worth adding for completeness, since #427's root cause is exactly this class of leakage.
3. Only one fixture hardened against the silent-walker-fallback masking
The follow-up addition of keep.ts to force the git enumeration path is applied solely to the dotfile-configs test. Other fixtures that are dotfile-only or sparse (e.g., the .env / config-related setups elsewhere in this file) still risk emptying git ls-files and falling through to the gitignore-unaware walker, where the same class of host-config bug would be masked again. Either generalize setupTree to inject a sentinel non-dotfile, or assert r.stderr/source-of-truth is git in tests that care.
The HERMETIC_GIT_ENV nulled global/system git config and core.excludesFile with a literal '/dev/null', which does not exist on Windows (git uses NUL). On win32 this would either error or fail to null the sink, defeating the hermetic guarantee the PR delivers. Use os.devNull, which resolves to /dev/null on POSIX and NUL on win32. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
1. 2. 3. Only one fixture hardened — Already covered. |
Problem
scan-project.mjsenumerates files viagit ls-files -co --exclude-standard, which honors the host'score.excludesFile(the global gitignore). Many contributors keep.env/.env.localin their global gitignore. When they do, the test fixtures' dotfiles never reach the scanner,byPath(r.output, '.env')returnsundefined, and the "dotfile configs" test dies with:CI (ubuntu-latest) has no global gitignore, so it never reproduces this — it only bites local contributors. Nulling
GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEMis not enough, becausecore.excludesFiledefaults to$XDG_CONFIG_HOME/git/ignore(or~/.config/git/ignore) even with no config file present — the knob itself must be overridden.Fix
Add a
HERMETIC_GIT_ENVconstant that nulls the global/system git config and overridescore.excludesFilevia theGIT_CONFIG_COUNT/GIT_CONFIG_KEY_0/GIT_CONFIG_VALUE_0mechanism, then pass it asenvto both spawnSync calls — thegit initinsetupTree()and thenode scan-project.mjsinvocation inrunScript(). This makes the fixtures hermetic against any host git configuration. The dotfile test's lookup is also guarded withtoBeDefined()for a clearer failure message if this ever regresses.Only the test file
tests/skill/understand/test_scan_project.test.mjschanges; the scanner itself is untouched.Testing
Reproduced the bug by simulating a host global gitignore that ignores
.env:TypeError ... reading 'fileCategory'at the.envdotfile test.Lint (
eslint) and core typecheck (tsc --noEmit) are green; the package test suite (vitest run tests/skill/understand/test_scan_project.test.mjs) passes 32/32 both with and without the simulated global ignore.Hardening the regression (follow-up commit)
The original dotfile-config fixture was dotfile-only. When a host global gitignore hides every dotfile,
git ls-filesreturns an empty list, soscan-project.mjsfalls back to its (gitignore-unaware) recursive walker and re-discovers the dotfiles — masking the #427 leak even without the hermetic env. Adding a non-dotfile sibling (keep.ts) to that fixture keepsgit ls-filesoutput non-empty, forcing the git enumeration path so the regression reliably fails without the hermetic env (expected undefined to be defined) and passes with it. Verified red→green:XDG_CONFIG_HOMEglobal ignore → the dotfile test FAILS.HERMETIC_GIT_ENV) → 32/32 pass both with and without the simulated ignore.Full root vitest suite (16 files, 207 tests) and
eslinton the changed file are green.Fixes #427
🤖 Generated with Claude Code