diff --git a/src/main/providers/local-pty-utils.test.ts b/src/main/providers/local-pty-utils.test.ts deleted file mode 100644 index c537f4e5121..00000000000 --- a/src/main/providers/local-pty-utils.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type * as fs from 'fs' -import { sep } from 'path' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { resolveNodePtySpawnHelperPath, selectNodePtySpawnHelperRepair } from './local-pty-utils' - -const { existsSyncMock } = vi.hoisted(() => ({ - existsSyncMock: vi.fn<(path: string) => boolean>() -})) - -vi.mock('fs', async () => { - const actual = await vi.importActual('fs') - return { - ...actual, - existsSync: existsSyncMock - } -}) - -function normalizePathForAssertion(path: string): string { - return sep === '\\' ? path.replaceAll('\\', '/') : path -} - -describe('selectNodePtySpawnHelperRepair', () => { - afterEach(() => { - existsSyncMock.mockReset() - }) - - it('does not repair when the resolved helper already exists', () => { - existsSyncMock.mockImplementation((path) => path === '/resolved/spawn-helper') - - expect( - selectNodePtySpawnHelperRepair({ - resolvedHelperPath: '/resolved/spawn-helper', - candidates: ['/resolved/spawn-helper', '/prebuilds/spawn-helper'] - }) - ).toEqual({ needsRepair: false, repairSourcePath: null }) - }) - - it('repairs from an alternate existing helper when the resolved path is missing', () => { - existsSyncMock.mockImplementation((path) => path === '/prebuilds/spawn-helper') - - expect( - selectNodePtySpawnHelperRepair({ - resolvedHelperPath: '/build/Release/spawn-helper', - candidates: ['/build/Release/spawn-helper', '/prebuilds/spawn-helper'] - }) - ).toEqual({ - needsRepair: true, - repairSourcePath: '/prebuilds/spawn-helper' - }) - }) - - it('does not claim a repair when no helper exists anywhere', () => { - existsSyncMock.mockReturnValue(false) - - expect( - selectNodePtySpawnHelperRepair({ - resolvedHelperPath: '/build/Release/spawn-helper', - candidates: ['/build/Release/spawn-helper', '/prebuilds/spawn-helper'] - }) - ).toEqual({ needsRepair: false, repairSourcePath: null }) - }) -}) - -describe('resolveNodePtySpawnHelperPath', () => { - it('matches node-pty path resolution for relative native dirs', () => { - expect( - normalizePathForAssertion( - resolveNodePtySpawnHelperPath({ - unixTerminalPath: '/app/node_modules/node-pty/lib/unixTerminal.js', - nativeDir: '../build/Release/' - }) - ) - ).toBe('/app/node_modules/node-pty/build/Release/spawn-helper') - }) - - it('targets the unpacked asar path used by node-pty at runtime', () => { - expect( - normalizePathForAssertion( - resolveNodePtySpawnHelperPath({ - unixTerminalPath: - '/App.app/Contents/Resources/app.asar/node_modules/node-pty/lib/unixTerminal.js', - nativeDir: '../build/Release/' - }) - ) - ).toBe( - '/App.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/build/Release/spawn-helper' - ) - }) - - it('does not double-apply the unpacked asar transform', () => { - expect( - normalizePathForAssertion( - resolveNodePtySpawnHelperPath({ - unixTerminalPath: - '/App.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/lib/unixTerminal.js', - nativeDir: '../build/Release/' - }) - ) - ).toBe( - '/App.app/Contents/Resources/app.asar.unpacked/node_modules/node-pty/build/Release/spawn-helper' - ) - }) -}) diff --git a/src/main/providers/local-pty-utils.ts b/src/main/providers/local-pty-utils.ts index ddc618fa874..0c5235b9264 100644 --- a/src/main/providers/local-pty-utils.ts +++ b/src/main/providers/local-pty-utils.ts @@ -1,13 +1,5 @@ -import { basename, dirname, join } from 'path' -import { - existsSync, - accessSync, - statSync, - chmodSync, - copyFileSync, - mkdirSync, - constants as fsConstants -} from 'fs' +import { basename, join } from 'path' +import { existsSync, accessSync, statSync, chmodSync, constants as fsConstants } from 'fs' import type * as pty from 'node-pty' let didEnsureSpawnHelperExecutable = false @@ -32,39 +24,6 @@ export function getNodePtySpawnHelperCandidates(): string[] { ].map(toUnpackedAsarPath) } -export function resolveNodePtySpawnHelperPath(args: { - unixTerminalPath: string - nativeDir: string -}): string { - return toUnpackedAsarPath(join(dirname(args.unixTerminalPath), args.nativeDir, 'spawn-helper')) -} - -export function getResolvedNodePtySpawnHelperPath(): string { - const { loadNativeModule } = require('node-pty/lib/utils') as { - loadNativeModule: (name: string) => { dir: string } - } - const native = loadNativeModule('pty') - return resolveNodePtySpawnHelperPath({ - unixTerminalPath: require.resolve('node-pty/lib/unixTerminal.js'), - nativeDir: native.dir - }) -} - -export function selectNodePtySpawnHelperRepair(args: { - resolvedHelperPath: string - candidates: readonly string[] -}): { needsRepair: boolean; repairSourcePath: string | null } { - if (existsSync(args.resolvedHelperPath)) { - return { needsRepair: false, repairSourcePath: null } - } - - const repairSourcePath = - args.candidates.find( - (candidate) => candidate !== args.resolvedHelperPath && existsSync(candidate) - ) ?? null - return { needsRepair: repairSourcePath !== null, repairSourcePath } -} - /** * Validate that a shell binary exists and is executable. * Returns an error message string if invalid, null if valid. @@ -98,16 +57,8 @@ export function ensureNodePtySpawnHelperExecutable(): void { didEnsureSpawnHelperExecutable = true try { - const candidates = getNodePtySpawnHelperCandidates() - const resolvedHelperPath = getResolvedNodePtySpawnHelperPath() - const repair = selectNodePtySpawnHelperRepair({ resolvedHelperPath, candidates }) - if (repair.needsRepair && repair.repairSourcePath) { - mkdirSync(dirname(resolvedHelperPath), { recursive: true }) - copyFileSync(repair.repairSourcePath, resolvedHelperPath) - } - - for (const candidate of [resolvedHelperPath, ...candidates]) { - if (!existsSync(candidate) || !statSync(candidate).isFile()) { + for (const candidate of getNodePtySpawnHelperCandidates()) { + if (!existsSync(candidate)) { continue } const mode = statSync(candidate).mode