[package-deps-hash] Fix build cache failures in git linked worktrees caused by GIT_DIR set by pre-commit hooks#5815
Conversation
7b20309 to
7bfddde
Compare
| 'CON', | ||
| 'PRN', | ||
| 'AUX', | ||
| 'NUL', | ||
| 'COM1', | ||
| 'COM2', | ||
| 'COM3', | ||
| 'COM4', | ||
| 'COM5', | ||
| 'COM6', | ||
| 'COM7', | ||
| 'COM8', | ||
| 'COM9', | ||
| 'LPT1', | ||
| 'LPT2', | ||
| 'LPT3', | ||
| 'LPT4', | ||
| 'LPT5', | ||
| 'LPT6', | ||
| 'LPT7', | ||
| 'LPT8', | ||
| 'LPT9' |
There was a problem hiding this comment.
This change came from the pre-commit prettier script - not sure if my dev environment isn't set up correctly, but I can revert this specific change if needed.
There was a problem hiding this comment.
That's fine. Don't worry about it.
|
@microsoft-github-policy-service agree company="Squarespace" |
… calls to fix build cache in linked worktrees
When a git pre-commit hook runs in a linked worktree, git sets GIT_DIR to the
per-worktree metadata directory (.git/worktrees/{name}) without setting
GIT_WORK_TREE. With GIT_DIR set this way, `git rev-parse --show-toplevel`
returns the CWD (e.g. the rushJsonFolder subdirectory) instead of the actual
worktree root, causing all subsequent git calls to use the wrong root directory.
This makes `git status -u` miss the top-level .gitignore, surfacing node_modules
symlinks as untracked files, which then causes `git hash-object` to fail on
symlink-to-directory entries and ultimately breaks the build cache.
Fix: strip GIT_DIR and GIT_WORK_TREE from the environment in getRepoRoot,
spawnGitAsync, and getRepoChanges so git auto-discovers the correct repo root
from the working directory regardless of hook-injected env vars.
7bfddde to
ef318f0
Compare
| { | ||
| currentWorkingDirectory | ||
| currentWorkingDirectory, | ||
| environment: getCleanGitEnvironment() |
There was a problem hiding this comment.
Should we strip those env vars iff they point to nonexistent locations/locations without a rush.json?
Also note that AFAIK rush.json doesn't have to be at the repo root.
| { | ||
| "changes": [ | ||
| { | ||
| "comment": "Fix build cache failures when running inside a git linked worktree via a pre-commit hook, caused by GIT_DIR being set to the per-worktree metadata directory", |
There was a problem hiding this comment.
Generate another changelog (with type "none") for @microsoft/rush so we can do a Rush publish with this change.
| const env: NodeJS.ProcessEnv = { ...process.env }; | ||
| delete env.GIT_DIR; | ||
| delete env.GIT_WORK_TREE; | ||
| return env; |
There was a problem hiding this comment.
delete is an expensive operation.
| return env; | |
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | |
| const { GIT_DIR, GIT_WORK_TREE, ...trimmedEnv } = process.env; | |
| return trimmedEnv; |
| } | ||
| ], | ||
| "packageName": "@rushstack/package-deps-hash", | ||
| "email": "kfleischman@squarespace.com" |
There was a problem hiding this comment.
| "email": "kfleischman@squarespace.com" | |
| "email": "istateside@users.noreply.github.com" |
Summary
Fixes build cache failures when Rush commands that trigger incremental build analysis run inside a git linked worktree via a pre-commit hook.
Fixes #5479
When git invokes a pre-commit hook in a linked worktree, it sets
GIT_DIRto the per-worktree metadata directory (e.g..git/worktrees/{name}) but does not setGIT_WORK_TREE. Child processes inherit this environment.getRepoRoot()callsgit rev-parse --show-topleveland inherits thisGIT_DIR, causing git to return thecurrentWorkingDirectoryargument (e.g. therushJsonFoldersubdirectory) instead of the actual worktree root. All subsequent git calls use this wrong root, causinggit status -uto miss the top-level.gitignore, surfacingnode_modules/symlinks as untracked files, whichgit hash-objectcannot hash — ultimately reported as "Build cache is only supported if running in a Git repository."Details
The fix strips
GIT_DIRandGIT_WORK_TREEfrom the environment before spawning any git subprocess ingetRepoState.ts. This lets git auto-discover the correct repo root by scanning up fromcurrentWorkingDirectory, which correctly resolves to the linked worktree root regardless of the hook-injected environment. Three call sites are patched:getRepoRoot,spawnGitAsync(used bygetDetailedRepoStateAsyncandhashFilesAsync), andgetRepoChanges.This regression was introduced in #5500, which switched from
git ls-tree -r HEAD(reads committed objects, never surfacesnode_modules/) togit ls-files --cached+git status -u(scans the work tree, exposing the broken.gitignorecontext).The diff also includes prettier reformatting the
WINDOWS_RESERVED_BASENAMESarray from a compact multi-line form to one-entry-per-line — this is an unrelated side effect of the project's pre-commit hook. Happy to add a// prettier-ignorecomment to suppress it if preferred.How it was tested
Added a unit test to
getRepoDeps.test.tsthat setsGIT_DIRto a nonexistent path (simulating hook interference) and verifies thatgetRepoRootstill returns the correct repo root. Without the fix,git rev-parseexits 128 ("not a git repository") and the function throws; with the fix,GIT_DIRis stripped and git auto-discovers the root correctly.Also manually reproduced the original failure by running a Rush build command from within a git linked worktree via a pre-commit hook and confirmed the build cache error no longer occurs with this fix applied.
Reproduction
The bug can be reproduced with this shell script: https://gist.github.com/istateside/e8b0c5f694424a423ae29fe9203ec895
The script bootstraps a Rush monorepo with all of the requisite contributing factors to recreate the bug:
rush.jsonis not in the root directory of the git repo)In that environment, the bug is triggered if you are in a linked worktree and make a commit, to trigger the pre-commit rush command.