Skip to content

fix: strip GIT_* env from test process to prevent worktree corruption#383

Merged
tomasz-tomczyk merged 1 commit intomainfrom
fix-testutil-git-leak
Apr 29, 2026
Merged

fix: strip GIT_* env from test process to prevent worktree corruption#383
tomasz-tomczyk merged 1 commit intomainfrom
fix-testutil-git-leak

Conversation

@tomasz-tomczyk
Copy link
Copy Markdown
Owner

Summary

  • Tests using runGit in testutil_test.go inherited GIT_DIR / GIT_INDEX_FILE / GIT_WORK_TREE from the parent process. When .githooks/pre-commit ran go test ./... during a real git commit, every git op inside test helpers targeted the parent repo instead of the test's t.TempDir() — silently corrupting the active worktree (HEAD renamed to main, stray initial commits authored by Test <test@test.com>, local user.email/name overwritten, and via tests that git config core.worktree, the bare repo's core.bare flipped to false).
  • Two-layer fix: a process-wide init() strips GIT_* once (covers production paths like NewSessionFromGit that exec git internally); runGit also filters GIT_* and HOME per-call as defense-in-depth.
  • Adds TestGitEnvLeakStripped to guard against regression.
  • Rewrites .config/wt.toml to use wt step copy-ignored in post-start instead of hand-rolled cp -R of node_modules in pre-start — covers all gitignored files, doesn't block worktree creation, keeps wt's approval cache stable across edits.

Test plan

  • go test ./... -count=1 passes (~29s).
  • Reproduced the original bug pre-fix: simulated git commit env (GIT_DIR/GIT_INDEX_FILE/GIT_WORK_TREE set), then go test -run 'TestNewSessionFromGitLazyThreshold' corrupted the worktree as described.
  • Same simulation with the fix in place: worktree HEAD/branch/config unchanged, bare repo bare = true preserved, no user identity leak.
  • Real git commit (this PR) ran the full pre-commit hook including go test ./... without corruption — the fix self-validates.
  • TestGitEnvLeakStripped passes.
  • Reviewer: try wt switch --create some-branch --base main and confirm wt step copy-ignored populates node_modules / e2e/node_modules from the primary worktree (may require wt config approvals add to re-approve the new command string).

🤖 Generated with Claude Code

testutil_test.go's runGit helper inherited GIT_DIR / GIT_INDEX_FILE /
GIT_WORK_TREE from os.Environ(). When `go test ./...` ran inside a git
hook (e.g. .githooks/pre-commit during `git commit`), every git op in
test helpers — including `git config user.email`, `git branch -M main`,
and the test's `git commit -m initial` — targeted the parent repo
instead of the test's t.TempDir(), corrupting the active worktree:
HEAD renamed to main, two stray "initial" commits authored by Test
<test@test.com>, local user identity overwritten, and (via tests that
do `git config core.worktree`) the bare repo's core.bare flipped to
false with a tmpdir worktree path.

Fix in two layers:
- init() in testutil_test.go strips GIT_* from the process env once,
  before any test code (including production paths like NewSessionFromGit
  that exec git internally) runs.
- runGit additionally filters GIT_* and HOME from cmd.Env each call —
  defense-in-depth against tests that t.Setenv("GIT_DIR", ...) for
  their own purposes, and to avoid duplicate-HOME ambiguity in cmd.Env.

TestGitEnvLeakStripped guards against regression: asserts init()
cleared the well-known vars and that runGit ignores a re-set GIT_DIR.

Also rewrites .config/wt.toml to use `wt step copy-ignored` in
post-start instead of hand-rolled cp -R of node_modules in pre-start —
copy-ignored handles all gitignored files, post-start doesn't block
worktree creation, and stable command strings keep wt's approval cache
valid across edits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.59%. Comparing base (f79d9f9) to head (06f273d).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #383      +/-   ##
==========================================
+ Coverage   66.58%   66.59%   +0.01%     
==========================================
  Files          19       19              
  Lines        8176     8176              
==========================================
+ Hits         5444     5445       +1     
+ Misses       2310     2309       -1     
  Partials      422      422              
Flag Coverage Δ
e2e 33.94% <ø> (ø)
unit 62.61% <ø> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tomasz-tomczyk tomasz-tomczyk merged commit 6cbb4dd into main Apr 29, 2026
6 checks passed
@tomasz-tomczyk tomasz-tomczyk deleted the fix-testutil-git-leak branch April 29, 2026 08:43
tomasz-tomczyk added a commit that referenced this pull request Apr 29, 2026
…#383)

testutil_test.go's runGit helper inherited GIT_DIR / GIT_INDEX_FILE /
GIT_WORK_TREE from os.Environ(). When `go test ./...` ran inside a git
hook (e.g. .githooks/pre-commit during `git commit`), every git op in
test helpers — including `git config user.email`, `git branch -M main`,
and the test's `git commit -m initial` — targeted the parent repo
instead of the test's t.TempDir(), corrupting the active worktree:
HEAD renamed to main, two stray "initial" commits authored by Test
<test@test.com>, local user identity overwritten, and (via tests that
do `git config core.worktree`) the bare repo's core.bare flipped to
false with a tmpdir worktree path.

Fix in two layers:
- init() in testutil_test.go strips GIT_* from the process env once,
  before any test code (including production paths like NewSessionFromGit
  that exec git internally) runs.
- runGit additionally filters GIT_* and HOME from cmd.Env each call —
  defense-in-depth against tests that t.Setenv("GIT_DIR", ...) for
  their own purposes, and to avoid duplicate-HOME ambiguity in cmd.Env.

TestGitEnvLeakStripped guards against regression: asserts init()
cleared the well-known vars and that runGit ignores a re-set GIT_DIR.

Also rewrites .config/wt.toml to use `wt step copy-ignored` in
post-start instead of hand-rolled cp -R of node_modules in pre-start —
copy-ignored handles all gitignored files, post-start doesn't block
worktree creation, and stable command strings keep wt's approval cache
valid across edits.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant