Skip to content

fix(web): let vitest run from a checkout inside a .git directory#879

Open
aelefebv wants to merge 1 commit into
mainfrom
agent/lucida-bfk-vitest-in-git-dir
Open

fix(web): let vitest run from a checkout inside a .git directory#879
aelefebv wants to merge 1 commit into
mainfrom
agent/lucida-bfk-vitest-in-git-dir

Conversation

@aelefebv

Copy link
Copy Markdown
Owner

Closes lucida-bfk.

The problem

Running the web suite from a checkout that lives inside a .git directory failed 49 of 153 test files. Two independent mechanisms: vitest's built-in exclude, and Vite's server.fs.deny, whose default includes **/.git/**. Any path containing a .git segment is rejected, so the happy-dom test files could not be loaded at all — a spurious failure with nothing wrong in the code.

This is not hypothetical housekeeping. Orchestrated worktrees are placed under .git/, which made the whole web toolchain unusable from them.

The fix

vite.config.ts replaces the default **/.git/** deny rule with one anchored to the checkout root, so the surrounding .git no longer blocks loads while any .git inside the tree stays protected. The override is narrowly gated: it applies only when VITEST is set and the mode is test and the checkout is actually inside a .git path. An ordinary checkout never takes it, in any mode.

There is also a startup guard: if the resolved workspace root escapes the checkout, the config throws rather than silently widening what the dev server will serve.

How it was verified

Independent review measured a seven-row live-server matrix, including a negative control the first attempt lacked — an ordinary checkout with a real sibling .git inside the allow root, which is the only row that could catch an over-broad override. The override is inert in five of six standard rows, active only for the intended one, and does not reach the sibling .git.

The pre-fix state leaked file contents through /@fs/<checkout>/.git/config on both the dev-server and vitest --api surfaces; all such requests are 403 after. .env, .env.*, *.{crt,pem} and fs.strict remain in force everywhere.

The new guard cannot fire in this repository today: there is no workspace marker from the checkout root upward and no workspaces field in the root package.json.

Gates under Node 24 (matching CI's lts/*): 153 files / 2198 tests, lint, tsc -b, and vite build all clean.

Known and documented, not fixed

Running vitest with a non-test --mode from such a checkout brings the original failure back. That invocation was already broken before this change, and the mode === 'test' condition is what stops a stray exported VITEST=1 from relaxing pnpm dev. It is written down in the gotcha page.

🤖 Generated with Claude Code

Vite's default `server.fs.deny` includes `**/.git/**` so a server never
hands repository internals to a client. That default assumes the project
is not itself inside a `.git` directory. When it is — a linked worktree
under `.git/` is an ordinary layout — the project's own files match the
pattern, and every test file running in a browser-like environment fails
to load with `Cannot find module '/src/...'`. From such a checkout the
suite reported 49 of 153 files red for reasons unrelated to the code.

Re-anchor that rule to the checkout root when running under Vitest with
`--mode test` from such a checkout, rather than dropping it. Test files
load again, and a real `.git` directory *inside* the checkout stays
denied: without the anchor its `config` — and any credential embedded in
a remote URL — is served in full.

The surrounding `.git` directory can't be handled by a glob, because it
is a parent of the checkout and denying it denies the checkout too. Its
reachability is set by `server.fs.allow`, into which Vitest unions its
own entries — `dirname(configFile)`, `searchForWorkspaceRoot(root)` and
its dist dir — alongside the `allow: ['..']` set here. A union is never
narrower, so those entries can only widen it. They land inside the
checkout today only because no workspace marker exists above it; adding
a `pnpm-workspace.yaml` at the repo root would widen test-time file
serving to include the outer `.git`. Rather than depend on that
silently, the config resolves the workspace root itself and fails with
an explanatory error — one that also offers a way out for a reader whose
checkout location isn't theirs to change.

Gating on `mode === 'test'` costs one thing, noted in the wiki: running
Vitest with a non-test `--mode` from such a checkout brings the original
failure back. It buys immunity from a stray exported `VITEST=1` relaxing
`pnpm dev`, which is the better trade.

Unchanged: `.env`, `.env.*` and key material stay denied in tests too,
`fs.strict` stays on, and an ordinary checkout never takes the override
in any mode. Two things the wiki page is explicit about rather than
glossing: a test run is not serverless (`vitest --api`/`--ui` expose the
same pipeline over HTTP), and everything under the checkout is reachable
through it, not just source and tests.

From this worktree: 49 failed / 104 passed (1606 tests) before,
153 passed (2198 tests) after.

Co-Authored-By: Claude Opus 5 <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