fix: resolve symlinks in workspace path normalization#1235
Conversation
normalizeWorkspaceId used path.resolve() which does not follow symlinks, causing workspace lookup failures when --cwd uses a symlink path that differs from the real path stored in workspace records. Add realpathSync to normalize both input and stored paths during comparison.
|
| Filename | Overview |
|---|---|
| packages/server/src/server/workspace-registry-model.ts | Core fix: adds realpathSync after path.resolve in normalizeWorkspaceId, with a try/catch fallback for non-existent paths. The logic is correct — empty/whitespace-only strings are guarded before any FS calls. |
| packages/server/src/server/workspace-directory.ts | Both the exact-match and prefix-match branches now normalize workspace.cwd via normalizeWorkspaceId. The home-directory guard is also resolved through normalizeWorkspaceId(homedir()), fixing the asymmetry noted in a prior review thread. The bestMatch refactor to carry the pre-computed normalizedCwd length is correct. |
| packages/server/src/server/session.ts | findExactWorkspaceByDirectory now normalizes workspace.cwd before comparing it against normalizedCwd. Both sides are consistently resolved through normalizePersistedWorkspaceId (an alias for normalizeWorkspaceId). |
| packages/server/src/server/workspace-registry-model.test.ts | Four new tests cover the symlink-resolution, no-symlink, non-existent path, and empty-string cases. afterEach is used but not explicitly imported (works via globals: true, but inconsistent with the rest of the file's explicit imports). |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Input: cwd (may be symlink)"] --> B["normalizeWorkspaceId(cwd)"]
B --> C["cwd.trim() → empty?"]
C -- "yes" --> D["return cwd unchanged"]
C -- "no" --> E["path.resolve(trimmed) → absolute path"]
E --> F["realpathSync(resolved)"]
F -- "success" --> G["return real path (symlinks resolved)"]
F -- "throws (non-existent)" --> H["return resolved (no-symlink fallback)"]
G --> I["Comparison sites"]
H --> I
I --> J["workspace-directory.ts\nresolveRegisteredWorkspaceIdForCwd"]
I --> K["session.ts\nfindExactWorkspaceByDirectory"]
I --> L["workspace-directory.ts\nhomedir guard"]
Reviews (2): Last reviewed commit: "fix: correct realpathSync casing and nor..." | Re-trigger Greptile
- Fix realPathSync (capital P) to realpathSync in test — would cause ReferenceError at runtime - Normalize homedir() through normalizeWorkspaceId for consistent symlink comparison in prefix match
|
CI workflows are waiting for approval (status: |
Summary
normalizeWorkspaceIdusedpath.resolve()which does not follow symlinks--cwduses a symlink path (e.g./data/app→/data1/app), workspace lookup fails because stored paths are real paths but inputpaths are symlink paths
fs.realpathSynctonormalizeWorkspaceId, and normalize both input and stored paths in all comparison sitesChanges
workspace-registry-model.ts: AddrealpathSyncwithtry/catchfallback tonormalizeWorkspaceIdworkspace-directory.ts: Normalize storedworkspace.cwdin both exact match and prefix match paths ofresolveRegisteredWorkspaceIdForCwdsession.ts: Normalize storedworkspace.cwdinfindExactWorkspaceByDirectoryworkspace-registry-model.test.ts: Add symlink resolution tests