fix(ssh): make the Windows deps probe exit 0 on a real load failure, like its POSIX twin

This commit is contained in:
Neil
2026-09-02 14:13:23 -07:00
parent be63c3dfbc
commit ecfe01ac1e
3 changed files with 43 additions and 3 deletions
+1 -1
View File
@@ -826,7 +826,7 @@ async function probeRequiredNativeDeps(
hostPlatform,
nodePath,
remoteDir,
`try { & ${powerShellLiteral(nodePath)} -e ${powerShellNativeArg(probeJs)} } catch { 'MISSING' }`
`try { & ${powerShellLiteral(nodePath)} -e ${powerShellNativeArg(probeJs)}; if ($LASTEXITCODE -ne 0) { 'MISSING' } } catch { 'MISSING' }`
)
: // Why: no `2>/dev/null` — it discarded the only line that says why node never reached the
// script. stderr stays its own stream so it can't be mistaken for the verdict, mirroring
+17 -2
View File
@@ -24,6 +24,16 @@ type SshCommandTerminationError = Error & {
sshChannelCloseConfirmed: boolean
}
// Why: callers must tell "the host answered no" from "the host never answered". Matching the
// message text is what let an unanswered probe be read as a definitive negative.
export const SSH_EXEC_TIMEOUT_CODE = 'SSH_EXEC_TIMEOUT'
export function isSshExecTimeout(error: unknown): boolean {
return (
error instanceof Error && (error as Partial<{ code: string }>).code === SSH_EXEC_TIMEOUT_CODE
)
}
export function isUnconfirmedSshCommandTermination(
error: unknown
): error is SshCommandTerminationError {
@@ -164,8 +174,13 @@ export async function execCommand(
}
const timeout = setTimeout(() => {
requestTermination(
new Error(
`Command "${redactRelayInstallMarkerTokens(command)}" timed out after ${timeoutMs / 1000}s`
Object.assign(
new Error(
`Command "${redactRelayInstallMarkerTokens(command)}" timed out after ${
timeoutMs / 1000
}s`
),
{ code: SSH_EXEC_TIMEOUT_CODE }
)
)
}, timeoutMs)
@@ -394,6 +394,31 @@ describe('installNativeDeps (via deployAndLaunchRelay)', () => {
}
})
it('does not rewrite node_modules when the health probe never answered', async () => {
// Why (#14830): a wedged `require("node-pty")` makes the probe time out. Reading that silence
// as "every native dep is missing" sent a healthy install through npm install + rebuild that
// could not help, and the retry loop burned the whole deploy budget at "Deploying relay…".
const conn = makeMockConnection(sftpCapture)
vi.mocked(isRelayAlreadyInstalled).mockResolvedValue(true)
feed([
'__ORCA_REMOTE_PLATFORM__ Linux x86_64',
'/home/u',
{ reject: 'Command "node -e ..." timed out after 30s' } // health probe never answered
])
await deployAndLaunchRelay(conn).catch(() => {})
const execCalls = vi.mocked(execCommand).mock.calls.map(([, c]) => c)
expect(execCalls.some((c) => c.includes('npm install'))).toBe(false)
expect(execCalls.some((c) => c.includes('npm rebuild'))).toBe(false)
const warnMessages = warnSpy.mock.calls.map((args) => String(args[0] ?? ''))
expect(warnMessages.some((m) => m.includes('Repairing missing native deps'))).toBe(false)
// Why no log assertion: the behavioural claim above is the real one. Asserting on warn text
// pinned wording that main's landed probe verdict does not use, and #18000 adds its own.
expect(execCalls.some((c) => c.includes("rm -rf 'node_modules/node-pty'"))).toBe(false)
})
it('lets a probe SSH-channel failure bubble up rather than silently mapping to MISSING', async () => {
const conn = makeMockConnection(sftpCapture)
feed(