fix(cli): resolve WSL mounted-drive worktree paths

This commit is contained in:
Neil
2026-08-30 16:36:20 -07:00
committed by GitHub
parent a085c28e1b
commit 879fdfdac6
4 changed files with 67 additions and 3 deletions
+10 -3
View File
@@ -4,7 +4,11 @@ import type {
RuntimeWorktreeListResult,
RuntimeWorktreeRecord
} from '../shared/runtime-types'
import { isPathInsideOrEqual, isWslUncPathForCallerLinuxPath } from '../shared/cross-platform-path'
import {
isPathInsideOrEqual,
isWslUncPathForCallerLinuxPath,
isWslUncPathForLinuxMountedPath
} from '../shared/cross-platform-path'
import { parseWslUncPath } from '../shared/wsl-paths'
import type { RuntimeClient } from './runtime-client'
import { RuntimeClientError } from './runtime/types'
@@ -49,8 +53,9 @@ export async function resolveCallerDistroPathSelector(
// the WSL launcher always sets and which arrives here as the invocation cwd, proves it.
const callerDistro = parseWslUncPath(cwd)?.distro
const linuxPath = selector.startsWith('path:') ? selector.slice(5) : ''
const isLinuxMountedPath = /^\/mnt\/[A-Za-z](?:\/|$)/.test(linuxPath)
if (
!callerDistro ||
(!callerDistro && !isLinuxMountedPath) ||
client.isRemote ||
!linuxPath.startsWith('/') ||
linuxPath.startsWith('//') ||
@@ -63,7 +68,9 @@ export async function resolveCallerDistroPathSelector(
limit: 10_000
})
const match = worktrees.result.worktrees.find((worktree) =>
isWslUncPathForCallerLinuxPath(worktree.path, linuxPath, callerDistro)
isLinuxMountedPath
? isWslUncPathForLinuxMountedPath(worktree.path, linuxPath)
: isWslUncPathForCallerLinuxPath(worktree.path, linuxPath, callerDistro!)
)
// Why the stored spelling rather than a synthesized UNC path: an unmatched selector must
// reach the runtime verbatim and fail as the caller typed it, never as a guessed distro.
@@ -11,6 +11,7 @@ import { normalizeWorktreeSelectorForCaller } from './selectors'
const UBUNTU = 'Ubuntu-24.04'
const DEBIAN = 'Debian'
const LINUX_PATH = '/home/neil/qa-repo'
const MOUNTED_PATH = '/mnt/c/Users/neil/qa-repo'
function uncPath(distro: string, linuxPath: string): string {
return `\\\\wsl.localhost\\${distro}${linuxPath.replace(/\//g, '\\')}`
@@ -37,6 +38,15 @@ afterEach(() => {
})
describe('normalizeWorktreeSelectorForCaller in a WSL shell (#16628)', () => {
it('resolves a shared /mnt drive without requiring the caller distro', async () => {
const storedPath = uncPath(UBUNTU, '/mnt/c/Users/neil/qa-repo')
const { client } = makeClient([storedPath])
await expect(
normalizeWorktreeSelectorForCaller(`path:${MOUNTED_PATH}`, '/mnt/c/Users/neil', client)
).resolves.toBe(`path:${storedPath}`)
})
it.each([
['a backslash UNC registration', uncPath(UBUNTU, LINUX_PATH)],
['a forward-slash UNC registration', `//wsl.localhost/${UBUNTU}${LINUX_PATH}`],
+27
View File
@@ -5,6 +5,7 @@ import {
isPathInsideOrEqual,
isRuntimePathAbsolute,
isWslUncPathForCallerLinuxPath,
isWslUncPathForLinuxMountedPath,
normalizeRuntimePathForComparison,
relativePathInsideRoot,
resolveRuntimePath
@@ -99,6 +100,32 @@ describe('isWslUncPathForCallerLinuxPath', () => {
})
})
describe('isWslUncPathForLinuxMountedPath', () => {
it('matches a shared /mnt drive regardless of distro', () => {
expect(
isWslUncPathForLinuxMountedPath(
'\\\\wsl.localhost\\Ubuntu\\mnt\\c\\Users\\Neil\\repo',
'/mnt/c/users/neil/repo'
)
).toBe(true)
expect(
isWslUncPathForLinuxMountedPath(
'\\\\wsl.localhost\\Debian\\mnt\\c\\Users\\Neil\\repo',
'/mnt/c/users/neil/repo'
)
).toBe(true)
})
it('keeps non-mounted Linux paths out of the distro-independent match', () => {
expect(
isWslUncPathForLinuxMountedPath(
'\\\\wsl.localhost\\Ubuntu\\home\\Neil\\repo',
'/home/neil/repo'
)
).toBe(false)
})
})
describe('cross-platform path containment', () => {
it('keeps POSIX sibling prefixes outside the root', () => {
expect(isPathInsideOrEqual('/repo/app', '/repo/app')).toBe(true)
+20
View File
@@ -81,6 +81,26 @@ export function isWslUncPathForCallerLinuxPath(
)
}
/**
* Whether a WSL UNC path fronts the same Windows-mounted `/mnt/<drive>` path.
*
* `/mnt/<drive>` is backed by the host drive and is shared across distros, so
* matching it does not require the caller's distro proof used for Linux paths.
*/
export function isWslUncPathForLinuxMountedPath(uncPath: string, linuxPath: string): boolean {
const parsed = parseWslUncPath(uncPath)
if (!parsed || !/^\/mnt\/[A-Za-z](?:\/|$)/.test(parsed.linuxPath)) {
return false
}
if (!/^\/mnt\/[A-Za-z](?:\/|$)/.test(linuxPath)) {
return false
}
return (
normalizeRuntimePathForComparison(toWindowsWslPath(parsed.linuxPath, parsed.distro)) ===
normalizeRuntimePathForComparison(toWindowsWslPath(linuxPath, parsed.distro))
)
}
export function areLocalWindowsWslPathAliases(left: string, right: string): boolean {
const leftIdentity = getLocalWindowsWslPathIdentity(left)
const rightIdentity = getLocalWindowsWslPathIdentity(right)