Files
orca/config/scripts/build-windows-cli-launcher.test.mjs
T
Brennan BensonandOrca QA c18c358fce Keep child-worktree creation in the caller’s project (#35787)
* fix(cli): keep inferred worktree creation in the caller project

* Fix caller checkout ranking and creation guidance

* Share caller-host containment preparation across CLI selections

* Correct Windows CLI fixture syntax and snapshot command

* Match worktree fixture output to typed execution hosts

* Regenerate bundled workspace placement guidance

* Refuse ambiguous workspace ownership and preserve launch replay

* Scope runtime filesystem mocks to their aggregate tests

* Preserve launch proof semantics under strict test diagnostics

* Keep launch proof imports within their compiler project boundary

* Consolidate journal cleanup test imports

* Adapt launch fixtures to the current host settings contract

* Keep the CLI error formatter under the file-size limit

Move the selector-ambiguity workspace-choice check into selector-ambiguity-recovery.ts, which already owns that error's recovery text. Behavior is unchanged.

* Expect main's concise orchestration output in the workspace-choice CLI tests

Orchestration commands now print the one-line failure on stderr and the workspace choices and next steps on stdout (or compact JSON plus the stderr line with --json), and print concise success fields.

---------

Co-authored-by: Orca QA <orca-qa@users.noreply.github.com>
2026-11-01 13:35:47 -08:00

544 lines
20 KiB
JavaScript

import {
copyFileSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
utimesSync,
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join, resolve } from 'node:path'
import { spawnSync } from 'node:child_process'
import { runProcessSync } from '@orca/process-host'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import {
shouldReuseCompiledWindowsRustExecutable,
windowsRustExecutableFileVersion,
windowsRustExecutableTarget,
windowsRustExecutableFingerprint
} from './windows-rust-executable-build.mjs'
import { findDynamicVcRuntimeImports, readPeImportedDllNames } from './windows-pe-imports.mjs'
const itCrossHost = process.platform === 'win32' ? it.skip : it
const projectRoot = resolve(import.meta.dirname, '../..')
const WINDOWS_LOCK_CODES = ['EBUSY', 'ENOTEMPTY', 'EPERM']
// Windows image/AV locks can outlast exit; retry cleanup then retain temp tree rather than fail launcher assertions.
function removeFixtureTree(path) {
try {
rmSync(path, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
} catch (error) {
if (process.platform !== 'win32' || !WINDOWS_LOCK_CODES.includes(error?.code)) {
throw error
}
}
}
// Cold Cargo compile/link exceeds unit timeout on hosted Windows; subsequent cases reuse target output.
function itWindows(name, test) {
const runner = process.platform === 'win32' ? it : it.skip
runner(name, { timeout: 300_000 }, test)
}
let sharedLauncherRoot = null
let sharedLauncherPath = null
let sharedLauncherBuild = null
// Compile once; each launch case gets a private copy so it can own its fixture tree.
beforeAll(() => {
if (process.platform !== 'win32') {
return
}
sharedLauncherRoot = mkdtempSync(join(tmpdir(), 'orca cli launcher build '))
sharedLauncherPath = join(sharedLauncherRoot, 'orca.exe')
const result = runProcessSync({
program: process.execPath,
args: ['config/scripts/build-windows-cli-launcher.mjs'],
cwd: projectRoot,
timeoutMs: 300_000
})
sharedLauncherBuild = { status: result.code, stdout: result.stdout, stderr: result.stderr }
if (result.code !== 0) {
throw new Error(`${result.stdout}\n${result.stderr}`)
}
copyFileSync(
join(projectRoot, 'native', 'windows-cli-launcher', '.build', 'orca.exe'),
sharedLauncherPath
)
}, 300_000)
afterAll(() => {
if (sharedLauncherRoot) {
removeFixtureTree(sharedLauncherRoot)
}
})
function copySharedLauncher(target) {
if (!sharedLauncherPath) {
throw new Error('shared Windows CLI launcher was not built')
}
mkdirSync(dirname(target), { recursive: true })
copyFileSync(sharedLauncherPath, target)
}
describe('Windows CLI launcher', () => {
itWindows('reuses the hook host target while invalidating changed inputs', () => {
const root = mkdtempSync(join(tmpdir(), 'orca-hook-target-'))
try {
const hookScript = join('config', 'scripts', 'build-windows-agent-hook.mjs')
const crate = join('native', 'windows-agent-hook')
const outputFile = join(crate, '.build', 'orca-agent-hook.exe')
const outputPath = join(root, outputFile)
const fingerprintPath = `${outputPath}.sha256`
// Why the default output: it reuses a warm canonical prebuild; Cargo runs only when that is missing or stale.
const first = runProcessSync({
program: process.execPath,
args: [hookScript],
cwd: projectRoot,
timeoutMs: 300_000
})
expect(first.code, `${first.stdout}\n${first.stderr}`).toBe(0)
for (const file of [
outputFile,
`${outputFile}.sha256`,
'package.json',
hookScript,
join('config', 'scripts', 'windows-rust-executable-build.mjs'),
join('config', 'scripts', 'windows-pe-imports.mjs'),
join('resources', 'build', 'icon.ico'),
...[
'src/main.rs',
'build.rs',
'Cargo.toml',
'Cargo.lock',
'app.manifest',
'.cargo/config.toml'
].map((file) => join(crate, ...file.split('/')))
]) {
mkdirSync(dirname(join(root, file)), { recursive: true })
copyFileSync(join(projectRoot, file), join(root, file))
}
const fingerprint = readFileSync(fingerprintPath, 'utf8')
const binary = readFileSync(outputPath)
// A reuse hit must succeed even when Cargo is unavailable.
const environment = Object.fromEntries(
Object.entries(process.env).filter(([key]) => key.toLowerCase() !== 'path')
)
environment.PATH = ''
const invoke = (args = []) =>
runProcessSync({
program: process.execPath,
args: [hookScript, ...args],
cwd: root,
env: environment,
timeoutMs: 10_000
})
for (const args of [[], ['--arch', process.arch]]) {
const reused = invoke(args)
expect(reused.code, `${reused.stdout}\n${reused.stderr}`).toBe(0)
expect(reused.stdout).toContain('reusing Windows agent hook')
expect(readFileSync(fingerprintPath, 'utf8')).toBe(fingerprint)
expect(readFileSync(outputPath)).toEqual(binary)
}
const otherArchitecture = process.arch === 'x64' ? 'arm64' : 'x64'
const crossTarget = invoke(['--arch', otherArchitecture])
expect(crossTarget.code).not.toBe(0)
expect(crossTarget.stderr).toContain('Unable to run cargo')
const unsupported = invoke(['--arch', 'ia32'])
expect(unsupported.code).not.toBe(0)
expect(unsupported.stderr).toContain('Unsupported Windows executable architecture: ia32')
for (const file of [join(crate, 'src', 'main.rs'), 'package.json']) {
const path = join(root, file)
const original = readFileSync(path, 'utf8')
writeFileSync(fingerprintPath, fingerprint)
writeFileSync(
path,
file === 'package.json'
? JSON.stringify({ ...JSON.parse(original), version: '99.0.0' })
: `${original}\n// changed fixture source\n`
)
const changed = invoke()
expect(changed.code).not.toBe(0)
expect(changed.stderr).toContain('Unable to run cargo')
writeFileSync(path, original)
}
writeFileSync(fingerprintPath, fingerprint)
expect(invoke().code).toBe(0)
} finally {
removeFixtureTree(root)
}
})
it('reuses restored builds only while all embedded inputs and the release version match', () => {
const root = mkdtempSync(join(tmpdir(), 'orca-cli-launcher-reuse-'))
try {
const inputs = ['main.rs', 'build.rs', 'Cargo.toml', 'app.manifest', 'icon.ico'].map(
(name) => {
const path = join(root, name)
writeFileSync(path, name)
return path
}
)
const outputPath = join(root, 'orca.exe')
const fingerprint = windowsRustExecutableFingerprint(inputs, '1.4.214')
expect(shouldReuseCompiledWindowsRustExecutable(outputPath, fingerprint)).toBe(false)
writeFileSync(outputPath, 'binary')
expect(shouldReuseCompiledWindowsRustExecutable(outputPath, fingerprint)).toBe(false)
writeFileSync(`${outputPath}.sha256`, fingerprint)
for (const input of inputs) {
utimesSync(input, new Date(), new Date())
}
expect(
shouldReuseCompiledWindowsRustExecutable(
outputPath,
windowsRustExecutableFingerprint(inputs, '1.4.214')
)
).toBe(true)
expect(
shouldReuseCompiledWindowsRustExecutable(
outputPath,
windowsRustExecutableFingerprint(inputs, '1.4.215')
)
).toBe(false)
for (const input of inputs) {
const original = readFileSync(input)
writeFileSync(input, 'changed')
expect(
shouldReuseCompiledWindowsRustExecutable(
outputPath,
windowsRustExecutableFingerprint(inputs, '1.4.214')
)
).toBe(false)
writeFileSync(input, original)
}
} finally {
removeFixtureTree(root)
}
})
it('reduces a prerelease to the numeric version Windows can record', () => {
expect(windowsRustExecutableFileVersion('1.4.214-daily.202609281300')).toBe('1.4.214.0')
expect(windowsRustExecutableFileVersion('1.4.214')).toBe('1.4.214.0')
for (const version of ['1.4.65535', '1.4', '1.4.214"', undefined]) {
expect(() => windowsRustExecutableFileVersion(version)).toThrow('Invalid Windows')
}
})
itWindows(
'embeds publisher, release version, icon and an unelevated application manifest',
() => {
const root = mkdtempSync(join(tmpdir(), 'orca launcher metadata '))
try {
const launcherPath = join(root, 'orca.exe')
expect(sharedLauncherBuild).not.toBeNull()
expect(
sharedLauncherBuild.status,
`${sharedLauncherBuild.stdout}\n${sharedLauncherBuild.stderr}`
).toBe(0)
copySharedLauncher(launcherPath)
const inspect = spawnSync(
'powershell.exe',
[
'-NoProfile',
'-NonInteractive',
'-Command',
'[Diagnostics.FileVersionInfo]::GetVersionInfo($env:ORCA_TEST_LAUNCHER) | ConvertTo-Json -Compress'
],
{ encoding: 'utf8', env: { ...process.env, ORCA_TEST_LAUNCHER: launcherPath } }
)
expect(inspect.status, inspect.stderr).toBe(0)
const info = JSON.parse(inspect.stdout)
const { version } = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'))
expect(info.CompanyName).toBe('Stably AI')
expect(info.ProductName).toBe('Orca')
expect(info.FileDescription).toBe('Orca CLI Launcher')
expect(info.FileVersion).toBe(`${version.split(/[+-]/)[0]}.0`)
expect(info.ProductVersion).toBe(version)
const binary = readFileSync(launcherPath)
expect(binary.includes(Buffer.from('requestedExecutionLevel level="asInvoker"'))).toBe(true)
const imports = readPeImportedDllNames(binary)
expect(imports.map((name) => name.toLowerCase())).toContain('kernel32.dll')
expect(findDynamicVcRuntimeImports(imports)).toEqual([])
const icon = readFileSync(join(projectRoot, 'resources', 'build', 'icon.ico'))
const imageSize = icon.readUInt32LE(14)
const imageOffset = icon.readUInt32LE(18)
expect(binary.includes(icon.subarray(imageOffset, imageOffset + imageSize))).toBe(true)
} finally {
removeFixtureTree(root)
}
}
)
itCrossHost('fails closed when the Windows launcher cannot be compiled on this host', () => {
const outputRoot = mkdtempSync(join(tmpdir(), 'orca cross-host launcher '))
try {
const result = spawnSync(
process.execPath,
['config/scripts/build-windows-cli-launcher.mjs', '--output', join(outputRoot, 'orca.exe')],
{ cwd: projectRoot, encoding: 'utf8' }
)
expect(result.status).not.toBe(0)
expect(result.stderr).toContain('Windows CLI launcher')
expect(result.stderr).toContain('Windows host')
} finally {
removeFixtureTree(outputRoot)
}
})
itWindows('preserves a multiline argument from PowerShell through the native launcher', () => {
const appRoot = mkdtempSync(join(tmpdir(), 'orca cli launcher '))
try {
const resourcesPath = join(appRoot, 'resources')
const launcherPath = join(resourcesPath, 'bin', 'orca.exe')
const cliPath = join(resourcesPath, 'app.asar.unpacked', 'out', 'cli', 'index.js')
mkdirSync(join(resourcesPath, 'bin'), { recursive: true })
mkdirSync(dirname(cliPath), { recursive: true })
copyFileSync(process.execPath, join(appRoot, 'Orca.exe'))
writeFileSync(
cliPath,
`process.stdout.write(JSON.stringify({
argv: process.argv.slice(2),
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE,
nodeOptions: process.env.NODE_OPTIONS ?? null,
orcaNodeOptions: process.env.ORCA_NODE_OPTIONS ?? null
}))\n`,
'utf8'
)
copySharedLauncher(launcherPath)
const body = 'paragraph one line one\nparagraph one line two\n\nparagraph two'
const powershell = spawnSync(
'powershell.exe',
[
'-NoProfile',
'-NonInteractive',
'-Command',
'& $env:ORCA_TEST_LAUNCHER orchestration send --body $env:ORCA_TEST_BODY --json'
],
{
encoding: 'utf8',
env: {
...process.env,
NODE_OPTIONS: '--no-warnings',
ORCA_TEST_BODY: body,
ORCA_TEST_LAUNCHER: launcherPath
}
}
)
expect(powershell.status, powershell.stderr).toBe(0)
expect(JSON.parse(powershell.stdout)).toEqual({
argv: ['orchestration', 'send', '--body', body, '--json'],
electronRunAsNode: '1',
nodeOptions: null,
orcaNodeOptions: '--no-warnings'
})
} finally {
removeFixtureTree(appRoot)
}
})
itWindows(
'keeps workspace-choice data in one argv value through native and supported PowerShell modes',
() => {
const root = mkdtempSync(join(tmpdir(), 'orca choice native argv '))
try {
const launcher = join(root, 'bin', 'orca.exe')
const entry = join(root, 'argv.cjs')
const script = join(root, 'choice.ps1')
writeFileSync(entry, 'process.stdout.write(JSON.stringify(process.argv.slice(2)))')
copySharedLauncher(launcher)
writeFileSync(
join(dirname(launcher), 'orca-launcher.cfg'),
`node=${process.execPath}\ncli=${entry}\nuser-data=${root}\n`
)
writeFileSync(
script,
[
'[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)',
'$PSNativeCommandArgumentPassing = $env:ORCA_CHOICE_MODE',
'& $env:ORCA_CHOICE_LAUNCHER worktree show --worktree $env:ORCA_CHOICE_SELECTOR --json',
'exit $LASTEXITCODE'
].join('\n')
)
const selectors = [
'id:repo::C:\\space path\\child',
'id:repo::C:\\quotes" and smart“quotes”',
'id:repo::C:\\semi;&$`%!^()',
'id:repo::C:\\back\\slash/',
'id:repo::C:\\line\nfeed',
'id:repo::C:\\cafe\u0301',
'id:repo::C:\\trailing\\'
]
for (const selector of selectors) {
const argv = ['worktree', 'show', '--worktree', selector, '--json']
const native = runProcessSync({ program: launcher, args: argv })
expect(native.code, native.stderr).toBe(0)
expect(JSON.parse(native.stdout)).toEqual(argv)
for (const [program, mode] of [
['powershell.exe', 'Legacy'],
['pwsh.exe', 'Standard'],
['pwsh.exe', 'Legacy']
]) {
// Legacy native argument passing itself drops embedded quotes; no universal shell recipe is promised.
if (mode === 'Legacy' && selector.includes('"')) {
continue
}
const result = runProcessSync({
program,
args: ['-NoProfile', '-NonInteractive', '-File', script],
env: {
...process.env,
ORCA_CHOICE_MODE: mode,
ORCA_CHOICE_LAUNCHER: launcher,
ORCA_CHOICE_SELECTOR: selector
}
})
expect(result.code, `${program}/${mode}: ${result.stderr}`).toBe(0)
expect(JSON.parse(result.stdout)).toEqual(argv)
}
}
} finally {
removeFixtureTree(root)
}
}
)
it('cross-compiles only the architectures the orcad template ships', () => {
expect(windowsRustExecutableTarget(undefined)).toBeNull()
expect(windowsRustExecutableTarget('x64')).toBe('x86_64-pc-windows-msvc')
expect(windowsRustExecutableTarget('arm64')).toBe('aarch64-pc-windows-msvc')
expect(() => windowsRustExecutableTarget('ia32')).toThrow('Unsupported')
})
itWindows('runs a server profile CLI on pinned Node with argv and profile intact', () => {
const root = mkdtempSync(join(tmpdir(), 'orca server launcher '))
try {
const profile = join(root, 'server profile')
const launcherPath = join(profile, 'cli', 'bin', 'orca.exe')
const cliPath = join(root, 'server slot', 'out', 'cli', 'index.js')
mkdirSync(dirname(cliPath), { recursive: true })
writeFileSync(
cliPath,
`process.stdout.write(JSON.stringify({
argv: process.argv.slice(2),
profile: process.env.ORCA_USER_DATA_PATH,
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE ?? null,
nodeOptions: process.env.NODE_OPTIONS ?? null,
desktopMarker: process.env.ORCA_WINDOWS_PACKAGED_CLI_LAUNCHER ?? null
}))\n`,
'utf8'
)
copySharedLauncher(launcherPath)
writeFileSync(
join(dirname(launcherPath), 'orca-launcher.cfg'),
`node=${process.execPath}\ncli=${cliPath}\nuser-data=${profile}\n`
)
const inherited = {
...process.env,
ELECTRON_RUN_AS_NODE: '1',
NODE_OPTIONS: '--require C:\\missing\\preload.js',
ORCA_USER_DATA_PATH: 'C:\\another\\owner',
ORCA_WINDOWS_PACKAGED_CLI_LAUNCHER: '1',
ORCA_TEST_LAUNCHER: launcherPath
}
const expected = (body) => ({
argv: ['orchestration', 'send', '--body', body, '--json'],
profile,
electronRunAsNode: null,
nodeOptions: null,
desktopMarker: null
})
// Windows PowerShell 5.1 itself drops embedded double quotes, so its body has none.
const shellBody = 'line one & %PATH% ^caret\n\nline three'
const powershell = spawnSync(
'powershell.exe',
[
'-NoProfile',
'-NonInteractive',
'-Command',
'& $env:ORCA_TEST_LAUNCHER orchestration send --body $env:ORCA_TEST_BODY --json'
],
{ encoding: 'utf8', env: { ...inherited, ORCA_TEST_BODY: shellBody } }
)
expect(powershell.status, powershell.stderr).toBe(0)
expect(JSON.parse(powershell.stdout)).toEqual(expected(shellBody))
const body = 'line one "quoted" \\"escaped\\" & %PATH% ^caret\\\n\nline three\\'
const direct = spawnSync(launcherPath, expected(body).argv, {
encoding: 'utf8',
env: inherited
})
expect(direct.status, direct.stderr).toBe(0)
expect(JSON.parse(direct.stdout)).toEqual(expected(body))
} finally {
removeFixtureTree(root)
}
})
itWindows('survives an inherited environment block containing PATH and Path', () => {
const appRoot = mkdtempSync(join(tmpdir(), 'orca duplicate path launcher '))
try {
const resourcesPath = join(appRoot, 'resources')
const launcherPath = join(resourcesPath, 'bin', 'orca.exe')
const cliPath = join(resourcesPath, 'app.asar.unpacked', 'out', 'cli', 'index.js')
const outputPath = join(appRoot, 'child-result.json')
const harnessSourcePath = join(
projectRoot,
'config',
'scripts',
'fixtures',
'DuplicatePathProcessLauncher.cs'
)
const harnessPath = join(appRoot, 'DuplicatePathLauncher.exe')
mkdirSync(dirname(launcherPath), { recursive: true })
mkdirSync(dirname(cliPath), { recursive: true })
copyFileSync(process.execPath, join(appRoot, 'Orca.exe'))
writeFileSync(
cliPath,
`require('node:fs').writeFileSync(process.env.ORCA_TEST_OUTPUT, JSON.stringify({
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE,
pathKeys: Object.keys(process.env).filter((key) => key.toLowerCase() === 'path')
}))\n`,
'utf8'
)
copySharedLauncher(launcherPath)
const compiler = findFrameworkCompiler()
expect(compiler).not.toBeNull()
const compileHarness = spawnSync(
compiler,
['/nologo', '/target:exe', `/out:${harnessPath}`, harnessSourcePath],
{ encoding: 'utf8' }
)
expect(compileHarness.status, `${compileHarness.stdout}\n${compileHarness.stderr}`).toBe(0)
const launch = spawnSync(harnessPath, [launcherPath, outputPath], { encoding: 'utf8' })
expect(launch.status, `${launch.stdout}\n${launch.stderr}`).toBe(0)
expect(JSON.parse(readFileSync(outputPath, 'utf8'))).toEqual({
electronRunAsNode: '1',
pathKeys: ['PATH', 'Path']
})
} finally {
removeFixtureTree(appRoot)
}
})
})
function findFrameworkCompiler() {
const windowsDirectory = process.env.WINDIR ?? process.env.SystemRoot
if (!windowsDirectory) {
return null
}
return (
[
join(windowsDirectory, 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe'),
join(windowsDirectory, 'Microsoft.NET', 'Framework', 'v4.0.30319', 'csc.exe')
].find((candidate) => existsSync(candidate)) ?? null
)
}