citty-test-utils
Version:
Unified testing framework for CLI applications with auto-detecting local/cleanroom execution, vitest config integration, and simplified scenario DSL.
137 lines (125 loc) • 3.88 kB
JavaScript
// src/commands/runner/execute.js - Runner execute verb command
import { defineCommand } from 'citty'
import { spawn, exec } from 'node:child_process'
import { promisify } from 'node:util'
import { runCitty } from '../../core/runners/cleanroom-runner.js'
const execAsync = promisify(exec)
export const executeCommand = defineCommand({
meta: {
name: 'execute',
description: 'Execute command with custom runner',
},
args: {
command: {
type: 'positional',
description: 'Command to run',
required: true,
},
environment: {
type: 'string',
description: 'Runner environment (local, cleanroom)',
default: 'local',
},
timeout: {
type: 'number',
description: 'Command timeout in milliseconds',
default: 10000,
},
cwd: {
type: 'string',
description: 'Working directory',
default: '.',
},
},
run: async (ctx) => {
const { command, environment, timeout, cwd, json, verbose } = ctx.args
if (verbose) {
console.error(`Running command "${command}" in ${environment} environment`)
}
let result
if (environment === 'cleanroom') {
// For cleanroom, we need to use runCitty but with a different approach
// Since runCitty is designed for CLI commands, we'll use it to run the command
// through the CLI's runner execute functionality - NO try-catch, fail fast!
const cleanroomResult = await runCitty(
['runner', 'execute', command, '--environment', 'local'],
{
cwd: '/app',
timeout,
env: { TEST_CLI: 'true' },
}
)
result = {
environment: 'cleanroom',
command,
exitCode: cleanroomResult.exitCode,
stdout: cleanroomResult.stdout,
stderr: cleanroomResult.stderr,
success: cleanroomResult.exitCode === 0,
timestamp: new Date().toISOString(),
}
} else {
// Run command locally using exec - let errors bubble up
const { stdout, stderr } = await execAsync(command, {
cwd,
timeout,
env: { ...process.env },
}).catch(error => {
// Transform exec errors into result format but still throw
const errorResult = {
environment: 'local',
command,
exitCode: error.code || 1,
stdout: error.stdout || '',
stderr: error.stderr || error.message,
success: false,
timestamp: new Date().toISOString(),
}
// Output the error result then throw
if (json) {
console.log(JSON.stringify(errorResult))
} else {
console.log(`Command: ${command}`)
console.log(`Environment: ${environment}`)
console.log(`Exit Code: ${errorResult.exitCode}`)
console.log(`Success: ❌`)
if (errorResult.stdout) {
console.log('Output:')
console.log(errorResult.stdout)
}
if (errorResult.stderr) {
console.log('Errors:')
console.log(errorResult.stderr)
}
}
throw error
})
result = {
environment: 'local',
command,
exitCode: 0,
stdout: stdout.trim(),
stderr: stderr.trim(),
success: true,
timestamp: new Date().toISOString(),
}
}
if (json) {
console.log(JSON.stringify(result))
} else {
console.log(`Command: ${command}`)
console.log(`Environment: ${environment}`)
console.log(`Exit Code: ${result.exitCode}`)
console.log(`Success: ${result.success ? '✅' : '❌'}`)
if (result.stdout) {
console.log('Output:')
console.log(result.stdout)
}
if (result.stderr) {
console.log('Errors:')
console.log(result.stderr)
}
}
},
})