mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
* fix(ssh): probe npm via prepended PATH, not colocated with node (#9165) The remote Node/npm toolchain gate invoked npm by its absolute path <nodeBinDir>/npm (POSIX) / npm.cmd (Windows, behind a Test-Path colocation check). But deploy (commandWithNodePath) runs bare `npm` with nodeBinDir merely prepended to PATH, so npm can resolve from anywhere on PATH. A host whose only resolvable node has npm elsewhere on PATH (e.g. node symlinked into a dir without npm) deployed fine on v1.4.144, but after upgrade the candidate is rejected with no fallback → SSH/relay connection fails to establish. Make the probe resolve npm exactly the way deploy does — bare `npm --version` under the same prepended PATH — so it still confirms npm is runnable (the #8450 concern) without requiring colocation. Windows now prepends the backslash-form dir (matching deploy) so bare-command PATH lookup resolves reliably. * test(ssh): cover split Node npm PATH resolution
67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
import { posix as posixPath } from 'node:path'
|
|
import { shellEscape } from './ssh-connection-utils'
|
|
import { powerShellLiteral } from './ssh-remote-powershell'
|
|
|
|
const MIN_NODE_MAJOR = 18
|
|
const NODE_VERSION_MARKER = '__ORCA_NODE_VERSION__'
|
|
const NPM_VERSION_MARKER = '__ORCA_NPM_VERSION__'
|
|
|
|
export function buildPosixNodeToolchainProbe(nodePath: string): string {
|
|
const nodeBinDir = posixPath.dirname(nodePath)
|
|
return [
|
|
`printf '%s\\n' '${NODE_VERSION_MARKER}'`,
|
|
`${shellEscape(nodePath)} --version`,
|
|
`printf '%s\\n' '${NPM_VERSION_MARKER}'`,
|
|
// Why: deploy prepends nodeBinDir before running bare npm; requiring a
|
|
// colocated executable rejects valid split layouts (#9165).
|
|
`PATH=${shellEscape(nodeBinDir)}:$PATH npm --version`
|
|
].join(' && ')
|
|
}
|
|
|
|
export function buildWindowsNodeToolchainProbe(nodePath: string): string {
|
|
const nodeBinDir = posixPath.dirname(nodePath)
|
|
const windowsNodeBinDir = nodeBinDir.replace(/\//g, '\\')
|
|
return [
|
|
// Why: mirror deploy's PATH-prepend + bare npm resolution so split
|
|
// Node/npm layouts are not rejected solely for lacking npm.cmd (#9165).
|
|
`$env:PATH = ${powerShellLiteral(windowsNodeBinDir)} + ';' + $env:PATH`,
|
|
`Write-Output ${powerShellLiteral(NODE_VERSION_MARKER)}`,
|
|
`& ${powerShellLiteral(nodePath)} --version`,
|
|
'if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }',
|
|
`Write-Output ${powerShellLiteral(NPM_VERSION_MARKER)}`,
|
|
'& npm --version',
|
|
'exit $LASTEXITCODE'
|
|
].join('; ')
|
|
}
|
|
|
|
export function nodeToolchainVersionsMeetRequirements(versionOutput: string): boolean {
|
|
const nodeMajor = markedVersionMajor(versionOutput, NODE_VERSION_MARKER)
|
|
const npmMajor = markedVersionMajor(versionOutput, NPM_VERSION_MARKER)
|
|
if (versionOutput.includes(NODE_VERSION_MARKER)) {
|
|
return nodeMajor !== null && nodeMajor >= MIN_NODE_MAJOR && npmMajor !== null
|
|
}
|
|
|
|
// Why: existing proxy integrations and tests may return only the Node
|
|
// version even though production probes now emit both version markers.
|
|
const legacyMatch = versionOutput.trim().match(/^v?(\d+)/)
|
|
return legacyMatch ? Number.parseInt(legacyMatch[1]!, 10) >= MIN_NODE_MAJOR : false
|
|
}
|
|
|
|
function markedVersionMajor(output: string, marker: string): number | null {
|
|
const lines = output.split(/\r?\n/)
|
|
const markerIndex = lines.indexOf(marker)
|
|
if (markerIndex < 0) {
|
|
return null
|
|
}
|
|
for (const line of lines.slice(markerIndex + 1)) {
|
|
if (line.startsWith('__ORCA_')) {
|
|
return null
|
|
}
|
|
const match = line.trim().match(/^v?(\d+)(?:\.\d+){1,2}(?:[-+].*)?$/)
|
|
if (match) {
|
|
return Number.parseInt(match[1]!, 10)
|
|
}
|
|
}
|
|
return null
|
|
}
|