mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
Guard empty argument-array expansion so the bundled setup recipe runs under Bash 3.2 with unset-variable checks. Preserve exact arguments and cleanup exit behavior; normalize only test fixture line endings. Co-authored-by: outof-place <contact@outofplace.space>
206 lines
7.4 KiB
JavaScript
206 lines
7.4 KiB
JavaScript
import { execFile } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { delimiter, join, resolve } from 'node:path'
|
|
import { promisify } from 'node:util'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
const run = promisify(execFile)
|
|
const referenceRoot = resolve(
|
|
import.meta.dirname,
|
|
'../../skill-guides/orca-per-workspace-env/references'
|
|
)
|
|
const vercel = await readFile(resolve(referenceRoot, 'provider-vercel.md'), 'utf8')
|
|
const ssh = await readFile(resolve(referenceRoot, 'ssh-host.md'), 'utf8')
|
|
const cleanup = vercel.match(/```bash\n(cleanup_snapshot\(\) \{[\s\S]*?\n\})\n```/u)?.[1]
|
|
|
|
async function executeShell(shell, script, env = {}) {
|
|
try {
|
|
const output = await run(shell, ['-c', script], {
|
|
env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1', ...env }
|
|
})
|
|
return { ...output, code: 0 }
|
|
} catch (error) {
|
|
return { stdout: error.stdout, stderr: error.stderr, code: error.code }
|
|
}
|
|
}
|
|
|
|
// Stock macOS Bash must run even when PATH starts with a newer Bash.
|
|
const shells = existsSync('/bin/bash') ? ['/bin/bash', 'bash'] : ['bash']
|
|
|
|
describe.skipIf(process.platform === 'win32').each(shells)(
|
|
'recipe shell examples (%s)',
|
|
(shell) => {
|
|
const runShell = (script, env) => executeShell(shell, script, env)
|
|
|
|
it.each(['base', 'auth'])('cleans the %s sandbox on failure and success', async (phase) => {
|
|
expect(cleanup).toBeDefined()
|
|
const trap = vercel.match(new RegExp(`trap 'cleanup_snapshot "\\$${phase}"' EXIT`, 'u'))?.[0]
|
|
expect(trap).toBeDefined()
|
|
expect(vercel.indexOf(trap)).toBeLessThan(
|
|
vercel.indexOf(`vercel sandbox create --name "$${phase}"`)
|
|
)
|
|
for (const exitCode of [0, 7]) {
|
|
const result = await runShell(`set -euo pipefail
|
|
${cleanup}
|
|
vercel_args=(--scope test-scope)
|
|
${phase}=unique-test-sandbox
|
|
vercel() { printf '%s\\n' "$@"; }
|
|
${trap}
|
|
exit ${exitCode}`)
|
|
expect(result.code).toBe(exitCode)
|
|
expect(result.stderr).toBe('sandbox\nremove\nunique-test-sandbox\n--scope\ntest-scope\n')
|
|
}
|
|
})
|
|
|
|
it('reports failed cleanup even after an otherwise successful snapshot', async () => {
|
|
const result = await runShell(`set -euo pipefail
|
|
${cleanup}
|
|
vercel_args=()
|
|
vercel() { return 9; }
|
|
trap 'cleanup_snapshot unique-test-sandbox' EXIT
|
|
exit 0`)
|
|
expect(result.code).toBe(1)
|
|
expect(result.stderr).toContain('Sandbox cleanup failed for unique-test-sandbox')
|
|
})
|
|
|
|
it.each([
|
|
{ scope: '', project: '' },
|
|
{ scope: 'scope with spaces *', project: '' },
|
|
{ scope: '', project: 'project with spaces ?' },
|
|
{ scope: 'scope with spaces *', project: 'project with spaces ?' }
|
|
])(
|
|
'runs the full create recipe with scope "$scope" and project "$project"',
|
|
async ({ scope, project }) => {
|
|
const result = await runCreateRecipe(shell, {
|
|
VERCEL_SCOPE: scope,
|
|
VERCEL_PROJECT: project
|
|
})
|
|
expect(result.code).toBe(0)
|
|
const output = JSON.parse(result.stdout)
|
|
expect(output.userData).toMatchObject({
|
|
provider: 'vercel-sandbox',
|
|
resourceId: 'orca-fixture-123',
|
|
snapshotId: 'snap_fixture'
|
|
})
|
|
expect(result.calls.map((args) => args.slice(0, 2))).toEqual([
|
|
['sandbox', 'create'],
|
|
['sandbox', 'exec'],
|
|
['sandbox', 'exec']
|
|
])
|
|
const expectedFlags = [
|
|
...(scope ? ['--scope', scope] : []),
|
|
...(project ? ['--project', project] : [])
|
|
]
|
|
for (const args of result.calls) {
|
|
const start = args.findIndex((arg) => arg === '--scope' || arg === '--project')
|
|
if (expectedFlags.length) {
|
|
expect(args.slice(start, start + expectedFlags.length)).toEqual(expectedFlags)
|
|
} else {
|
|
expect(start).toBe(-1)
|
|
}
|
|
expect(args).not.toContain('')
|
|
}
|
|
}
|
|
)
|
|
|
|
it('removes a failed create with no optional flags', async () => {
|
|
const result = await runCreateRecipe(shell, { VERCEL_CREATE_EXIT: '7' })
|
|
expect(result.code).toBe(7)
|
|
expect(result.calls.map((args) => args.slice(0, 2))).toEqual([
|
|
['sandbox', 'create'],
|
|
['sandbox', 'remove']
|
|
])
|
|
expect(result.calls[1]).toEqual(['sandbox', 'remove', 'orca-fixture-123'])
|
|
expect(result.stderr).not.toContain('unbound variable')
|
|
})
|
|
|
|
it('disables Git prompts when the Vercel token is absent', async () => {
|
|
const prefix = vercel.match(
|
|
/-- bash -lc 'set -euo pipefail; cd "\$ORCA_PROJECT_ROOT"; \\\n([\s\S]*?) git fetch/u
|
|
)?.[1]
|
|
expect(prefix).toBeDefined()
|
|
const result = await runShell(
|
|
`set -euo pipefail\nunset GH_TOKEN\n${prefix}\nprintf '%s' "$GIT_TERMINAL_PROMPT"`
|
|
)
|
|
expect(result.code).toBe(0)
|
|
expect(result.stdout).toBe('0')
|
|
})
|
|
|
|
it('uses host credentials and refuses unverified SSH hosts without forwarding tokens', async () => {
|
|
const script = ssh.match(/```bash\n(#!\/usr\/bin\/env bash[\s\S]*?)\n```/u)?.[1]
|
|
expect(script).toBeDefined()
|
|
const sync = script.slice(0, script.indexOf('# 2. print'))
|
|
const result = await runShell(
|
|
`ssh() { printf '%s\\n' "$@"; }
|
|
ssh_username=worker
|
|
host=example.test
|
|
ssh_port=2222
|
|
project_root='/remote/path with spaces'
|
|
repo_url=https://example.test/org/repo.git
|
|
repo_ref=main
|
|
${sync}`,
|
|
{ GH_TOKEN: 'test-token-must-not-be-forwarded' }
|
|
)
|
|
expect(result.code).toBe(0)
|
|
expect(result.stderr).toContain('StrictHostKeyChecking=yes')
|
|
expect(result.stderr).toContain('BatchMode=yes')
|
|
expect(result.stderr).not.toContain('test-token-must-not-be-forwarded')
|
|
expect(result.stderr).not.toContain('GH_TOKEN=')
|
|
expect(script).toContain('export GIT_TERMINAL_PROMPT=0')
|
|
})
|
|
}
|
|
)
|
|
|
|
async function runCreateRecipe(shell, overrides) {
|
|
const script = vercel.match(/```bash\n(#!\/usr\/bin\/env bash[\s\S]*?)\n```/u)?.[1]
|
|
expect(script).toBeDefined()
|
|
const directory = await mkdtemp(join(tmpdir(), 'orca-vercel-recipe-'))
|
|
const bin = join(directory, 'bin')
|
|
const log = join(directory, 'calls.jsonl')
|
|
try {
|
|
await mkdir(bin)
|
|
const command = join(bin, 'vercel')
|
|
await writeFile(
|
|
command,
|
|
`#!/usr/bin/env node
|
|
const { appendFileSync } = require('node:fs')
|
|
const args = process.argv.slice(2)
|
|
appendFileSync(process.env.VERCEL_FIXTURE_LOG, JSON.stringify(args) + '\\n')
|
|
if (args[1] === 'create') {
|
|
if (process.env.VERCEL_CREATE_EXIT) process.exit(Number(process.env.VERCEL_CREATE_EXIT))
|
|
console.log('https://fixture.vercel.run')
|
|
} else if (args[1] === 'exec' && args.includes('60s')) {
|
|
console.log(JSON.stringify({ userData: {} }))
|
|
}
|
|
`
|
|
)
|
|
await chmod(command, 0o755)
|
|
const result = await executeShell(
|
|
shell,
|
|
`scope="$VERCEL_SCOPE"; project="$VERCEL_PROJECT"
|
|
snapshot_id=snap_fixture; port=6768; repo_url=https://example.test/repo.git; repo_ref=main; project_root='/fixture repo'
|
|
${script}`,
|
|
{
|
|
PATH: `${bin}${delimiter}${process.env.PATH}`,
|
|
VERCEL_FIXTURE_LOG: log,
|
|
VERCEL_SCOPE: '',
|
|
VERCEL_PROJECT: '',
|
|
VERCEL_CREATE_EXIT: '',
|
|
GH_TOKEN: 'fixture-token',
|
|
ORCA_RECIPE_ID: 'fixture',
|
|
ORCA_VM_INSTANCE_ID: '123',
|
|
...overrides
|
|
}
|
|
)
|
|
const calls = (await readFile(log, 'utf8'))
|
|
.trim()
|
|
.split('\n')
|
|
.map((line) => JSON.parse(line))
|
|
return { ...result, calls }
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true })
|
|
}
|
|
}
|