Skip to content

Commit e47f89e

Browse files
committed
fix(sea): preserve native reentry and windows shim execution
1 parent a81df4c commit e47f89e

4 files changed

Lines changed: 48 additions & 3 deletions

File tree

‎src/command/ask/handle-ask.mts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sea from 'node:sea'
12
import { promises as fs } from 'node:fs'
23
import path from 'node:path'
34

@@ -130,6 +131,9 @@ const ENVIRONMENT_KEYWORDS = {
130131
export function getCliReentryArgv(
131132
command: string[] | readonly string[],
132133
): string[] | undefined {
134+
if (sea.isSea()) {
135+
return [...command]
136+
}
133137
const entryPath = process.argv[1]
134138
return entryPath ? [entryPath, ...command] : undefined
135139
}

‎src/util/ecosystem/environment-agent.mts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import semver from 'semver'
1919

2020
import { getNpmExecPath, getPnpmExecPath } from '../../constants/agents.mts'
2121
import { FLAG_VERSION } from '../../constants/cli.mts'
22-
import { execPath, nodeNoWarningsFlags } from '../../constants/paths.mts'
22+
import { nodeNoWarningsFlags } from '../../constants/paths.mts'
23+
import { resolveNodeRuntime } from '../spawn/node-runtime.mts'
2324
import { preferWindowsCmdShim, resolveBinPathSync } from './windows-shims.mts'
2425

2526
import type { Agent } from './supported-agents.mts'
@@ -117,10 +118,11 @@ export async function getAgentVersion(
117118
}
118119

119120
if (shouldRunWithNode) {
121+
const runtime = await resolveNodeRuntime({ cwd })
120122
const spawnResult = await spawn(
121-
execPath,
123+
runtime.executable,
122124
[...nodeNoWarningsFlags, shouldRunWithNode, FLAG_VERSION],
123-
{ cwd },
125+
{ cwd, env: runtime.environment },
124126
)
125127

126128
if (!spawnResult) {

‎test/unit/command/ask/handle-ask.test.mts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sea from 'node:sea'
12
import {
23
extractWords,
34
normalizeQuery,
@@ -97,6 +98,15 @@ describe('handleAsk', () => {
9798
)
9899
})
99100

101+
it('reenters a SEA executable with command arguments only', () => {
102+
const probe = vi.spyOn(sea, 'isSea').mockReturnValue(true)
103+
try {
104+
expect(getCliReentryArgv(['scan', '--json'])).toEqual(['scan', '--json'])
105+
} finally {
106+
probe.mockRestore()
107+
}
108+
})
109+
100110
it('reports an unknown entry script instead of guessing one', () => {
101111
const originalArgv1 = process.argv[1]
102112
process.argv[1] = ''

‎test/unit/util/ecosystem/environment.test.mts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ import {
2323
resolveBinPathSync,
2424
} from '../../../../src/util/ecosystem/environment.mts'
2525

26+
const mockIsWin32 = vi.hoisted(() => vi.fn(() => false))
27+
const mockNodeRuntime = vi.hoisted(() => vi.fn())
28+
vi.mock(
29+
import('@socketsecurity/lib-stable/constants/platform'),
30+
async importOriginal => ({
31+
...(await importOriginal()),
32+
isWin32: mockIsWin32,
33+
}),
34+
)
35+
vi.mock(import('../../../../src/util/spawn/node-runtime.mts'), () => ({
36+
resolveNodeRuntime: mockNodeRuntime,
37+
}))
38+
2639
// Mock the dependencies.
2740
const mockExistsSync = vi.hoisted(() => vi.fn())
2841
const mockReadFileSync = vi.hoisted(() => vi.fn())
@@ -70,6 +83,7 @@ vi.mock(import('semver'), () => ({
7083
describe('package-environment', () => {
7184
beforeEach(() => {
7285
vi.clearAllMocks()
86+
mockIsWin32.mockReturnValue(false)
7387
// Default mock behavior for spawn to get package manager version.
7488
mockSpawn.mockResolvedValue({ stdout: '10.0.0', stderr: '', code: 0 })
7589
})
@@ -201,6 +215,21 @@ describe('package-environment', () => {
201215
})
202216

203217
describe('getAgentVersion', () => {
218+
it('runs Windows JavaScript shims with the trusted Node runtime', async () => {
219+
mockIsWin32.mockReturnValue(true)
220+
mockExistsSync.mockReturnValue(true)
221+
mockReadFileSync.mockReturnValue('node "/tools/npm-cli.js" "$@"')
222+
mockNodeRuntime.mockResolvedValue({
223+
executable: '/trusted/node',
224+
environment: { PATH: '/trusted' },
225+
})
226+
await getAgentVersion('npm', '/tools/npm', '/project')
227+
expect(mockSpawn).toHaveBeenCalledWith(
228+
'/trusted/node',
229+
expect.arrayContaining(['/tools/npm-cli.js', '--version']),
230+
{ cwd: '/project', env: { PATH: '/trusted' } },
231+
)
232+
})
204233
it('returns coerced semver version on successful spawn', async () => {
205234
mockSpawn.mockResolvedValue({
206235
stdout: '10.8.2',

0 commit comments

Comments
 (0)