mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
perf(pty): check WSL working directories off the main thread (#27705)
* fix(wsl): start host-side wsl.exe probes through process-host so they never open a console wsl --list, wsl --status, the WSL home and directory probes, and WSL UNC deletes were started with node:child_process directly. The synchronous ones omitted windowsHide, so each spawn from the GUI-subsystem main process flashed a conhost window that could take keyboard focus. Route them through @orca/process-host, which pins windowsHide, and keep the execFile-shaped exit errors the availability cache classifies. Refs #27390 * build(tc): list wsl-exe-process.ts in the web typecheck project wsl-availability.ts is in the explicit web file list and now imports it. * refactor(wsl): drop the unused sync availability probe; classify ProcessResult directly No production code calls the sync isWslAvailable (the IPC and RPC paths already assert they never do), so delete it and its sync guest-probe twin instead of porting them. The async probe now reads the --status ProcessResult itself: an exit code is definitive, a timeout or signal kill is retryable, ENOENT on spawn is definitive. The WSL2-missing-kernel exit codes live in one named set. Availability tests now drive the async probe. * refactor(wsl): shrink wsl-exe-process to a spec builder and stdout reader With the availability cache classifying ProcessResult itself, nothing reads the execFile-shaped code/status error any more. wslExeFailure returns a plain reason string; wslExeStdout throws it; the UNC delete folds it into its own error. The home probe builds its argv with buildWslExecArgs. * perf(pty): probe WSL working directories off the main thread The in-process terminal launch path validated a \wsl.localhost cwd with a synchronous wsl.exe probe, blocking the main process ~70 ms per launch (300-530 ms when the distro is stopped, up to the 5 s timeout). The launch now awaits the existing async probe and the synchronous one is removed. Refs #20497 * fix(pty): let a canceled spawn abandon its pending WSL cwd probe The local spawn awaited the WSL directory probe without its cancellation signal, so a pane closed mid-probe waited up to the 5s wsl.exe timeout before noticing. Pass the signal through validateWorkingDirectoryAsync and report an abandoned probe as the spawn's own cancellation, as the daemon spawn preflight does. * test(wsl): mock process-host in the empty running-list case from main * docs(wsl): the availability probe no longer blocks; drop the stale word * build(ratchet): prune the wsl.exe sync launches this branch removed * build(ratchet): prune the sync WSL cwd probe this branch moved off the main thread --------- Co-authored-by: Orca Worker <orca-worker@localhost>
This commit is contained in:
@@ -53,7 +53,6 @@
|
||||
1 src/main/plugins/plugin-private-marketplace-ssh-shim.cjs
|
||||
2 src/main/plugins/plugin-secrets-store.ts
|
||||
2 src/main/plugins/plugin-storage-store.ts
|
||||
1 src/main/providers/working-directory-validation.ts
|
||||
1 src/main/pty/posix-pty-foreground-group.ts
|
||||
1 src/main/pty/posix-pty-process-groups.ts
|
||||
1 src/main/pwsh.ts
|
||||
@@ -72,7 +71,7 @@
|
||||
1 src/main/startup/linux-secret-service-probe.ts
|
||||
2 src/main/win32-utils.ts
|
||||
1 src/main/worktree-runner-script.ts
|
||||
4 src/main/wsl.ts
|
||||
3 src/main/wsl.ts
|
||||
3 src/main/zcode/zcode-plan-api-key-store.ts
|
||||
2 src/main/zsh-startup-hook-pty-harness.ts
|
||||
1 src/shared/bounded-secure-json-file.ts
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { withFreshOmpLaunch } from '../../shared/omp-fresh-launch'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { piBuildPtyEnvMock, spawnMock } from './pty-ipc-mock-registry'
|
||||
import {
|
||||
piBuildPtyEnvMock,
|
||||
spawnMock,
|
||||
wslUncDirectoryExistsAsyncMock
|
||||
} from './pty-ipc-mock-registry'
|
||||
import { TEST_CODEX_HOME } from './pty-ipc-test-constants'
|
||||
import { setupPtyIpcSuite } from './pty-ipc-test-harness'
|
||||
import { LocalPtyProvider } from '../providers/local-pty-provider'
|
||||
@@ -307,6 +311,7 @@ describe('registerPtyHandlers', () => {
|
||||
})
|
||||
}))
|
||||
__resetPersistedWindowsPathCacheForTests()
|
||||
wslUncDirectoryExistsAsyncMock.mockResolvedValue(true)
|
||||
|
||||
try {
|
||||
const provider = new LocalPtyProvider({
|
||||
|
||||
@@ -96,7 +96,7 @@ async function resolveWorkspaceCleanupActivityAt(
|
||||
}
|
||||
|
||||
// Why: best-effort only. Win32 stat over \\wsl.localhost (9P) can falsely
|
||||
// report ENOENT (see wslUncDirectoryExists), so a failed stat degrades to the
|
||||
// report ENOENT (see wslUncDirectoryExistsAsync), so a failed stat degrades to the
|
||||
// persisted activity timestamp instead of blocking or mislabeling the row.
|
||||
async function getNewestLocalWorktreeActivityAt(
|
||||
worktreePath: string,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from './local-pty-launch-helpers'
|
||||
import type { LocalPtyProviderOptions } from './local-pty-provider-types'
|
||||
import type { getShellLaunchConfig } from './local-pty-shell-ready'
|
||||
import { ensureNodePtySpawnHelperExecutable, validateWorkingDirectory } from './local-pty-utils'
|
||||
import { ensureNodePtySpawnHelperExecutable } from './local-pty-utils'
|
||||
import {
|
||||
resolveEffectiveWindowsPowerShell,
|
||||
shouldProbeWindowsPowerShellAvailability,
|
||||
@@ -77,9 +77,6 @@ function finalizeLocalPtyLaunchPlan(
|
||||
}
|
||||
): LocalPtyLaunchPlan {
|
||||
ensureNodePtySpawnHelperExecutable()
|
||||
if (seed.args.prevalidatedCwd !== shell.validationCwd) {
|
||||
validateWorkingDirectory(shell.validationCwd)
|
||||
}
|
||||
const isWslShell =
|
||||
Boolean(seed.wslInfo) || pathWin32.basename(shell.shellPath).toLowerCase() === 'wsl.exe'
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -141,7 +141,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -141,7 +141,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -141,7 +141,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -142,7 +142,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -146,7 +146,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -142,7 +142,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -29,7 +29,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -109,7 +109,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -143,7 +143,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -148,7 +148,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export type LocalPtyProviderMocks = {
|
||||
readWindowsPtyJobProcessIdsMock: Mock
|
||||
killWithDescendantSweepMock: Mock
|
||||
isWslAvailableAsyncMock: Mock
|
||||
wslUncDirectoryExistsMock: Mock
|
||||
wslUncDirectoryExistsAsyncMock: Mock
|
||||
createShellPromptReadinessProbeMock: Mock
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ export function applyLocalPtyProviderMockDefaults(mocks: LocalPtyProviderMocks):
|
||||
mocks.readWindowsPtyJobProcessIdsMock.mockReturnValue(null)
|
||||
mocks.isWslAvailableAsyncMock.mockReset()
|
||||
mocks.isWslAvailableAsyncMock.mockResolvedValue(true)
|
||||
mocks.wslUncDirectoryExistsMock.mockReset()
|
||||
mocks.wslUncDirectoryExistsMock.mockReturnValue(true)
|
||||
mocks.wslUncDirectoryExistsAsyncMock.mockReset()
|
||||
mocks.wslUncDirectoryExistsAsyncMock.mockResolvedValue(true)
|
||||
mocks.createShellPromptReadinessProbeMock.mockReset()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
@@ -27,7 +27,7 @@ const {
|
||||
readWindowsPtyJobProcessIdsMock: vi.fn(),
|
||||
killWithDescendantSweepMock: vi.fn(),
|
||||
isWslAvailableAsyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
createShellPromptReadinessProbeMock: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -108,7 +108,7 @@ vi.mock('../wsl', () => ({
|
||||
isWslAvailableAsync: () => isWslAvailableAsyncMock(),
|
||||
// Why: WSL worktree validation now asks the distro; these tests use WSL UNC
|
||||
// cwds that are meant to exist, so report them present without spawning wsl.exe.
|
||||
wslUncDirectoryExists: (...args: unknown[]) => wslUncDirectoryExistsMock(...args)
|
||||
wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args)
|
||||
}))
|
||||
|
||||
vi.mock('../shell-prompt-readiness-probe', () => ({
|
||||
@@ -143,7 +143,7 @@ describe('LocalPtyProvider', () => {
|
||||
readWindowsPtyJobProcessIdsMock,
|
||||
killWithDescendantSweepMock,
|
||||
isWslAvailableAsyncMock,
|
||||
wslUncDirectoryExistsMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
createShellPromptReadinessProbeMock
|
||||
})
|
||||
|
||||
@@ -358,7 +358,7 @@ describe('LocalPtyProvider', () => {
|
||||
expect.stringContaining(`cd '${cwd}'`)
|
||||
])
|
||||
expect(spawnCall[2].cwd).not.toBe(cwd)
|
||||
expect(wslUncDirectoryExistsMock).not.toHaveBeenCalled()
|
||||
expect(wslUncDirectoryExistsAsyncMock).not.toHaveBeenCalled()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -375,7 +375,50 @@ describe('LocalPtyProvider', () => {
|
||||
terminalWindowsWslDistro: 'Debian'
|
||||
})
|
||||
|
||||
expect(wslUncDirectoryExistsMock).toHaveBeenCalledWith('\\\\wsl.localhost\\Debian\\a')
|
||||
expect(wslUncDirectoryExistsAsyncMock).toHaveBeenCalledWith('\\\\wsl.localhost\\Debian\\a')
|
||||
})
|
||||
|
||||
it('rejects a WSL cwd the distro reports missing without spawning a shell', async () => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
|
||||
wslUncDirectoryExistsAsyncMock.mockResolvedValue(false)
|
||||
const spawnCallsBefore = spawnMock.mock.calls.length
|
||||
|
||||
await expect(
|
||||
provider.spawn({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
cwd: '\\\\wsl.localhost\\Debian\\home\\jin\\gone'
|
||||
})
|
||||
).rejects.toThrow(/does not exist/)
|
||||
expect(wslUncDirectoryExistsAsyncMock).toHaveBeenCalledWith(
|
||||
'\\\\wsl.localhost\\Debian\\home\\jin\\gone'
|
||||
)
|
||||
expect(spawnMock.mock.calls).toHaveLength(spawnCallsBefore)
|
||||
})
|
||||
|
||||
it('rejects a canceled spawn while its WSL cwd probe is still pending', async () => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
|
||||
const probeStarted = Promise.withResolvers<void>()
|
||||
wslUncDirectoryExistsAsyncMock.mockImplementation(() => {
|
||||
probeStarted.resolve()
|
||||
return new Promise(() => {})
|
||||
})
|
||||
const spawnCallsBefore = spawnMock.mock.calls.length
|
||||
|
||||
const spawn = provider.spawn({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
cwd: '\\\\wsl.localhost\\Stalled\\home\\jin\\repo',
|
||||
sessionId: 'stalled-wsl-cwd-session'
|
||||
})
|
||||
const canceledSpawn = expect(spawn).rejects.toThrow(
|
||||
'PTY spawn canceled: stalled-wsl-cwd-session'
|
||||
)
|
||||
await probeStarted.promise
|
||||
await provider.shutdown('stalled-wsl-cwd-session', { immediate: true })
|
||||
|
||||
await canceledSpawn
|
||||
expect(spawnMock.mock.calls).toHaveLength(spawnCallsBefore)
|
||||
})
|
||||
|
||||
it('resolves and persists the default distro for Windows cwd WSL terminals', async () => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { finalizeLocalPtySpawnEnvironment } from './local-pty-finalize-environme
|
||||
import { normalizeLocalCallerSessionId } from './local-pty-launch-helpers'
|
||||
import { createLocalPtyLaunchPlan, DeferredLocalPtyLaunchPlan } from './local-pty-launch-plan'
|
||||
import type { LocalPtyProviderOptions } from './local-pty-provider-types'
|
||||
import { validateWorkingDirectory, WorkingDirectoryValidationAbortedError } from './local-pty-utils'
|
||||
import { allocatePtyId, ptyShutdownOperations } from './local-pty-provider-state'
|
||||
import { activateLocalPtySession } from './local-pty-session-activation'
|
||||
import {
|
||||
@@ -68,6 +69,18 @@ export async function spawnLocalPty(
|
||||
plan = plan.finish(available)
|
||||
}
|
||||
throwIfCanceled()
|
||||
if (args.prevalidatedCwd !== plan.validationCwd) {
|
||||
try {
|
||||
await validateWorkingDirectory(plan.validationCwd, cancellation)
|
||||
} catch (error) {
|
||||
// Why: an abandoned probe keeps running for other waiters; report the spawn's own cancel.
|
||||
if (error instanceof WorkingDirectoryValidationAbortedError) {
|
||||
throwIfCanceled()
|
||||
}
|
||||
throw error
|
||||
}
|
||||
throwIfCanceled()
|
||||
}
|
||||
const envResult = buildLocalPtySpawnEnvironment({
|
||||
id,
|
||||
spawn: args,
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type * as fs from 'node:fs'
|
||||
import type * as FsPromises from 'node:fs/promises'
|
||||
import type { Stats } from 'node:fs'
|
||||
|
||||
const { existsSyncMock, statSyncMock, accessSyncMock, wslUncDirectoryExistsMock, wrapSpawnMock } =
|
||||
vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
statSyncMock: vi.fn(),
|
||||
accessSyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsMock: vi.fn(),
|
||||
wrapSpawnMock: vi.fn()
|
||||
}))
|
||||
const {
|
||||
existsSyncMock,
|
||||
statSyncMock,
|
||||
statMock,
|
||||
accessSyncMock,
|
||||
wslUncDirectoryExistsAsyncMock,
|
||||
wrapSpawnMock
|
||||
} = vi.hoisted(() => ({
|
||||
existsSyncMock: vi.fn(),
|
||||
statSyncMock: vi.fn(),
|
||||
statMock: vi.fn(),
|
||||
accessSyncMock: vi.fn(),
|
||||
wslUncDirectoryExistsAsyncMock: vi.fn(),
|
||||
wrapSpawnMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('fs', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof fs>()
|
||||
@@ -21,12 +29,17 @@ vi.mock('fs', async (importOriginal) => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('node:fs/promises', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof FsPromises>()),
|
||||
stat: statMock
|
||||
}))
|
||||
|
||||
function dirStats(isDirectory: boolean): Stats {
|
||||
return { isDirectory: () => isDirectory } as Stats
|
||||
}
|
||||
|
||||
vi.mock('../wsl', () => ({
|
||||
wslUncDirectoryExists: wslUncDirectoryExistsMock
|
||||
wslUncDirectoryExistsAsync: wslUncDirectoryExistsAsyncMock
|
||||
}))
|
||||
|
||||
vi.mock('./macos-tcc-login-shell', () => ({
|
||||
@@ -39,6 +52,7 @@ import {
|
||||
spawnShellWithFallback,
|
||||
validateWorkingDirectory
|
||||
} from './local-pty-utils'
|
||||
import { _resetWorkingDirectoryValidationStateForTest } from './working-directory-validation'
|
||||
import { getFishXdgDataDirsLaunchEnv } from '../fish-xdg-data-dirs-handoff'
|
||||
|
||||
const WSL_UNC_DIR = '\\\\wsl.localhost\\Ubuntu\\home\\jin\\repo'
|
||||
@@ -48,61 +62,81 @@ describe('validateWorkingDirectory', () => {
|
||||
beforeEach(() => {
|
||||
existsSyncMock.mockReset()
|
||||
statSyncMock.mockReset()
|
||||
wslUncDirectoryExistsMock.mockReset()
|
||||
statMock.mockReset()
|
||||
wslUncDirectoryExistsAsyncMock.mockReset()
|
||||
_resetWorkingDirectoryValidationStateForTest()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('accepts a WSL UNC worktree that exists inside the distro even when fs.statSync would fail', () => {
|
||||
it('accepts a WSL UNC worktree that exists inside the distro even when fs.statSync would fail', async () => {
|
||||
// Why: the Win32 9P stat is the exact path that falsely reported ENOENT and
|
||||
// broke opening WSL worktrees. The distro answer must win.
|
||||
wslUncDirectoryExistsMock.mockReturnValue(true)
|
||||
wslUncDirectoryExistsAsyncMock.mockResolvedValue(true)
|
||||
existsSyncMock.mockReturnValue(false)
|
||||
|
||||
expect(() => validateWorkingDirectory(WSL_UNC_DIR)).not.toThrow()
|
||||
expect(wslUncDirectoryExistsMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
await expect(validateWorkingDirectory(WSL_UNC_DIR)).resolves.toBeUndefined()
|
||||
expect(wslUncDirectoryExistsAsyncMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
// The fs fallback must not run when the distro confirmed existence.
|
||||
expect(existsSyncMock).not.toHaveBeenCalled()
|
||||
expect(statMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects a WSL UNC worktree that does not exist inside the distro', () => {
|
||||
wslUncDirectoryExistsMock.mockReturnValue(false)
|
||||
it('returns to the event loop while the distro probe is still running', async () => {
|
||||
const probe = Promise.withResolvers<boolean | null>()
|
||||
wslUncDirectoryExistsAsyncMock.mockReturnValue(probe.promise)
|
||||
let settled = false
|
||||
|
||||
expect(() => validateWorkingDirectory(WSL_UNC_DIR)).toThrow(/does not exist/)
|
||||
const validation = validateWorkingDirectory(WSL_UNC_DIR).finally(() => {
|
||||
settled = true
|
||||
})
|
||||
await new Promise((resolve) => setImmediate(resolve))
|
||||
expect(settled).toBe(false)
|
||||
|
||||
probe.resolve(true)
|
||||
await validation
|
||||
expect(settled).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects a WSL UNC worktree that does not exist inside the distro', async () => {
|
||||
wslUncDirectoryExistsAsyncMock.mockResolvedValue(false)
|
||||
|
||||
await expect(validateWorkingDirectory(WSL_UNC_DIR)).rejects.toThrow(/does not exist/)
|
||||
expect(existsSyncMock).not.toHaveBeenCalled()
|
||||
expect(statMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('falls back to the fs check when the distro answer is inconclusive', async () => {
|
||||
wslUncDirectoryExistsAsyncMock.mockResolvedValue(null)
|
||||
statMock.mockResolvedValue(dirStats(true))
|
||||
|
||||
await expect(validateWorkingDirectory(WSL_UNC_DIR)).resolves.toBeUndefined()
|
||||
expect(wslUncDirectoryExistsAsyncMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
expect(statMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
expect(existsSyncMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('falls back to the fs check when the distro answer is inconclusive', () => {
|
||||
wslUncDirectoryExistsMock.mockReturnValue(null)
|
||||
it('validates native Windows paths via fs without consulting the distro', async () => {
|
||||
existsSyncMock.mockReturnValue(true)
|
||||
statSyncMock.mockReturnValue(dirStats(true))
|
||||
|
||||
expect(() => validateWorkingDirectory(WSL_UNC_DIR)).not.toThrow()
|
||||
expect(wslUncDirectoryExistsMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
expect(existsSyncMock).toHaveBeenCalledWith(WSL_UNC_DIR)
|
||||
})
|
||||
|
||||
it('validates native Windows paths via fs without consulting the distro', () => {
|
||||
existsSyncMock.mockReturnValue(true)
|
||||
statSyncMock.mockReturnValue(dirStats(true))
|
||||
|
||||
expect(() => validateWorkingDirectory(NATIVE_DIR)).not.toThrow()
|
||||
expect(wslUncDirectoryExistsMock).not.toHaveBeenCalled()
|
||||
await expect(validateWorkingDirectory(NATIVE_DIR)).resolves.toBeUndefined()
|
||||
expect(wslUncDirectoryExistsAsyncMock).not.toHaveBeenCalled()
|
||||
expect(existsSyncMock).toHaveBeenCalledWith(NATIVE_DIR)
|
||||
})
|
||||
|
||||
it('rejects a missing native Windows path', () => {
|
||||
it('rejects a missing native Windows path', async () => {
|
||||
existsSyncMock.mockReturnValue(false)
|
||||
const previousVersion = process.env.ORCA_APP_VERSION
|
||||
process.env.ORCA_APP_VERSION = '1.4.178-test'
|
||||
|
||||
try {
|
||||
expect(() => validateWorkingDirectory(NATIVE_DIR)).toThrow(
|
||||
await expect(validateWorkingDirectory(NATIVE_DIR)).rejects.toThrow(
|
||||
/does not exist.*orca: 1\.4\.178-test/
|
||||
)
|
||||
expect(wslUncDirectoryExistsMock).not.toHaveBeenCalled()
|
||||
expect(wslUncDirectoryExistsAsyncMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
if (previousVersion === undefined) {
|
||||
delete process.env.ORCA_APP_VERSION
|
||||
@@ -112,11 +146,11 @@ describe('validateWorkingDirectory', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('rejects a native Windows path that exists but is not a directory', () => {
|
||||
it('rejects a native Windows path that exists but is not a directory', async () => {
|
||||
existsSyncMock.mockReturnValue(true)
|
||||
statSyncMock.mockReturnValue(dirStats(false))
|
||||
|
||||
expect(() => validateWorkingDirectory(NATIVE_DIR)).toThrow(/is not a directory/)
|
||||
await expect(validateWorkingDirectory(NATIVE_DIR)).rejects.toThrow(/is not a directory/)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ const { stat } = vi.hoisted(() => ({
|
||||
}))
|
||||
vi.mock('node:fs/promises', () => ({ stat }))
|
||||
vi.mock('../wsl', () => ({
|
||||
wslUncDirectoryExists: () => {
|
||||
throw new Error('Unexpected WSL probe')
|
||||
},
|
||||
wslUncDirectoryExistsAsync: () => {
|
||||
throw new Error('Unexpected WSL probe')
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { existsSync, statSync } from 'node:fs'
|
||||
import { stat } from 'node:fs/promises'
|
||||
import { release } from 'node:os'
|
||||
import { isWslUncPath, uncRouteKey } from '../../shared/wsl-paths'
|
||||
import { wslUncDirectoryExists, wslUncDirectoryExistsAsync } from '../wsl'
|
||||
import { wslUncDirectoryExistsAsync } from '../wsl'
|
||||
import { PrioritySemaphore } from '../../shared/priority-semaphore'
|
||||
|
||||
const pendingWorkingDirectoryValidations = new Map<string, Promise<void>>()
|
||||
@@ -89,20 +89,13 @@ function throwMissingWorkingDirectory(cwd: string): never {
|
||||
|
||||
/**
|
||||
* Validate that a working directory exists and is a directory.
|
||||
* Throws a descriptive Error if not.
|
||||
* Rejects with a descriptive Error if not.
|
||||
*/
|
||||
export function validateWorkingDirectory(cwd: string): void {
|
||||
// Why: Win32 fs.statSync against the WSL 9P share (\\wsl.localhost\...) can
|
||||
// falsely report ENOENT for directories that exist on the Linux side. Ask the
|
||||
// distro itself; only fall back to the fs check when wsl.exe is inconclusive.
|
||||
export async function validateWorkingDirectory(cwd: string, signal?: AbortSignal): Promise<void> {
|
||||
// Why: the distro probe spawns wsl.exe (~70 ms warm, 300+ ms for a stopped
|
||||
// distro), so it must never run synchronously on the main process.
|
||||
if (isWslUncPath(cwd)) {
|
||||
const existsInDistro = wslUncDirectoryExists(cwd)
|
||||
if (existsInDistro === false) {
|
||||
throwMissingWorkingDirectory(cwd)
|
||||
}
|
||||
if (existsInDistro === true) {
|
||||
return
|
||||
}
|
||||
return validateWorkingDirectoryAsync(cwd, { signal })
|
||||
}
|
||||
|
||||
if (!existsSync(cwd)) {
|
||||
@@ -155,6 +148,9 @@ function validateWorkingDirectoryUncached(cwd: string): Promise<void> {
|
||||
}
|
||||
|
||||
async function probeWorkingDirectory(cwd: string): Promise<void> {
|
||||
// Why: Win32 stat against the WSL 9P share (\\wsl.localhost\...) can falsely
|
||||
// report ENOENT for directories that exist on the Linux side. Ask the distro
|
||||
// itself; only fall back to the fs check when wsl.exe is inconclusive.
|
||||
if (isWslUncPath(cwd)) {
|
||||
const existsInDistro = await wslUncDirectoryExistsAsync(cwd)
|
||||
if (existsInDistro === false) {
|
||||
|
||||
@@ -41,7 +41,6 @@ import {
|
||||
parseWslPath,
|
||||
toLinuxPath,
|
||||
toWindowsWslPath,
|
||||
wslUncDirectoryExists,
|
||||
wslUncDirectoryExistsAsync
|
||||
} from './wsl'
|
||||
|
||||
@@ -608,60 +607,6 @@ describe('wsl path helpers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('wslUncDirectoryExists', () => {
|
||||
afterEach(() => {
|
||||
runProcessSyncMock.mockReset()
|
||||
})
|
||||
|
||||
it('returns true when the distro reports the directory exists', () => {
|
||||
runProcessSyncMock.mockReturnValue(exited('__ORCA_DIRECTORY_EXISTS__'))
|
||||
const result = withPlatform('win32', () =>
|
||||
wslUncDirectoryExists('\\\\wsl.localhost\\Ubuntu\\home\\jin\\repo')
|
||||
)
|
||||
expect(result).toBe(true)
|
||||
expect(runProcessSyncMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
program: 'wsl.exe',
|
||||
timeoutMs: 5000,
|
||||
args: [
|
||||
'-d',
|
||||
'Ubuntu',
|
||||
'--exec',
|
||||
'sh',
|
||||
'-c',
|
||||
expect.stringContaining('__ORCA_DIRECTORY_EXISTS__'),
|
||||
'sh',
|
||||
'/home/jin/repo'
|
||||
]
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false when the guest reports the directory missing', () => {
|
||||
runProcessSyncMock.mockReturnValue(exited('__ORCA_DIRECTORY_MISSING__'))
|
||||
const result = withPlatform('win32', () =>
|
||||
wslUncDirectoryExists('\\\\wsl.localhost\\Ubuntu\\home\\jin\\missing')
|
||||
)
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
it('returns null when wsl.exe or the distro is unavailable', () => {
|
||||
runProcessSyncMock.mockReturnValue(exited('', 4294967295))
|
||||
const result = withPlatform('win32', () =>
|
||||
wslUncDirectoryExists('\\\\wsl.localhost\\Ubuntu\\home\\jin\\repo')
|
||||
)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for non-WSL paths and off Windows', () => {
|
||||
expect(withPlatform('win32', () => wslUncDirectoryExists('C:\\Users\\jin\\repo'))).toBeNull()
|
||||
expect(
|
||||
withPlatform('linux', () => wslUncDirectoryExists('\\\\wsl.localhost\\Ubuntu\\home\\jin'))
|
||||
).toBeNull()
|
||||
expect(runProcessSyncMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('wslUncDirectoryExistsAsync', () => {
|
||||
afterEach(() => {
|
||||
runProcessMock.mockReset()
|
||||
|
||||
+1
-19
@@ -55,7 +55,7 @@ export function isWslPath(path: string): boolean {
|
||||
* Check whether a WSL UNC working directory exists by testing it inside the
|
||||
* distro itself, returning null when the answer can't be determined.
|
||||
*
|
||||
* Why: Win32 fs.statSync against the WSL 9P filesystem (\\wsl.localhost\...)
|
||||
* Why: Win32 fs.stat against the WSL 9P filesystem (\\wsl.localhost\...)
|
||||
* is unreliable for repos that live on the WSL side — it can report ENOENT for
|
||||
* directories that exist, which made opening a WSL worktree fail with
|
||||
* "Working directory ... does not exist". The guest marker probe asks the
|
||||
@@ -63,24 +63,6 @@ export function isWslPath(path: string): boolean {
|
||||
* than false) when wsl.exe is unavailable or errors so callers can fall back to
|
||||
* the fs check instead of falsely rejecting a valid directory.
|
||||
*/
|
||||
export function wslUncDirectoryExists(uncPath: string): boolean | null {
|
||||
if (process.platform !== 'win32') {
|
||||
return null
|
||||
}
|
||||
const info = parseWslUncPath(uncPath)
|
||||
if (!info) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const stdout = wslExeStdout(
|
||||
runProcessSync(wslExeProcessSpec(getWslDirectoryProbeArgs(info), WSL_PROBE_TIMEOUT_MS))
|
||||
)
|
||||
return parseWslDirectoryProbeOutput(stdout)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function wslUncDirectoryExistsAsync(uncPath: string): Promise<boolean | null> {
|
||||
if (process.platform !== 'win32') {
|
||||
return Promise.resolve(null)
|
||||
|
||||
@@ -177,7 +177,7 @@ describe('wsl login shell command helpers', () => {
|
||||
['awk field reference', ['sh', '-c', `echo 'a b' | awk '{print $2}'`], 'b\n'],
|
||||
['literal escaped dollar', ['sh', '-c', `printf '[%s]' "\\$HOME"`], '[$HOME]'],
|
||||
['single-quoted dollar', ['sh', '-c', `printf '[%s]' '$PATH'`], '[$PATH]'],
|
||||
// The shape wslUncDirectoryExists uses: `--` blanked $1, so every existing
|
||||
// The shape wslUncDirectoryExistsAsync uses: `--` blanked $1, so every existing
|
||||
// directory probed as missing.
|
||||
['positional argument', ['sh', '-c', 'printf "[%s]" "$1"', 'sh', 'ARG'], '[ARG]'],
|
||||
['shell local', ['sh', '-c', 'x=hi; printf "[%s]" "$x"'], '[hi]']
|
||||
|
||||
Reference in New Issue
Block a user