Files
orca/config/scripts/runtime-verification/desktop-transport/run-linux-cli-launch-contract-docker.mjs
T
OrcaWinandOrcaWin 895cd026c0 test: remove low-value fixture, relay, and recovery cases (#41591)
* test: remove low-value fixture suites and custom scenarios

* test: prune relay and recovery matrices and refresh retained host journey

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-12 08:32:10 -08:00

238 lines
7.6 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// Exercise packaged CLI paths under the hostile Linux conditions from #11609/#12530/#13719/#14229.
import { execFileSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { resolve } from 'node:path'
const commandArgs = process.argv.slice(2)
const appImageArg = valueAfter('--appimage')
const appImage = appImageArg ? resolve(appImageArg) : null
const platform = valueAfter('--platform')
const dockerPlatformArgs = platform ? ['--platform', platform] : []
const suffix = `${process.pid}-${Date.now()}`
const artifactVolume = `orca-cli-contract-artifact-${suffix}`
const tagArchitecture = platform?.split('/')[1] ?? process.arch
const tag = `orca-cli-launch-contract:ubuntu-24.04-${tagArchitecture}-${suffix}`
const containers = new Set()
let artifactVolumeCreated = false
const CASE_TIMEOUT_MS = 90_000
const BUILD_TIMEOUT_MS = 10 * 60_000
const STAGING_TIMEOUT_MS = 5 * 60_000
const DOCKER_TIMEOUT_MS = 2 * 60_000
const CLEANUP_TIMEOUT_MS = 30_000
// Exact statuses reject silent no-op launches as well as crashes.
const CASES = [
{
name: 'nofuse-userns-bundled-status',
// No runtime is running; the CLI must report that itself.
expectStatus: 1,
expectOutput: 'appRunning',
why: 'A command that needs the runtime must report its absence, not abort.'
},
{
name: 'nofuse-nosandbox-direct-binary-skills',
expectStatus: 0,
expectOutput: 'Usage: orca skills',
why: 'A direct binary launch that reaches JavaScript must run the command, not boot a GUI (#14229).'
},
{
name: 'stale-display-nosandbox-direct-binary-gui',
expectStatus: 1,
expectOutput: 'needs a usable display server',
why: 'A stale DISPLAY value must diagnose the unreachable endpoint instead of dying in uv_close (#13719).'
}
]
try {
if (!appImage) {
fail(
'Usage: run-linux-cli-launch-contract-docker.mjs --appimage /path/to/orca-linux.AppImage [--platform linux/amd64|linux/arm64]'
)
}
if (commandArgs.includes('--platform') && !platform) {
fail('Missing value for --platform')
}
if (platform !== null && platform !== 'linux/amd64' && platform !== 'linux/arm64') {
fail(`Unsupported --platform: ${platform}`)
}
if (!existsSync(appImage)) {
fail(`AppImage not found: ${appImage}`)
}
docker(['volume', 'create', artifactVolume], { timeoutMs: DOCKER_TIMEOUT_MS })
artifactVolumeCreated = true
buildImage()
stageArtifacts()
runContract()
console.log('\nLinux CLI launch contract passed.')
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))
process.exitCode = 1
} finally {
for (const container of containers) {
docker(['rm', '-f', container], { allowFailure: true, timeoutMs: CLEANUP_TIMEOUT_MS })
}
if (artifactVolumeCreated) {
docker(['volume', 'rm', artifactVolume], {
allowFailure: true,
timeoutMs: CLEANUP_TIMEOUT_MS
})
}
docker(['image', 'rm', tag], { allowFailure: true, timeoutMs: CLEANUP_TIMEOUT_MS })
}
function runContract() {
const failures = []
for (const testCase of CASES) {
const output = runCase(testCase.name)
const statusMatch = /^RESULT status=(\d+)/m.exec(output)
if (!statusMatch) {
failures.push(`${testCase.name}: ${firstLine(output)}\n ${testCase.why}`)
console.log(` FAIL ${testCase.name} — ${firstLine(output)}`)
continue
}
const status = Number(statusMatch[1])
// Avoid asserting harness’s RESULT case-name echo: expected substring must come from CLI output.
const commandOutput = output
.split('\n')
.filter((line) => !/^(?:RESULT|CRASHED|PRECONDITION_FAILED) /.test(line))
.join('\n')
const matchesOutput =
typeof testCase.expectOutput === 'string'
? commandOutput.includes(testCase.expectOutput)
: testCase.expectOutput.test(commandOutput)
if (status !== testCase.expectStatus || !matchesOutput) {
failures.push(
`${testCase.name}: expected status ${testCase.expectStatus} and ${testCase.expectOutput}, ` +
`got status ${status}\n ${testCase.why}`
)
console.log(` FAIL ${testCase.name} — status ${status}`)
continue
}
console.log(` ok ${testCase.name} (status ${status})`)
}
if (failures.length > 0) {
fail(`Linux CLI launch contract failed:\n - ${failures.join('\n - ')}`)
}
}
function runCase(caseName) {
const container = `orca-cli-contract-${caseName}-${suffix}`
containers.add(container)
// FUSE and extra capabilities would invalidate the test conditions.
return docker(
[
'run',
...dockerPlatformArgs,
'--name',
container,
'--rm',
'-v',
`${artifactVolume}:/artifacts`,
tag,
caseName
],
{ allowFailure: true, capture: true, timeoutMs: CASE_TIMEOUT_MS }
)
}
function buildImage() {
console.log(`Building ${tag}…`)
const buildArgs = [
'build',
...dockerPlatformArgs,
...(process.env.ORCA_CLI_FIXTURE_CACHE_IMAGE
? ['--cache-from', process.env.ORCA_CLI_FIXTURE_CACHE_IMAGE]
: []),
'-f',
'config/docker/cli-launch-contract/Dockerfile',
'-t',
tag,
'config/docker/cli-launch-contract'
]
// Why: apt fetches from archive.ubuntu.com stall or fail mid-sync; a second build usually lands on a healthy index.
try {
docker(buildArgs, { timeoutMs: BUILD_TIMEOUT_MS })
} catch (error) {
console.error(
`${error instanceof Error ? error.message : String(error)}\nRetrying docker build once…`
)
docker(buildArgs, { timeoutMs: BUILD_TIMEOUT_MS })
}
}
// Extract unprivileged so chrome-sandbox is not root-owned setuid.
function stageArtifacts() {
console.log('Staging the AppImage payload…')
const container = `orca-cli-contract-stage-${suffix}`
containers.add(container)
docker(
[
'run',
...dockerPlatformArgs,
'--name',
container,
'--rm',
'-v',
`${artifactVolume}:/artifacts`,
'-v',
`${appImage}:/input/orca-linux.AppImage:ro`,
'--entrypoint',
'bash',
tag,
'-lc',
[
'set -euo pipefail',
'cp /input/orca-linux.AppImage /artifacts/orca-linux.AppImage',
'chmod +x /artifacts/orca-linux.AppImage',
'chown -R orca:orca /artifacts',
// Use the AppImage runtime's no-FUSE extraction path.
'cd /artifacts && runuser --user orca -- ./orca-linux.AppImage --appimage-extract >/dev/null',
'test -x /artifacts/squashfs-root/resources/bin/orca-ide'
].join(' && ')
],
{ timeoutMs: STAGING_TIMEOUT_MS }
)
}
function docker(args, options = {}) {
try {
const output = execFileSync('docker', args, {
encoding: 'utf8',
stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
timeout: options.timeoutMs ?? DOCKER_TIMEOUT_MS,
killSignal: 'SIGTERM'
})
return output ?? ''
} catch (error) {
const timedOut = error instanceof Error && 'code' in error && error.code === 'ETIMEDOUT'
if (timedOut) {
const message = `docker ${args.join(' ')} timed out after ${options.timeoutMs ?? DOCKER_TIMEOUT_MS}ms`
if (!options.allowFailure) {
fail(message)
}
return message
}
if (!options.allowFailure) {
fail(
`docker ${args.join(' ')} failed: ${error instanceof Error ? error.message : String(error)}`
)
}
return `${error?.stdout ?? ''}${error?.stderr ?? ''}`
}
}
function firstLine(value) {
return (value ?? '').trim().split('\n')[0] || '(no output)'
}
function valueAfter(flag) {
const index = commandArgs.indexOf(flag)
return index === -1 ? null : (commandArgs[index + 1] ?? null)
}
function fail(message) {
throw new Error(message)
}