Fix apphost path case sensitivity mismatch in VS Code extension#15866
Fix apphost path case sensitivity mismatch in VS Code extension#15866adamint wants to merge 2 commits intomicrosoft:mainfrom
Conversation
Use fs.realpathSync.native() to normalize apphost paths to their canonical on-disk casing before passing them to the CLI via --apphost. This prevents case mismatches on case-insensitive file systems (macOS, Windows) that cause the CLI to fail to find running AppHosts. Fixes microsoft#15588
There was a problem hiding this comment.
Pull request overview
This PR fixes VS Code extension failures to locate running AppHosts on case-insensitive file systems by normalizing AppHost paths to their canonical on-disk casing before storing/passing them to the Aspire CLI.
Changes:
- Add
resolveCanonicalPath()utility usingfs.realpathSync.native()with fallback behavior. - Canonicalize AppHost paths sourced from config files, the active editor, and
aspire extension get-apphostsoutput. - Add unit tests covering canonicalization behavior (existing paths, wrong casing, symlinks, directories, and non-existent paths).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| extension/src/views/AppHostDataRepository.ts | Canonicalizes workspace AppHost path derived from aspire extension get-apphosts. |
| extension/src/utils/io.ts | Adds resolveCanonicalPath() helper using native realpath. |
| extension/src/test/io.test.ts | Adds tests validating canonical path resolution behavior across scenarios/platforms. |
| extension/src/editor/AspireEditorCommandProvider.ts | Canonicalizes AppHost paths sourced from config and active editor. |
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15866Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15866" |
| */ | ||
| export function resolveCanonicalPath(p: string): string { | ||
| try { | ||
| return realpathSync.native(p); |
There was a problem hiding this comment.
What happens if the AppHost was started from a symlinked path (for example a symlinked repo root, or a config entry that points through a symlink)? realpathSync.native() does more than fix casing here: it dereferences the symlink and changes the literal path we send back via --apphost.
That breaks the current CLI contract because the server side still keys AppHost state off Path.GetFullPath(appPath) rather than a realpath/canonical target (DotNetBasedAppHostServerProject and PrebuiltAppHostServer both hash the full path string, and AuxiliaryBackchannelMonitor scope checks also use Path.GetFullPath). Path.GetFullPath preserves the symlink text, so a running AppHost started as /repo-link/AppHost.csproj is distinct from /actual/repo/AppHost.csproj. After this change, restart/stop/logs can fail to find an already-running AppHost again, just under a different path variant.
This is a regression from the old behavior, which forwarded the configured/opened path unchanged. The fix direction seems to be: normalize casing without resolving symlinks, or make the CLI canonicalize AppHost identity the same way on both sides.
Description
Fix apphost path case sensitivity mismatch in the VS Code extension that prevents resource commands (restart, stop, logs) from finding running AppHosts on case-insensitive file systems (macOS, Windows).
Root Cause
The extension obtains the apphost path from multiple sources (config files, CLI output, active editor) that can produce different casing for the same file path. On macOS (case-insensitive APFS), VS Code's
Uri.fsPathpreserves the workspace URI's casing rather than the canonical on-disk casing. When this differs from the casing the running AppHost used, the CLI's hash-based socket lookup fails because different casing produces different hashes.Fix
Use
fs.realpathSync.native()to resolve all apphost paths to their canonical on-disk casing before storing or passing them to the CLI via--apphost. The native variant (unlike the defaultrealpathSync) uses the OS's nativerealpath(3)which returns the actual on-disk casing on case-insensitive file systems.Three normalization points:
AspireEditorCommandProvider.ts— config-file based path resolutionAspireEditorCommandProvider.ts—getAppHostPath()active editor pathAppHostDataRepository.ts— CLI-based path fromaspire extension get-apphostsFalls back to the original path if the file doesn't exist yet (e.g., during initial project setup).
Fixes #15588
Checklist