verify-grid 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/cli.js +15 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
- 📊 **Multiple Formats** - Table, minimal, or JSON output
|
|
27
27
|
- 🎨 **Horizontal Layout** - Optimized for terminal space (services × tasks)
|
|
28
28
|
- 🔧 **Task Overrides** - Customize tasks per service
|
|
29
|
+
- ✨ **Optional Tasks** - Skip tasks that don't apply to certain services
|
|
29
30
|
- ⏱️ **Timeout Support** - Set time limits for tasks
|
|
30
31
|
- 🌍 **Environment Variables** - Per-task and per-service configuration
|
|
31
32
|
- 🎲 **Random Execution** - Test robustness with randomized order
|
|
@@ -128,6 +129,7 @@ qa run --random
|
|
|
128
129
|
shell: false, // Run in shell (optional)
|
|
129
130
|
timeout: 30000, // Timeout in ms (optional)
|
|
130
131
|
priority: 10, // Higher = earlier (optional)
|
|
132
|
+
optional: false, // Skip if not applicable (optional)
|
|
131
133
|
env: { // Environment variables (optional)
|
|
132
134
|
NODE_ENV: 'test'
|
|
133
135
|
}
|
|
@@ -145,6 +147,7 @@ qa run --random
|
|
|
145
147
|
| `shell` | `boolean` | ❌ | Run in shell (default: false) |
|
|
146
148
|
| `timeout` | `number` | ❌ | Timeout in milliseconds |
|
|
147
149
|
| `priority` | `number` | ❌ | Execution priority (higher first, default: 0) |
|
|
150
|
+
| `optional` | `boolean` | ❌ | Skip task if command not defined (default: false) |
|
|
148
151
|
| `env` | `Record<string, string>` | ❌ | Environment variables |
|
|
149
152
|
|
|
150
153
|
### Service Definition
|
|
@@ -204,6 +207,10 @@ export default {
|
|
|
204
207
|
command: 'tsc',
|
|
205
208
|
args: ['--noEmit'],
|
|
206
209
|
},
|
|
210
|
+
screenshot: {
|
|
211
|
+
command: '',
|
|
212
|
+
optional: true, // Only runs if service defines it
|
|
213
|
+
},
|
|
207
214
|
},
|
|
208
215
|
services: [
|
|
209
216
|
{
|
|
@@ -221,6 +228,12 @@ export default {
|
|
|
221
228
|
name: 'web',
|
|
222
229
|
cwd: './services/web',
|
|
223
230
|
env: { PORT: '3001' },
|
|
231
|
+
tasks: {
|
|
232
|
+
screenshot: {
|
|
233
|
+
command: 'npm',
|
|
234
|
+
args: ['run', 'screenshot'],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
224
237
|
},
|
|
225
238
|
{
|
|
226
239
|
name: 'shared',
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ var TaskDefSchema = z.object({
|
|
|
18
18
|
shell: z.boolean().optional(),
|
|
19
19
|
timeout: z.number().positive().optional(),
|
|
20
20
|
priority: z.number().optional(),
|
|
21
|
+
optional: z.boolean().optional(),
|
|
21
22
|
env: z.record(z.string()).optional()
|
|
22
23
|
});
|
|
23
24
|
var ServiceDefSchema = z.object({
|
|
@@ -29,6 +30,7 @@ var ServiceDefSchema = z.object({
|
|
|
29
30
|
shell: z.boolean().optional(),
|
|
30
31
|
timeout: z.number().positive().optional(),
|
|
31
32
|
priority: z.number().optional(),
|
|
33
|
+
optional: z.boolean().optional(),
|
|
32
34
|
env: z.record(z.string()).optional()
|
|
33
35
|
})).optional(),
|
|
34
36
|
env: z.record(z.string()).optional()
|
|
@@ -133,7 +135,12 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
133
135
|
continue;
|
|
134
136
|
}
|
|
135
137
|
const taskOverride = service.tasks?.[taskName];
|
|
136
|
-
|
|
138
|
+
const isOptional = taskOverride?.optional ?? globalTask.optional ?? false;
|
|
139
|
+
const finalCommand = taskOverride?.command ?? globalTask.command;
|
|
140
|
+
if (!finalCommand) {
|
|
141
|
+
if (isOptional) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
137
144
|
jobs.push({
|
|
138
145
|
id: `${taskName}:${serviceName}`,
|
|
139
146
|
taskName,
|
|
@@ -146,11 +153,12 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
146
153
|
}
|
|
147
154
|
const mergedTask = {
|
|
148
155
|
name: taskName,
|
|
149
|
-
command:
|
|
156
|
+
command: finalCommand,
|
|
150
157
|
args: taskOverride?.args ?? globalTask.args,
|
|
151
158
|
shell: taskOverride?.shell ?? globalTask.shell ?? true,
|
|
152
159
|
timeout: taskOverride?.timeout ?? globalTask.timeout,
|
|
153
160
|
priority: taskOverride?.priority ?? globalTask.priority,
|
|
161
|
+
optional: isOptional,
|
|
154
162
|
env: {
|
|
155
163
|
...globalTask.env,
|
|
156
164
|
...service.env,
|
|
@@ -167,6 +175,7 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
167
175
|
shell: mergedTask.shell,
|
|
168
176
|
timeout: mergedTask.timeout,
|
|
169
177
|
priority: mergedTask.priority,
|
|
178
|
+
optional: mergedTask.optional,
|
|
170
179
|
env: mergedTask.env,
|
|
171
180
|
status: "queued"
|
|
172
181
|
});
|
|
@@ -554,6 +563,10 @@ var WatchRenderer = class {
|
|
|
554
563
|
clearInterval(this.updateInterval);
|
|
555
564
|
this.updateInterval = null;
|
|
556
565
|
}
|
|
566
|
+
if (this.state) {
|
|
567
|
+
const output = renderTable(this.state);
|
|
568
|
+
logUpdate(output);
|
|
569
|
+
}
|
|
557
570
|
logUpdate.done();
|
|
558
571
|
}
|
|
559
572
|
render() {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/config/loader.ts","../src/config/schema.ts","../src/runner/generator.ts","../src/runner/runner.ts","../src/runner/executor.ts","../src/utils/log.ts","../src/render/table.ts","../src/utils/colors.ts","../src/utils/spinner.ts","../src/render/watch.ts","../src/report/aggregator.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { cac } from 'cac';\nimport { cpus } from 'node:os';\nimport { loadConfig } from './config/loader.js';\nimport { generateJobs } from './runner/generator.js';\nimport { Runner } from './runner/runner.js';\nimport { renderTable, renderMinimal, renderJson } from './render/table.js';\nimport { WatchRenderer } from './render/watch.js';\nimport { aggregateErrors, formatErrors } from './report/aggregator.js';\nimport { setColorsEnabled, green, yellow, red, cyan, dim } from './utils/colors.js';\nimport type { RunOptions, ServiceDef } from './model/types.js';\n\nconst cli = cac('qa');\n\ncli.usage(`${green('qa')} ${cyan('<command>')} ${dim('[options]')}`);\n\ncli\n .command('run', 'Run tasks across services')\n .option('--tasks <tasks>', 'Comma-separated list of tasks or \"all\"', { default: 'all' })\n .option('--services <services>', 'Comma-separated list of services or \"all\"', { default: 'all' })\n .option('--concurrency <n>', 'Maximum parallel jobs', { default: cpus().length })\n .option('--parallel', 'Alias for concurrency')\n .option('--fail-fast', 'Stop on first error', { default: false })\n .option('--random', 'Randomize job execution order', { default: false })\n .option('--no-watch', 'Disable live updates')\n .option('--format <format>', 'Output format: table, minimal, json', { default: 'table' })\n .option('--output <path>', 'Save JSON output to file')\n .option('--verbose', 'Verbose output', { default: false })\n .option('--quiet', 'Minimal output', { default: false })\n .option('--timeout <ms>', 'Timeout per task in milliseconds')\n .option('--no-color', 'Disable colors')\n .option('--config <path>', 'Path to config file')\n .action(async (options) => {\n try {\n const isTTY = process.stdout.isTTY;\n const colorsEnabled = options.color !== false && isTTY;\n setColorsEnabled(colorsEnabled);\n\n let config;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n console.error(red('✗ Config Error:'), error instanceof Error ? error.message : String(error));\n console.log(dim('\\nMake sure you have a qa.config.js file in your project root.'));\n console.log(dim('Example: https://github.com/your-repo/verify-grid#configuration'));\n process.exit(2);\n }\n\n const tasks = options.tasks === 'all' \n ? ['all'] \n : options.tasks.split(',').map((t: string) => t.trim());\n \n const services = options.services === 'all'\n ? ['all']\n : options.services.split(',').map((s: string) => s.trim());\n\n const runOptions: RunOptions = {\n tasks,\n services,\n concurrency: Number(options.concurrency),\n failFast: options.failFast,\n watch: (options.watch !== false && isTTY && options.format === 'table'),\n format: options.format,\n output: options.output,\n verbose: options.verbose,\n quiet: options.quiet,\n timeout: options.timeout ? Number(options.timeout) : undefined,\n noColor: !colorsEnabled,\n random: options.random,\n };\n\n let jobs;\n try {\n jobs = generateJobs(config, tasks, services);\n } catch (error) {\n console.error(red('✗ Job Generation Error:'), error instanceof Error ? error.message : String(error));\n process.exit(2);\n }\n\n if (jobs.length === 0) {\n console.error(yellow('⚠ No jobs to run'));\n console.log(dim('\\nCheck your --tasks and --services filters.'));\n console.log(dim(`Available tasks: ${Object.keys(config.tasks).join(', ')}`));\n console.log(dim(`Available services: ${config.services.map((s: ServiceDef) => s.name).join(', ')}`));\n process.exit(2);\n }\n\n const runner = new Runner(runOptions);\n let watchRenderer: WatchRenderer | null = null;\n\n if (runOptions.watch) {\n watchRenderer = new WatchRenderer();\n watchRenderer.start(runner.getState());\n\n runner.on('jobStart', () => {\n watchRenderer?.update(runner.getState());\n });\n\n runner.on('jobComplete', () => {\n watchRenderer?.update(runner.getState());\n });\n\n runner.on('jobError', () => {\n watchRenderer?.update(runner.getState());\n });\n }\n\n const state = await runner.run(jobs);\n\n if (watchRenderer) {\n watchRenderer.stop();\n }\n\n if (!runOptions.quiet && !runOptions.watch) {\n let output: string;\n \n if (runOptions.format === 'json') {\n output = renderJson(state);\n } else if (runOptions.format === 'minimal') {\n output = renderMinimal(state);\n } else {\n output = renderTable(state);\n }\n\n console.log(output);\n\n const errors = aggregateErrors(state);\n if (errors.length > 0 && runOptions.format !== 'json') {\n console.log(formatErrors(errors));\n }\n }\n\n if (runOptions.watch && !runOptions.quiet) {\n const errors = aggregateErrors(state);\n if (errors.length > 0) {\n console.log('\\n' + formatErrors(errors));\n }\n }\n\n if (options.output) {\n const { writeFileSync } = await import('node:fs');\n writeFileSync(options.output, renderJson(state), 'utf-8');\n }\n\n const hasErrors = Array.from(state.jobs.values()).some(j => j.status === 'error');\n process.exit(hasErrors ? 1 : 0);\n\n } catch (error) {\n console.error(red('✗ Unexpected Error:'), error instanceof Error ? error.message : String(error));\n if (error instanceof Error && error.stack) {\n console.error(dim(error.stack));\n }\n process.exit(2);\n }\n });\n\ncli.help();\ncli.version('0.1.0');\n\nif (process.argv.length === 2) {\n console.log(green('🚀 QA Task Runner\\n'));\n console.log('Quick start:');\n console.log(` ${cyan('qa run')} ${dim('# Run all tasks on all services')}`);\n console.log(` ${cyan('qa run --tasks lint')} ${dim('# Run specific task')}`);\n console.log(` ${cyan('qa run --services api,web')} ${dim('# Run on specific services')}`);\n console.log(` ${cyan('qa run --fail-fast')} ${dim('# Stop on first error')}`);\n console.log(` ${cyan('qa run --concurrency 4')} ${dim('# Limit parallel jobs')}`);\n console.log('\\nOptions:');\n console.log(` ${cyan('--help')} ${dim('# Show all available options')}`);\n console.log(` ${cyan('--version')} ${dim('# Show version')}`);\n console.log('\\nConfiguration:');\n console.log(` Create ${yellow('qa.config.js')} in your project root.`);\n console.log(` See: ${dim('https://github.com/your-repo/verify-grid#configuration')}`);\n process.exit(0);\n}\n\ncli.parse();\n","import { pathToFileURL } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { ConfigSchema } from './schema.js';\nimport type { Config } from '../model/types.js';\n\nconst CONFIG_FILES = ['qa.config.ts', 'qa.config.js', 'qa.config.mjs'];\n\nexport async function findConfig(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = resolve(startDir);\n const root = resolve('/');\n\n while (currentDir !== root) {\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(currentDir, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n currentDir = dirname(currentDir);\n }\n\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(root, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n\n return null;\n}\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const path = configPath || await findConfig();\n \n if (!path) {\n throw new Error(\n 'No config file found. Create qa.config.ts or qa.config.js in your project root.'\n );\n }\n\n if (!existsSync(path)) {\n throw new Error(`Config file not found: ${path}`);\n }\n\n try {\n const fileUrl = pathToFileURL(path).href;\n const module = await import(fileUrl);\n const configData = module.default || module;\n\n const result = ConfigSchema.safeParse(configData);\n \n if (!result.success) {\n const errors = result.error.errors.map(err => \n ` - ${err.path.join('.')}: ${err.message}`\n ).join('\\n');\n throw new Error(`Invalid config:\\n${errors}`);\n }\n\n const serviceNames = new Set<string>();\n for (const service of result.data.services) {\n if (serviceNames.has(service.name)) {\n throw new Error(`Duplicate service name: ${service.name}`);\n }\n serviceNames.add(service.name);\n }\n\n const config: Config = {\n services: result.data.services,\n tasks: Object.entries(result.data.tasks).reduce((acc, [name, def]) => {\n acc[name] = { name, ...def };\n return acc;\n }, {} as Record<string, any>),\n };\n\n return config;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to load config: ${String(error)}`);\n }\n}\n","import { z } from 'zod';\n\nexport const TaskDefSchema = z.object({\n name: z.string(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ServiceDefSchema = z.object({\n name: z.string(),\n cwd: z.string(),\n tasks: z.record(z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n env: z.record(z.string()).optional(),\n })).optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ConfigSchema = z.object({\n services: z.array(ServiceDefSchema),\n tasks: z.record(TaskDefSchema.omit({ name: true })),\n});\n\nexport type ConfigInput = z.input<typeof ConfigSchema>;\nexport type ConfigOutput = z.output<typeof ConfigSchema>;\n","import { existsSync } from 'node:fs';\nimport type { Config, Job, TaskDef } from '../model/types.js';\n\nexport function generateJobs(\n config: Config,\n taskNames: string[],\n serviceNames: string[]\n): Job[] {\n const jobs: Job[] = [];\n const resolvedServiceNames = serviceNames.includes('all') \n ? config.services.map(s => s.name)\n : serviceNames;\n\n const resolvedTaskNames = taskNames.includes('all')\n ? Object.keys(config.tasks)\n : taskNames;\n\n for (const taskName of resolvedTaskNames) {\n const globalTask = config.tasks[taskName];\n \n if (!globalTask) {\n throw new Error(`Unknown task: ${taskName}`);\n }\n\n for (const serviceName of resolvedServiceNames) {\n const service = config.services.find(s => s.name === serviceName);\n \n if (!service) {\n throw new Error(`Unknown service: ${serviceName}`);\n }\n\n if (!existsSync(service.cwd)) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const taskOverride = service.tasks?.[taskName];\n \n if (taskOverride && taskOverride.command === undefined && !globalTask.command) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const mergedTask: TaskDef = {\n name: taskName,\n command: taskOverride?.command ?? globalTask.command,\n args: taskOverride?.args ?? globalTask.args,\n shell: taskOverride?.shell ?? globalTask.shell ?? true,\n timeout: taskOverride?.timeout ?? globalTask.timeout,\n priority: taskOverride?.priority ?? globalTask.priority,\n env: {\n ...globalTask.env,\n ...service.env,\n ...taskOverride?.env,\n },\n };\n\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: mergedTask.command,\n args: mergedTask.args,\n shell: mergedTask.shell,\n timeout: mergedTask.timeout,\n priority: mergedTask.priority,\n env: mergedTask.env,\n status: 'queued',\n });\n }\n }\n\n return jobs;\n}\n","import PQueue from 'p-queue';\nimport { EventEmitter } from 'node:events';\nimport type { Job, JobResult, MatrixState, RunOptions } from '../model/types.js';\nimport { executeJob } from './executor.js';\n\nexport interface RunnerEvents {\n jobStart: (job: Job) => void;\n jobComplete: (result: JobResult) => void;\n jobError: (result: JobResult) => void;\n runComplete: (state: MatrixState) => void;\n runCanceled: () => void;\n}\n\nexport class Runner extends EventEmitter {\n private queue: PQueue;\n private state: MatrixState;\n private shouldStop = false;\n private options: RunOptions;\n\n constructor(options: RunOptions) {\n super();\n this.options = options;\n this.queue = new PQueue({ concurrency: options.concurrency });\n this.state = {\n jobs: new Map(),\n results: new Map(),\n errors: [],\n startedAt: Date.now(),\n };\n\n process.on('SIGINT', () => this.handleInterrupt());\n process.on('SIGTERM', () => this.handleInterrupt());\n }\n\n private handleInterrupt(): void {\n if (this.shouldStop) {\n process.exit(130);\n }\n \n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, job] of this.state.jobs) {\n if (job.status === 'queued') {\n job.status = 'canceled';\n this.state.jobs.set(id, job);\n }\n }\n \n this.emit('runCanceled');\n }\n\n async run(jobs: Job[]): Promise<MatrixState> {\n for (const job of jobs) {\n this.state.jobs.set(job.id, job);\n }\n\n let executableJobs = jobs.filter(j => j.status === 'queued');\n \n if (this.options.random) {\n executableJobs = this.shuffleJobs(executableJobs);\n } else {\n executableJobs = this.sortJobsByPriority(executableJobs);\n }\n \n for (const job of executableJobs) {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n continue;\n }\n\n this.queue.add(async () => {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n return;\n }\n\n job.status = 'running';\n job.startedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.emit('jobStart', job);\n\n const result = await executeJob(job);\n \n job.status = result.exitCode === 0 ? 'done' : 'error';\n job.endedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.state.results.set(job.id, result);\n\n if (result.exitCode !== 0) {\n this.emit('jobError', result);\n \n if (this.options.failFast) {\n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, j] of this.state.jobs) {\n if (j.status === 'queued') {\n j.status = 'canceled';\n this.state.jobs.set(id, j);\n }\n }\n }\n } else {\n this.emit('jobComplete', result);\n }\n });\n }\n\n await this.queue.onIdle();\n \n this.state.endedAt = Date.now();\n this.emit('runComplete', this.state);\n \n return this.state;\n }\n\n getState(): MatrixState {\n return this.state;\n }\n\n private sortJobsByPriority(jobs: Job[]): Job[] {\n return jobs.slice().sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA;\n });\n }\n\n private shuffleJobs(jobs: Job[]): Job[] {\n const shuffled = jobs.slice();\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = shuffled[i];\n shuffled[i] = shuffled[j]!;\n shuffled[j] = temp!;\n }\n return shuffled;\n }\n}\n","import { execa, type ExecaError } from 'execa';\nimport type { Job, JobResult } from '../model/types.js';\nimport { LogBuffer } from '../utils/log.js';\n\nexport async function executeJob(job: Job): Promise<JobResult> {\n const startTime = Date.now();\n const stdoutBuffer = new LogBuffer();\n const stderrBuffer = new LogBuffer();\n\n try {\n const args = job.args || [];\n const options: any = {\n cwd: job.cwd,\n env: { ...process.env, ...job.env },\n shell: job.shell,\n timeout: job.timeout,\n reject: false,\n all: true,\n };\n\n const result = await execa(job.command, args, options);\n\n stdoutBuffer.append(result.stdout || '');\n stderrBuffer.append(result.stderr || '');\n\n const durationMs = Date.now() - startTime;\n\n if (result.exitCode === 0) {\n return {\n job,\n exitCode: 0,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n };\n }\n\n return {\n job,\n exitCode: result.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType: result.signal ? 'signal' : 'exit',\n signal: result.signal || undefined,\n };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const execaError = error as ExecaError;\n\n if (execaError.stdout) stdoutBuffer.append(execaError.stdout);\n if (execaError.stderr) stderrBuffer.append(execaError.stderr);\n\n let errorType: 'exit' | 'timeout' | 'spawn' | 'signal' = 'exit';\n \n if (execaError.timedOut) {\n errorType = 'timeout';\n } else if (execaError.signal) {\n errorType = 'signal';\n } else if (execaError.exitCode === 127 || (error as any).code === 'ENOENT') {\n errorType = 'spawn';\n }\n\n return {\n job,\n exitCode: execaError.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType,\n signal: execaError.signal || undefined,\n };\n }\n}\n","const MAX_LOG_LINES = 200;\n\nexport function truncateLog(log: string, maxLines: number = MAX_LOG_LINES): string {\n const lines = log.split('\\n');\n if (lines.length <= maxLines) {\n return log;\n }\n return lines.slice(-maxLines).join('\\n');\n}\n\nexport function extractErrorSnippet(stderr: string, stdout: string, maxLines: number = 10): string {\n const errorLog = stderr || stdout;\n const lines = errorLog.split('\\n').filter(line => line.trim());\n \n if (lines.length === 0) {\n return 'No error output';\n }\n \n const relevantLines = lines.slice(-maxLines);\n return relevantLines.join('\\n');\n}\n\nexport class LogBuffer {\n private lines: string[] = [];\n private maxLines: number;\n\n constructor(maxLines: number = MAX_LOG_LINES) {\n this.maxLines = maxLines;\n }\n\n append(data: string): void {\n const newLines = data.split('\\n');\n this.lines.push(...newLines);\n \n if (this.lines.length > this.maxLines) {\n this.lines = this.lines.slice(-this.maxLines);\n }\n }\n\n getContent(): string {\n return this.lines.join('\\n');\n }\n\n clear(): void {\n this.lines = [];\n }\n}\n","import Table from 'cli-table3';\nimport type { MatrixState, Job } from '../model/types.js';\nimport { getStatusColor } from '../utils/colors.js';\nimport { Spinner } from '../utils/spinner.js';\n\nconst spinner = new Spinner();\n\nexport function renderTable(state: MatrixState): string {\n const taskNames = getUniqueTaskNames(state);\n const serviceGroups = groupJobsByService(state);\n\n const table = new Table({\n head: ['Service', ...taskNames],\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const [serviceName, jobs] of serviceGroups) {\n const row: string[] = [serviceName];\n \n for (const taskName of taskNames) {\n const job = jobs.find(j => j.taskName === taskName);\n \n if (job) {\n const statusColor = getStatusColor(job.status);\n let statusText: string = job.status;\n \n if (job.status === 'running') {\n statusText = `${spinner.next()} ${job.status}`;\n } else if (job.status === 'queued') {\n statusText = `⏳ ${job.status}`;\n }\n \n row.push(statusColor(statusText));\n } else {\n row.push('-');\n }\n }\n \n table.push(row);\n }\n\n return table.toString();\n}\n\nexport function renderMinimal(state: MatrixState): string {\n const lines: string[] = [];\n \n for (const [, job] of state.jobs) {\n const statusColor = getStatusColor(job.status);\n const statusText = statusColor(job.status);\n lines.push(`${job.taskName} ${job.serviceName} ${statusText}`);\n }\n \n return lines.join('\\n');\n}\n\nexport function renderJson(state: MatrixState): string {\n const matrix: Record<string, Record<string, any>> = {};\n \n for (const [, job] of state.jobs) {\n if (!matrix[job.taskName]) {\n matrix[job.taskName] = {};\n }\n \n const result = state.results.get(job.id);\n const taskMatrix = matrix[job.taskName];\n \n if (taskMatrix) {\n taskMatrix[job.serviceName] = {\n status: job.status,\n exitCode: result?.exitCode,\n durationMs: result?.durationMs,\n };\n }\n }\n\n const errors = Array.from(state.results.values())\n .filter(r => r.job.status === 'error')\n .map(r => ({\n taskName: r.job.taskName,\n serviceName: r.job.serviceName,\n exitCode: r.exitCode,\n durationMs: r.durationMs,\n errorType: r.errorType,\n }));\n\n const output = {\n startedAt: state.startedAt,\n endedAt: state.endedAt,\n durationMs: state.endedAt ? state.endedAt - state.startedAt : undefined,\n matrix,\n errors,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nfunction groupJobsByService(state: MatrixState): Map<string, Job[]> {\n const groups = new Map<string, Job[]>();\n \n for (const [, job] of state.jobs) {\n if (!groups.has(job.serviceName)) {\n groups.set(job.serviceName, []);\n }\n groups.get(job.serviceName)!.push(job);\n }\n \n return groups;\n}\n\nfunction getUniqueTaskNames(state: MatrixState): string[] {\n const taskNames = new Set<string>();\n \n for (const [, job] of state.jobs) {\n taskNames.add(job.taskName);\n }\n \n return Array.from(taskNames).sort();\n}\n","import kleur from 'kleur';\nimport type { JobStatus } from '../model/types.js';\n\nlet colorsEnabled = true;\n\nexport function setColorsEnabled(enabled: boolean): void {\n colorsEnabled = enabled;\n kleur.enabled = enabled;\n}\n\nexport function getStatusColor(status: JobStatus): (text: string) => string {\n if (!colorsEnabled) {\n return (text: string) => text;\n }\n\n switch (status) {\n case 'done':\n return kleur.green;\n case 'error':\n return kleur.red;\n case 'running':\n return kleur.blue;\n case 'queued':\n return kleur.yellow;\n case 'skipped':\n case 'canceled':\n return kleur.gray;\n default:\n return (text: string) => text;\n }\n}\n\nexport const colors = {\n error: kleur.red,\n success: kleur.green,\n warning: kleur.yellow,\n info: kleur.blue,\n dim: kleur.gray,\n bold: kleur.bold,\n};\n\nexport const green = kleur.green;\nexport const yellow = kleur.yellow;\nexport const red = kleur.red;\nexport const cyan = kleur.cyan;\nexport const dim = kleur.gray;\n","const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nexport class Spinner {\n private frame = 0;\n\n next(): string {\n const char = SPINNER_FRAMES[this.frame];\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n return char || '⠋';\n }\n\n reset(): void {\n this.frame = 0;\n }\n}\n","import logUpdate from 'log-update';\nimport type { MatrixState } from '../model/types.js';\nimport { renderTable } from './table.js';\n\nexport class WatchRenderer {\n private updateInterval: NodeJS.Timeout | null = null;\n private state: MatrixState | null = null;\n\n start(state: MatrixState): void {\n this.state = state;\n this.render();\n \n this.updateInterval = setInterval(() => {\n this.render();\n }, 80);\n }\n\n update(state: MatrixState): void {\n this.state = state;\n }\n\n stop(): void {\n if (this.updateInterval) {\n clearInterval(this.updateInterval);\n this.updateInterval = null;\n }\n logUpdate.done();\n }\n\n private render(): void {\n if (!this.state) return;\n \n const output = renderTable(this.state);\n logUpdate(output);\n }\n}\n","import type { MatrixState, ErrorEntry, JobResult } from '../model/types.js';\nimport { extractErrorSnippet } from '../utils/log.js';\n\nexport function aggregateErrors(state: MatrixState): ErrorEntry[] {\n const errors: ErrorEntry[] = [];\n\n for (const [, result] of state.results) {\n if (result.job.status === 'error') {\n const errorMessage = getErrorMessage(result);\n const logSnippet = extractErrorSnippet(result.stderr, result.stdout);\n\n errors.push({\n taskName: result.job.taskName,\n serviceName: result.job.serviceName,\n exitCode: result.exitCode,\n durationMs: result.durationMs,\n message: errorMessage,\n logSnippet,\n errorType: result.errorType,\n });\n }\n }\n\n errors.sort((a, b) => {\n if (a.taskName !== b.taskName) {\n return a.taskName.localeCompare(b.taskName);\n }\n return a.serviceName.localeCompare(b.serviceName);\n });\n\n return errors;\n}\n\nfunction getErrorMessage(result: JobResult): string {\n if (result.errorType === 'timeout') {\n return `Timeout after ${result.durationMs}ms`;\n }\n \n if (result.errorType === 'spawn') {\n return `Command not found: ${result.job.command}`;\n }\n \n if (result.errorType === 'signal') {\n return `Killed by signal: ${result.signal}`;\n }\n \n return `Exit code ${result.exitCode}`;\n}\n\nexport function formatErrors(errors: ErrorEntry[]): string {\n if (errors.length === 0) {\n return '';\n }\n\n const lines: string[] = ['', 'Errors:'];\n\n for (const error of errors) {\n lines.push(`- ${error.serviceName} has error in ${error.taskName} test:`);\n \n const snippet = error.logSnippet.trim();\n if (snippet) {\n const snippetLines = snippet.split('\\n').slice(0, 10);\n for (const line of snippetLines) {\n lines.push(` ${line}`);\n }\n } else {\n lines.push(` ${error.message}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["existsSync","colorsEnabled"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/config/loader.ts","../src/config/schema.ts","../src/runner/generator.ts","../src/runner/runner.ts","../src/runner/executor.ts","../src/utils/log.ts","../src/render/table.ts","../src/utils/colors.ts","../src/utils/spinner.ts","../src/render/watch.ts","../src/report/aggregator.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { cac } from 'cac';\nimport { cpus } from 'node:os';\nimport { loadConfig } from './config/loader.js';\nimport { generateJobs } from './runner/generator.js';\nimport { Runner } from './runner/runner.js';\nimport { renderTable, renderMinimal, renderJson } from './render/table.js';\nimport { WatchRenderer } from './render/watch.js';\nimport { aggregateErrors, formatErrors } from './report/aggregator.js';\nimport { setColorsEnabled, green, yellow, red, cyan, dim } from './utils/colors.js';\nimport type { RunOptions, ServiceDef } from './model/types.js';\n\nconst cli = cac('qa');\n\ncli.usage(`${green('qa')} ${cyan('<command>')} ${dim('[options]')}`);\n\ncli\n .command('run', 'Run tasks across services')\n .option('--tasks <tasks>', 'Comma-separated list of tasks or \"all\"', { default: 'all' })\n .option('--services <services>', 'Comma-separated list of services or \"all\"', { default: 'all' })\n .option('--concurrency <n>', 'Maximum parallel jobs', { default: cpus().length })\n .option('--parallel', 'Alias for concurrency')\n .option('--fail-fast', 'Stop on first error', { default: false })\n .option('--random', 'Randomize job execution order', { default: false })\n .option('--no-watch', 'Disable live updates')\n .option('--format <format>', 'Output format: table, minimal, json', { default: 'table' })\n .option('--output <path>', 'Save JSON output to file')\n .option('--verbose', 'Verbose output', { default: false })\n .option('--quiet', 'Minimal output', { default: false })\n .option('--timeout <ms>', 'Timeout per task in milliseconds')\n .option('--no-color', 'Disable colors')\n .option('--config <path>', 'Path to config file')\n .action(async (options) => {\n try {\n const isTTY = process.stdout.isTTY;\n const colorsEnabled = options.color !== false && isTTY;\n setColorsEnabled(colorsEnabled);\n\n let config;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n console.error(red('✗ Config Error:'), error instanceof Error ? error.message : String(error));\n console.log(dim('\\nMake sure you have a qa.config.js file in your project root.'));\n console.log(dim('Example: https://github.com/your-repo/verify-grid#configuration'));\n process.exit(2);\n }\n\n const tasks = options.tasks === 'all' \n ? ['all'] \n : options.tasks.split(',').map((t: string) => t.trim());\n \n const services = options.services === 'all'\n ? ['all']\n : options.services.split(',').map((s: string) => s.trim());\n\n const runOptions: RunOptions = {\n tasks,\n services,\n concurrency: Number(options.concurrency),\n failFast: options.failFast,\n watch: (options.watch !== false && isTTY && options.format === 'table'),\n format: options.format,\n output: options.output,\n verbose: options.verbose,\n quiet: options.quiet,\n timeout: options.timeout ? Number(options.timeout) : undefined,\n noColor: !colorsEnabled,\n random: options.random,\n };\n\n let jobs;\n try {\n jobs = generateJobs(config, tasks, services);\n } catch (error) {\n console.error(red('✗ Job Generation Error:'), error instanceof Error ? error.message : String(error));\n process.exit(2);\n }\n\n if (jobs.length === 0) {\n console.error(yellow('⚠ No jobs to run'));\n console.log(dim('\\nCheck your --tasks and --services filters.'));\n console.log(dim(`Available tasks: ${Object.keys(config.tasks).join(', ')}`));\n console.log(dim(`Available services: ${config.services.map((s: ServiceDef) => s.name).join(', ')}`));\n process.exit(2);\n }\n\n const runner = new Runner(runOptions);\n let watchRenderer: WatchRenderer | null = null;\n\n if (runOptions.watch) {\n watchRenderer = new WatchRenderer();\n watchRenderer.start(runner.getState());\n\n runner.on('jobStart', () => {\n watchRenderer?.update(runner.getState());\n });\n\n runner.on('jobComplete', () => {\n watchRenderer?.update(runner.getState());\n });\n\n runner.on('jobError', () => {\n watchRenderer?.update(runner.getState());\n });\n }\n\n const state = await runner.run(jobs);\n\n if (watchRenderer) {\n watchRenderer.stop();\n }\n\n if (!runOptions.quiet && !runOptions.watch) {\n let output: string;\n \n if (runOptions.format === 'json') {\n output = renderJson(state);\n } else if (runOptions.format === 'minimal') {\n output = renderMinimal(state);\n } else {\n output = renderTable(state);\n }\n\n console.log(output);\n\n const errors = aggregateErrors(state);\n if (errors.length > 0 && runOptions.format !== 'json') {\n console.log(formatErrors(errors));\n }\n }\n\n if (runOptions.watch && !runOptions.quiet) {\n const errors = aggregateErrors(state);\n if (errors.length > 0) {\n console.log('\\n' + formatErrors(errors));\n }\n }\n\n if (options.output) {\n const { writeFileSync } = await import('node:fs');\n writeFileSync(options.output, renderJson(state), 'utf-8');\n }\n\n const hasErrors = Array.from(state.jobs.values()).some(j => j.status === 'error');\n process.exit(hasErrors ? 1 : 0);\n\n } catch (error) {\n console.error(red('✗ Unexpected Error:'), error instanceof Error ? error.message : String(error));\n if (error instanceof Error && error.stack) {\n console.error(dim(error.stack));\n }\n process.exit(2);\n }\n });\n\ncli.help();\ncli.version('0.1.0');\n\nif (process.argv.length === 2) {\n console.log(green('🚀 QA Task Runner\\n'));\n console.log('Quick start:');\n console.log(` ${cyan('qa run')} ${dim('# Run all tasks on all services')}`);\n console.log(` ${cyan('qa run --tasks lint')} ${dim('# Run specific task')}`);\n console.log(` ${cyan('qa run --services api,web')} ${dim('# Run on specific services')}`);\n console.log(` ${cyan('qa run --fail-fast')} ${dim('# Stop on first error')}`);\n console.log(` ${cyan('qa run --concurrency 4')} ${dim('# Limit parallel jobs')}`);\n console.log('\\nOptions:');\n console.log(` ${cyan('--help')} ${dim('# Show all available options')}`);\n console.log(` ${cyan('--version')} ${dim('# Show version')}`);\n console.log('\\nConfiguration:');\n console.log(` Create ${yellow('qa.config.js')} in your project root.`);\n console.log(` See: ${dim('https://github.com/your-repo/verify-grid#configuration')}`);\n process.exit(0);\n}\n\ncli.parse();\n","import { pathToFileURL } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { ConfigSchema } from './schema.js';\nimport type { Config } from '../model/types.js';\n\nconst CONFIG_FILES = ['qa.config.ts', 'qa.config.js', 'qa.config.mjs'];\n\nexport async function findConfig(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = resolve(startDir);\n const root = resolve('/');\n\n while (currentDir !== root) {\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(currentDir, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n currentDir = dirname(currentDir);\n }\n\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(root, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n\n return null;\n}\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const path = configPath || await findConfig();\n \n if (!path) {\n throw new Error(\n 'No config file found. Create qa.config.ts or qa.config.js in your project root.'\n );\n }\n\n if (!existsSync(path)) {\n throw new Error(`Config file not found: ${path}`);\n }\n\n try {\n const fileUrl = pathToFileURL(path).href;\n const module = await import(fileUrl);\n const configData = module.default || module;\n\n const result = ConfigSchema.safeParse(configData);\n \n if (!result.success) {\n const errors = result.error.errors.map(err => \n ` - ${err.path.join('.')}: ${err.message}`\n ).join('\\n');\n throw new Error(`Invalid config:\\n${errors}`);\n }\n\n const serviceNames = new Set<string>();\n for (const service of result.data.services) {\n if (serviceNames.has(service.name)) {\n throw new Error(`Duplicate service name: ${service.name}`);\n }\n serviceNames.add(service.name);\n }\n\n const config: Config = {\n services: result.data.services,\n tasks: Object.entries(result.data.tasks).reduce((acc, [name, def]) => {\n acc[name] = { name, ...def };\n return acc;\n }, {} as Record<string, any>),\n };\n\n return config;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to load config: ${String(error)}`);\n }\n}\n","import { z } from 'zod';\n\nexport const TaskDefSchema = z.object({\n name: z.string(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n optional: z.boolean().optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ServiceDefSchema = z.object({\n name: z.string(),\n cwd: z.string(),\n tasks: z.record(z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n optional: z.boolean().optional(),\n env: z.record(z.string()).optional(),\n })).optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ConfigSchema = z.object({\n services: z.array(ServiceDefSchema),\n tasks: z.record(TaskDefSchema.omit({ name: true })),\n});\n\nexport type ConfigInput = z.input<typeof ConfigSchema>;\nexport type ConfigOutput = z.output<typeof ConfigSchema>;\n","import { existsSync } from 'node:fs';\nimport type { Config, Job, TaskDef } from '../model/types.js';\n\nexport function generateJobs(\n config: Config,\n taskNames: string[],\n serviceNames: string[]\n): Job[] {\n const jobs: Job[] = [];\n const resolvedServiceNames = serviceNames.includes('all') \n ? config.services.map(s => s.name)\n : serviceNames;\n\n const resolvedTaskNames = taskNames.includes('all')\n ? Object.keys(config.tasks)\n : taskNames;\n\n for (const taskName of resolvedTaskNames) {\n const globalTask = config.tasks[taskName];\n \n if (!globalTask) {\n throw new Error(`Unknown task: ${taskName}`);\n }\n\n for (const serviceName of resolvedServiceNames) {\n const service = config.services.find(s => s.name === serviceName);\n \n if (!service) {\n throw new Error(`Unknown service: ${serviceName}`);\n }\n\n if (!existsSync(service.cwd)) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const taskOverride = service.tasks?.[taskName];\n const isOptional = taskOverride?.optional ?? globalTask.optional ?? false;\n \n const finalCommand = taskOverride?.command ?? globalTask.command;\n \n if (!finalCommand) {\n if (isOptional) {\n continue;\n }\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const mergedTask: TaskDef = {\n name: taskName,\n command: finalCommand,\n args: taskOverride?.args ?? globalTask.args,\n shell: taskOverride?.shell ?? globalTask.shell ?? true,\n timeout: taskOverride?.timeout ?? globalTask.timeout,\n priority: taskOverride?.priority ?? globalTask.priority,\n optional: isOptional,\n env: {\n ...globalTask.env,\n ...service.env,\n ...taskOverride?.env,\n },\n };\n\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: mergedTask.command,\n args: mergedTask.args,\n shell: mergedTask.shell,\n timeout: mergedTask.timeout,\n priority: mergedTask.priority,\n optional: mergedTask.optional,\n env: mergedTask.env,\n status: 'queued',\n });\n }\n }\n\n return jobs;\n}\n","import PQueue from 'p-queue';\nimport { EventEmitter } from 'node:events';\nimport type { Job, JobResult, MatrixState, RunOptions } from '../model/types.js';\nimport { executeJob } from './executor.js';\n\nexport interface RunnerEvents {\n jobStart: (job: Job) => void;\n jobComplete: (result: JobResult) => void;\n jobError: (result: JobResult) => void;\n runComplete: (state: MatrixState) => void;\n runCanceled: () => void;\n}\n\nexport class Runner extends EventEmitter {\n private queue: PQueue;\n private state: MatrixState;\n private shouldStop = false;\n private options: RunOptions;\n\n constructor(options: RunOptions) {\n super();\n this.options = options;\n this.queue = new PQueue({ concurrency: options.concurrency });\n this.state = {\n jobs: new Map(),\n results: new Map(),\n errors: [],\n startedAt: Date.now(),\n };\n\n process.on('SIGINT', () => this.handleInterrupt());\n process.on('SIGTERM', () => this.handleInterrupt());\n }\n\n private handleInterrupt(): void {\n if (this.shouldStop) {\n process.exit(130);\n }\n \n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, job] of this.state.jobs) {\n if (job.status === 'queued') {\n job.status = 'canceled';\n this.state.jobs.set(id, job);\n }\n }\n \n this.emit('runCanceled');\n }\n\n async run(jobs: Job[]): Promise<MatrixState> {\n for (const job of jobs) {\n this.state.jobs.set(job.id, job);\n }\n\n let executableJobs = jobs.filter(j => j.status === 'queued');\n \n if (this.options.random) {\n executableJobs = this.shuffleJobs(executableJobs);\n } else {\n executableJobs = this.sortJobsByPriority(executableJobs);\n }\n \n for (const job of executableJobs) {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n continue;\n }\n\n this.queue.add(async () => {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n return;\n }\n\n job.status = 'running';\n job.startedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.emit('jobStart', job);\n\n const result = await executeJob(job);\n \n job.status = result.exitCode === 0 ? 'done' : 'error';\n job.endedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.state.results.set(job.id, result);\n\n if (result.exitCode !== 0) {\n this.emit('jobError', result);\n \n if (this.options.failFast) {\n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, j] of this.state.jobs) {\n if (j.status === 'queued') {\n j.status = 'canceled';\n this.state.jobs.set(id, j);\n }\n }\n }\n } else {\n this.emit('jobComplete', result);\n }\n });\n }\n\n await this.queue.onIdle();\n \n this.state.endedAt = Date.now();\n this.emit('runComplete', this.state);\n \n return this.state;\n }\n\n getState(): MatrixState {\n return this.state;\n }\n\n private sortJobsByPriority(jobs: Job[]): Job[] {\n return jobs.slice().sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA;\n });\n }\n\n private shuffleJobs(jobs: Job[]): Job[] {\n const shuffled = jobs.slice();\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = shuffled[i];\n shuffled[i] = shuffled[j]!;\n shuffled[j] = temp!;\n }\n return shuffled;\n }\n}\n","import { execa, type ExecaError } from 'execa';\nimport type { Job, JobResult } from '../model/types.js';\nimport { LogBuffer } from '../utils/log.js';\n\nexport async function executeJob(job: Job): Promise<JobResult> {\n const startTime = Date.now();\n const stdoutBuffer = new LogBuffer();\n const stderrBuffer = new LogBuffer();\n\n try {\n const args = job.args || [];\n const options: any = {\n cwd: job.cwd,\n env: { ...process.env, ...job.env },\n shell: job.shell,\n timeout: job.timeout,\n reject: false,\n all: true,\n };\n\n const result = await execa(job.command, args, options);\n\n stdoutBuffer.append(result.stdout || '');\n stderrBuffer.append(result.stderr || '');\n\n const durationMs = Date.now() - startTime;\n\n if (result.exitCode === 0) {\n return {\n job,\n exitCode: 0,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n };\n }\n\n return {\n job,\n exitCode: result.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType: result.signal ? 'signal' : 'exit',\n signal: result.signal || undefined,\n };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const execaError = error as ExecaError;\n\n if (execaError.stdout) stdoutBuffer.append(execaError.stdout);\n if (execaError.stderr) stderrBuffer.append(execaError.stderr);\n\n let errorType: 'exit' | 'timeout' | 'spawn' | 'signal' = 'exit';\n \n if (execaError.timedOut) {\n errorType = 'timeout';\n } else if (execaError.signal) {\n errorType = 'signal';\n } else if (execaError.exitCode === 127 || (error as any).code === 'ENOENT') {\n errorType = 'spawn';\n }\n\n return {\n job,\n exitCode: execaError.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType,\n signal: execaError.signal || undefined,\n };\n }\n}\n","const MAX_LOG_LINES = 200;\n\nexport function truncateLog(log: string, maxLines: number = MAX_LOG_LINES): string {\n const lines = log.split('\\n');\n if (lines.length <= maxLines) {\n return log;\n }\n return lines.slice(-maxLines).join('\\n');\n}\n\nexport function extractErrorSnippet(stderr: string, stdout: string, maxLines: number = 10): string {\n const errorLog = stderr || stdout;\n const lines = errorLog.split('\\n').filter(line => line.trim());\n \n if (lines.length === 0) {\n return 'No error output';\n }\n \n const relevantLines = lines.slice(-maxLines);\n return relevantLines.join('\\n');\n}\n\nexport class LogBuffer {\n private lines: string[] = [];\n private maxLines: number;\n\n constructor(maxLines: number = MAX_LOG_LINES) {\n this.maxLines = maxLines;\n }\n\n append(data: string): void {\n const newLines = data.split('\\n');\n this.lines.push(...newLines);\n \n if (this.lines.length > this.maxLines) {\n this.lines = this.lines.slice(-this.maxLines);\n }\n }\n\n getContent(): string {\n return this.lines.join('\\n');\n }\n\n clear(): void {\n this.lines = [];\n }\n}\n","import Table from 'cli-table3';\nimport type { MatrixState, Job } from '../model/types.js';\nimport { getStatusColor } from '../utils/colors.js';\nimport { Spinner } from '../utils/spinner.js';\n\nconst spinner = new Spinner();\n\nexport function renderTable(state: MatrixState): string {\n const taskNames = getUniqueTaskNames(state);\n const serviceGroups = groupJobsByService(state);\n\n const table = new Table({\n head: ['Service', ...taskNames],\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const [serviceName, jobs] of serviceGroups) {\n const row: string[] = [serviceName];\n \n for (const taskName of taskNames) {\n const job = jobs.find(j => j.taskName === taskName);\n \n if (job) {\n const statusColor = getStatusColor(job.status);\n let statusText: string = job.status;\n \n if (job.status === 'running') {\n statusText = `${spinner.next()} ${job.status}`;\n } else if (job.status === 'queued') {\n statusText = `⏳ ${job.status}`;\n }\n \n row.push(statusColor(statusText));\n } else {\n row.push('-');\n }\n }\n \n table.push(row);\n }\n\n return table.toString();\n}\n\nexport function renderMinimal(state: MatrixState): string {\n const lines: string[] = [];\n \n for (const [, job] of state.jobs) {\n const statusColor = getStatusColor(job.status);\n const statusText = statusColor(job.status);\n lines.push(`${job.taskName} ${job.serviceName} ${statusText}`);\n }\n \n return lines.join('\\n');\n}\n\nexport function renderJson(state: MatrixState): string {\n const matrix: Record<string, Record<string, any>> = {};\n \n for (const [, job] of state.jobs) {\n if (!matrix[job.taskName]) {\n matrix[job.taskName] = {};\n }\n \n const result = state.results.get(job.id);\n const taskMatrix = matrix[job.taskName];\n \n if (taskMatrix) {\n taskMatrix[job.serviceName] = {\n status: job.status,\n exitCode: result?.exitCode,\n durationMs: result?.durationMs,\n };\n }\n }\n\n const errors = Array.from(state.results.values())\n .filter(r => r.job.status === 'error')\n .map(r => ({\n taskName: r.job.taskName,\n serviceName: r.job.serviceName,\n exitCode: r.exitCode,\n durationMs: r.durationMs,\n errorType: r.errorType,\n }));\n\n const output = {\n startedAt: state.startedAt,\n endedAt: state.endedAt,\n durationMs: state.endedAt ? state.endedAt - state.startedAt : undefined,\n matrix,\n errors,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nfunction groupJobsByService(state: MatrixState): Map<string, Job[]> {\n const groups = new Map<string, Job[]>();\n \n for (const [, job] of state.jobs) {\n if (!groups.has(job.serviceName)) {\n groups.set(job.serviceName, []);\n }\n groups.get(job.serviceName)!.push(job);\n }\n \n return groups;\n}\n\nfunction getUniqueTaskNames(state: MatrixState): string[] {\n const taskNames = new Set<string>();\n \n for (const [, job] of state.jobs) {\n taskNames.add(job.taskName);\n }\n \n return Array.from(taskNames).sort();\n}\n","import kleur from 'kleur';\nimport type { JobStatus } from '../model/types.js';\n\nlet colorsEnabled = true;\n\nexport function setColorsEnabled(enabled: boolean): void {\n colorsEnabled = enabled;\n kleur.enabled = enabled;\n}\n\nexport function getStatusColor(status: JobStatus): (text: string) => string {\n if (!colorsEnabled) {\n return (text: string) => text;\n }\n\n switch (status) {\n case 'done':\n return kleur.green;\n case 'error':\n return kleur.red;\n case 'running':\n return kleur.blue;\n case 'queued':\n return kleur.yellow;\n case 'skipped':\n case 'canceled':\n return kleur.gray;\n default:\n return (text: string) => text;\n }\n}\n\nexport const colors = {\n error: kleur.red,\n success: kleur.green,\n warning: kleur.yellow,\n info: kleur.blue,\n dim: kleur.gray,\n bold: kleur.bold,\n};\n\nexport const green = kleur.green;\nexport const yellow = kleur.yellow;\nexport const red = kleur.red;\nexport const cyan = kleur.cyan;\nexport const dim = kleur.gray;\n","const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nexport class Spinner {\n private frame = 0;\n\n next(): string {\n const char = SPINNER_FRAMES[this.frame];\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n return char || '⠋';\n }\n\n reset(): void {\n this.frame = 0;\n }\n}\n","import logUpdate from 'log-update';\nimport type { MatrixState } from '../model/types.js';\nimport { renderTable } from './table.js';\n\nexport class WatchRenderer {\n private updateInterval: NodeJS.Timeout | null = null;\n private state: MatrixState | null = null;\n\n start(state: MatrixState): void {\n this.state = state;\n this.render();\n \n this.updateInterval = setInterval(() => {\n this.render();\n }, 80);\n }\n\n update(state: MatrixState): void {\n this.state = state;\n }\n\n stop(): void {\n if (this.updateInterval) {\n clearInterval(this.updateInterval);\n this.updateInterval = null;\n }\n \n if (this.state) {\n const output = renderTable(this.state);\n logUpdate(output);\n }\n \n logUpdate.done();\n }\n\n private render(): void {\n if (!this.state) return;\n \n const output = renderTable(this.state);\n logUpdate(output);\n }\n}\n","import type { MatrixState, ErrorEntry, JobResult } from '../model/types.js';\nimport { extractErrorSnippet } from '../utils/log.js';\n\nexport function aggregateErrors(state: MatrixState): ErrorEntry[] {\n const errors: ErrorEntry[] = [];\n\n for (const [, result] of state.results) {\n if (result.job.status === 'error') {\n const errorMessage = getErrorMessage(result);\n const logSnippet = extractErrorSnippet(result.stderr, result.stdout);\n\n errors.push({\n taskName: result.job.taskName,\n serviceName: result.job.serviceName,\n exitCode: result.exitCode,\n durationMs: result.durationMs,\n message: errorMessage,\n logSnippet,\n errorType: result.errorType,\n });\n }\n }\n\n errors.sort((a, b) => {\n if (a.taskName !== b.taskName) {\n return a.taskName.localeCompare(b.taskName);\n }\n return a.serviceName.localeCompare(b.serviceName);\n });\n\n return errors;\n}\n\nfunction getErrorMessage(result: JobResult): string {\n if (result.errorType === 'timeout') {\n return `Timeout after ${result.durationMs}ms`;\n }\n \n if (result.errorType === 'spawn') {\n return `Command not found: ${result.job.command}`;\n }\n \n if (result.errorType === 'signal') {\n return `Killed by signal: ${result.signal}`;\n }\n \n return `Exit code ${result.exitCode}`;\n}\n\nexport function formatErrors(errors: ErrorEntry[]): string {\n if (errors.length === 0) {\n return '';\n }\n\n const lines: string[] = ['', 'Errors:'];\n\n for (const error of errors) {\n lines.push(`- ${error.serviceName} has error in ${error.taskName} test:`);\n \n const snippet = error.logSnippet.trim();\n if (snippet) {\n const snippetLines = snippet.split('\\n').slice(0, 10);\n for (const line of snippetLines) {\n lines.push(` ${line}`);\n }\n } else {\n lines.push(` ${error.message}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["existsSync","colorsEnabled"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ interface TaskDef {
|
|
|
9
9
|
timeout?: number;
|
|
10
10
|
env?: Record<string, string>;
|
|
11
11
|
priority?: number;
|
|
12
|
+
optional?: boolean;
|
|
12
13
|
}
|
|
13
14
|
interface ServiceDef {
|
|
14
15
|
name: string;
|
|
@@ -27,6 +28,7 @@ interface Job {
|
|
|
27
28
|
timeout?: number;
|
|
28
29
|
env?: Record<string, string>;
|
|
29
30
|
priority?: number;
|
|
31
|
+
optional?: boolean;
|
|
30
32
|
status: JobStatus;
|
|
31
33
|
startedAt?: number;
|
|
32
34
|
endedAt?: number;
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ var TaskDefSchema = z.object({
|
|
|
12
12
|
shell: z.boolean().optional(),
|
|
13
13
|
timeout: z.number().positive().optional(),
|
|
14
14
|
priority: z.number().optional(),
|
|
15
|
+
optional: z.boolean().optional(),
|
|
15
16
|
env: z.record(z.string()).optional()
|
|
16
17
|
});
|
|
17
18
|
var ServiceDefSchema = z.object({
|
|
@@ -23,6 +24,7 @@ var ServiceDefSchema = z.object({
|
|
|
23
24
|
shell: z.boolean().optional(),
|
|
24
25
|
timeout: z.number().positive().optional(),
|
|
25
26
|
priority: z.number().optional(),
|
|
27
|
+
optional: z.boolean().optional(),
|
|
26
28
|
env: z.record(z.string()).optional()
|
|
27
29
|
})).optional(),
|
|
28
30
|
env: z.record(z.string()).optional()
|
|
@@ -127,7 +129,12 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
127
129
|
continue;
|
|
128
130
|
}
|
|
129
131
|
const taskOverride = service.tasks?.[taskName];
|
|
130
|
-
|
|
132
|
+
const isOptional = taskOverride?.optional ?? globalTask.optional ?? false;
|
|
133
|
+
const finalCommand = taskOverride?.command ?? globalTask.command;
|
|
134
|
+
if (!finalCommand) {
|
|
135
|
+
if (isOptional) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
131
138
|
jobs.push({
|
|
132
139
|
id: `${taskName}:${serviceName}`,
|
|
133
140
|
taskName,
|
|
@@ -140,11 +147,12 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
140
147
|
}
|
|
141
148
|
const mergedTask = {
|
|
142
149
|
name: taskName,
|
|
143
|
-
command:
|
|
150
|
+
command: finalCommand,
|
|
144
151
|
args: taskOverride?.args ?? globalTask.args,
|
|
145
152
|
shell: taskOverride?.shell ?? globalTask.shell ?? true,
|
|
146
153
|
timeout: taskOverride?.timeout ?? globalTask.timeout,
|
|
147
154
|
priority: taskOverride?.priority ?? globalTask.priority,
|
|
155
|
+
optional: isOptional,
|
|
148
156
|
env: {
|
|
149
157
|
...globalTask.env,
|
|
150
158
|
...service.env,
|
|
@@ -161,6 +169,7 @@ function generateJobs(config, taskNames, serviceNames) {
|
|
|
161
169
|
shell: mergedTask.shell,
|
|
162
170
|
timeout: mergedTask.timeout,
|
|
163
171
|
priority: mergedTask.priority,
|
|
172
|
+
optional: mergedTask.optional,
|
|
164
173
|
env: mergedTask.env,
|
|
165
174
|
status: "queued"
|
|
166
175
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/loader.ts","../src/config/schema.ts","../src/runner/generator.ts","../src/runner/runner.ts","../src/runner/executor.ts","../src/utils/log.ts","../src/render/table.ts","../src/utils/colors.ts","../src/utils/spinner.ts","../src/report/aggregator.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { ConfigSchema } from './schema.js';\nimport type { Config } from '../model/types.js';\n\nconst CONFIG_FILES = ['qa.config.ts', 'qa.config.js', 'qa.config.mjs'];\n\nexport async function findConfig(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = resolve(startDir);\n const root = resolve('/');\n\n while (currentDir !== root) {\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(currentDir, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n currentDir = dirname(currentDir);\n }\n\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(root, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n\n return null;\n}\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const path = configPath || await findConfig();\n \n if (!path) {\n throw new Error(\n 'No config file found. Create qa.config.ts or qa.config.js in your project root.'\n );\n }\n\n if (!existsSync(path)) {\n throw new Error(`Config file not found: ${path}`);\n }\n\n try {\n const fileUrl = pathToFileURL(path).href;\n const module = await import(fileUrl);\n const configData = module.default || module;\n\n const result = ConfigSchema.safeParse(configData);\n \n if (!result.success) {\n const errors = result.error.errors.map(err => \n ` - ${err.path.join('.')}: ${err.message}`\n ).join('\\n');\n throw new Error(`Invalid config:\\n${errors}`);\n }\n\n const serviceNames = new Set<string>();\n for (const service of result.data.services) {\n if (serviceNames.has(service.name)) {\n throw new Error(`Duplicate service name: ${service.name}`);\n }\n serviceNames.add(service.name);\n }\n\n const config: Config = {\n services: result.data.services,\n tasks: Object.entries(result.data.tasks).reduce((acc, [name, def]) => {\n acc[name] = { name, ...def };\n return acc;\n }, {} as Record<string, any>),\n };\n\n return config;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to load config: ${String(error)}`);\n }\n}\n","import { z } from 'zod';\n\nexport const TaskDefSchema = z.object({\n name: z.string(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ServiceDefSchema = z.object({\n name: z.string(),\n cwd: z.string(),\n tasks: z.record(z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n env: z.record(z.string()).optional(),\n })).optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ConfigSchema = z.object({\n services: z.array(ServiceDefSchema),\n tasks: z.record(TaskDefSchema.omit({ name: true })),\n});\n\nexport type ConfigInput = z.input<typeof ConfigSchema>;\nexport type ConfigOutput = z.output<typeof ConfigSchema>;\n","import { existsSync } from 'node:fs';\nimport type { Config, Job, TaskDef } from '../model/types.js';\n\nexport function generateJobs(\n config: Config,\n taskNames: string[],\n serviceNames: string[]\n): Job[] {\n const jobs: Job[] = [];\n const resolvedServiceNames = serviceNames.includes('all') \n ? config.services.map(s => s.name)\n : serviceNames;\n\n const resolvedTaskNames = taskNames.includes('all')\n ? Object.keys(config.tasks)\n : taskNames;\n\n for (const taskName of resolvedTaskNames) {\n const globalTask = config.tasks[taskName];\n \n if (!globalTask) {\n throw new Error(`Unknown task: ${taskName}`);\n }\n\n for (const serviceName of resolvedServiceNames) {\n const service = config.services.find(s => s.name === serviceName);\n \n if (!service) {\n throw new Error(`Unknown service: ${serviceName}`);\n }\n\n if (!existsSync(service.cwd)) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const taskOverride = service.tasks?.[taskName];\n \n if (taskOverride && taskOverride.command === undefined && !globalTask.command) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const mergedTask: TaskDef = {\n name: taskName,\n command: taskOverride?.command ?? globalTask.command,\n args: taskOverride?.args ?? globalTask.args,\n shell: taskOverride?.shell ?? globalTask.shell ?? true,\n timeout: taskOverride?.timeout ?? globalTask.timeout,\n priority: taskOverride?.priority ?? globalTask.priority,\n env: {\n ...globalTask.env,\n ...service.env,\n ...taskOverride?.env,\n },\n };\n\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: mergedTask.command,\n args: mergedTask.args,\n shell: mergedTask.shell,\n timeout: mergedTask.timeout,\n priority: mergedTask.priority,\n env: mergedTask.env,\n status: 'queued',\n });\n }\n }\n\n return jobs;\n}\n","import PQueue from 'p-queue';\nimport { EventEmitter } from 'node:events';\nimport type { Job, JobResult, MatrixState, RunOptions } from '../model/types.js';\nimport { executeJob } from './executor.js';\n\nexport interface RunnerEvents {\n jobStart: (job: Job) => void;\n jobComplete: (result: JobResult) => void;\n jobError: (result: JobResult) => void;\n runComplete: (state: MatrixState) => void;\n runCanceled: () => void;\n}\n\nexport class Runner extends EventEmitter {\n private queue: PQueue;\n private state: MatrixState;\n private shouldStop = false;\n private options: RunOptions;\n\n constructor(options: RunOptions) {\n super();\n this.options = options;\n this.queue = new PQueue({ concurrency: options.concurrency });\n this.state = {\n jobs: new Map(),\n results: new Map(),\n errors: [],\n startedAt: Date.now(),\n };\n\n process.on('SIGINT', () => this.handleInterrupt());\n process.on('SIGTERM', () => this.handleInterrupt());\n }\n\n private handleInterrupt(): void {\n if (this.shouldStop) {\n process.exit(130);\n }\n \n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, job] of this.state.jobs) {\n if (job.status === 'queued') {\n job.status = 'canceled';\n this.state.jobs.set(id, job);\n }\n }\n \n this.emit('runCanceled');\n }\n\n async run(jobs: Job[]): Promise<MatrixState> {\n for (const job of jobs) {\n this.state.jobs.set(job.id, job);\n }\n\n let executableJobs = jobs.filter(j => j.status === 'queued');\n \n if (this.options.random) {\n executableJobs = this.shuffleJobs(executableJobs);\n } else {\n executableJobs = this.sortJobsByPriority(executableJobs);\n }\n \n for (const job of executableJobs) {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n continue;\n }\n\n this.queue.add(async () => {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n return;\n }\n\n job.status = 'running';\n job.startedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.emit('jobStart', job);\n\n const result = await executeJob(job);\n \n job.status = result.exitCode === 0 ? 'done' : 'error';\n job.endedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.state.results.set(job.id, result);\n\n if (result.exitCode !== 0) {\n this.emit('jobError', result);\n \n if (this.options.failFast) {\n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, j] of this.state.jobs) {\n if (j.status === 'queued') {\n j.status = 'canceled';\n this.state.jobs.set(id, j);\n }\n }\n }\n } else {\n this.emit('jobComplete', result);\n }\n });\n }\n\n await this.queue.onIdle();\n \n this.state.endedAt = Date.now();\n this.emit('runComplete', this.state);\n \n return this.state;\n }\n\n getState(): MatrixState {\n return this.state;\n }\n\n private sortJobsByPriority(jobs: Job[]): Job[] {\n return jobs.slice().sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA;\n });\n }\n\n private shuffleJobs(jobs: Job[]): Job[] {\n const shuffled = jobs.slice();\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = shuffled[i];\n shuffled[i] = shuffled[j]!;\n shuffled[j] = temp!;\n }\n return shuffled;\n }\n}\n","import { execa, type ExecaError } from 'execa';\nimport type { Job, JobResult } from '../model/types.js';\nimport { LogBuffer } from '../utils/log.js';\n\nexport async function executeJob(job: Job): Promise<JobResult> {\n const startTime = Date.now();\n const stdoutBuffer = new LogBuffer();\n const stderrBuffer = new LogBuffer();\n\n try {\n const args = job.args || [];\n const options: any = {\n cwd: job.cwd,\n env: { ...process.env, ...job.env },\n shell: job.shell,\n timeout: job.timeout,\n reject: false,\n all: true,\n };\n\n const result = await execa(job.command, args, options);\n\n stdoutBuffer.append(result.stdout || '');\n stderrBuffer.append(result.stderr || '');\n\n const durationMs = Date.now() - startTime;\n\n if (result.exitCode === 0) {\n return {\n job,\n exitCode: 0,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n };\n }\n\n return {\n job,\n exitCode: result.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType: result.signal ? 'signal' : 'exit',\n signal: result.signal || undefined,\n };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const execaError = error as ExecaError;\n\n if (execaError.stdout) stdoutBuffer.append(execaError.stdout);\n if (execaError.stderr) stderrBuffer.append(execaError.stderr);\n\n let errorType: 'exit' | 'timeout' | 'spawn' | 'signal' = 'exit';\n \n if (execaError.timedOut) {\n errorType = 'timeout';\n } else if (execaError.signal) {\n errorType = 'signal';\n } else if (execaError.exitCode === 127 || (error as any).code === 'ENOENT') {\n errorType = 'spawn';\n }\n\n return {\n job,\n exitCode: execaError.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType,\n signal: execaError.signal || undefined,\n };\n }\n}\n","const MAX_LOG_LINES = 200;\n\nexport function truncateLog(log: string, maxLines: number = MAX_LOG_LINES): string {\n const lines = log.split('\\n');\n if (lines.length <= maxLines) {\n return log;\n }\n return lines.slice(-maxLines).join('\\n');\n}\n\nexport function extractErrorSnippet(stderr: string, stdout: string, maxLines: number = 10): string {\n const errorLog = stderr || stdout;\n const lines = errorLog.split('\\n').filter(line => line.trim());\n \n if (lines.length === 0) {\n return 'No error output';\n }\n \n const relevantLines = lines.slice(-maxLines);\n return relevantLines.join('\\n');\n}\n\nexport class LogBuffer {\n private lines: string[] = [];\n private maxLines: number;\n\n constructor(maxLines: number = MAX_LOG_LINES) {\n this.maxLines = maxLines;\n }\n\n append(data: string): void {\n const newLines = data.split('\\n');\n this.lines.push(...newLines);\n \n if (this.lines.length > this.maxLines) {\n this.lines = this.lines.slice(-this.maxLines);\n }\n }\n\n getContent(): string {\n return this.lines.join('\\n');\n }\n\n clear(): void {\n this.lines = [];\n }\n}\n","import Table from 'cli-table3';\nimport type { MatrixState, Job } from '../model/types.js';\nimport { getStatusColor } from '../utils/colors.js';\nimport { Spinner } from '../utils/spinner.js';\n\nconst spinner = new Spinner();\n\nexport function renderTable(state: MatrixState): string {\n const taskNames = getUniqueTaskNames(state);\n const serviceGroups = groupJobsByService(state);\n\n const table = new Table({\n head: ['Service', ...taskNames],\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const [serviceName, jobs] of serviceGroups) {\n const row: string[] = [serviceName];\n \n for (const taskName of taskNames) {\n const job = jobs.find(j => j.taskName === taskName);\n \n if (job) {\n const statusColor = getStatusColor(job.status);\n let statusText: string = job.status;\n \n if (job.status === 'running') {\n statusText = `${spinner.next()} ${job.status}`;\n } else if (job.status === 'queued') {\n statusText = `⏳ ${job.status}`;\n }\n \n row.push(statusColor(statusText));\n } else {\n row.push('-');\n }\n }\n \n table.push(row);\n }\n\n return table.toString();\n}\n\nexport function renderMinimal(state: MatrixState): string {\n const lines: string[] = [];\n \n for (const [, job] of state.jobs) {\n const statusColor = getStatusColor(job.status);\n const statusText = statusColor(job.status);\n lines.push(`${job.taskName} ${job.serviceName} ${statusText}`);\n }\n \n return lines.join('\\n');\n}\n\nexport function renderJson(state: MatrixState): string {\n const matrix: Record<string, Record<string, any>> = {};\n \n for (const [, job] of state.jobs) {\n if (!matrix[job.taskName]) {\n matrix[job.taskName] = {};\n }\n \n const result = state.results.get(job.id);\n const taskMatrix = matrix[job.taskName];\n \n if (taskMatrix) {\n taskMatrix[job.serviceName] = {\n status: job.status,\n exitCode: result?.exitCode,\n durationMs: result?.durationMs,\n };\n }\n }\n\n const errors = Array.from(state.results.values())\n .filter(r => r.job.status === 'error')\n .map(r => ({\n taskName: r.job.taskName,\n serviceName: r.job.serviceName,\n exitCode: r.exitCode,\n durationMs: r.durationMs,\n errorType: r.errorType,\n }));\n\n const output = {\n startedAt: state.startedAt,\n endedAt: state.endedAt,\n durationMs: state.endedAt ? state.endedAt - state.startedAt : undefined,\n matrix,\n errors,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nfunction groupJobsByService(state: MatrixState): Map<string, Job[]> {\n const groups = new Map<string, Job[]>();\n \n for (const [, job] of state.jobs) {\n if (!groups.has(job.serviceName)) {\n groups.set(job.serviceName, []);\n }\n groups.get(job.serviceName)!.push(job);\n }\n \n return groups;\n}\n\nfunction getUniqueTaskNames(state: MatrixState): string[] {\n const taskNames = new Set<string>();\n \n for (const [, job] of state.jobs) {\n taskNames.add(job.taskName);\n }\n \n return Array.from(taskNames).sort();\n}\n","import kleur from 'kleur';\nimport type { JobStatus } from '../model/types.js';\n\nlet colorsEnabled = true;\n\nexport function setColorsEnabled(enabled: boolean): void {\n colorsEnabled = enabled;\n kleur.enabled = enabled;\n}\n\nexport function getStatusColor(status: JobStatus): (text: string) => string {\n if (!colorsEnabled) {\n return (text: string) => text;\n }\n\n switch (status) {\n case 'done':\n return kleur.green;\n case 'error':\n return kleur.red;\n case 'running':\n return kleur.blue;\n case 'queued':\n return kleur.yellow;\n case 'skipped':\n case 'canceled':\n return kleur.gray;\n default:\n return (text: string) => text;\n }\n}\n\nexport const colors = {\n error: kleur.red,\n success: kleur.green,\n warning: kleur.yellow,\n info: kleur.blue,\n dim: kleur.gray,\n bold: kleur.bold,\n};\n\nexport const green = kleur.green;\nexport const yellow = kleur.yellow;\nexport const red = kleur.red;\nexport const cyan = kleur.cyan;\nexport const dim = kleur.gray;\n","const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nexport class Spinner {\n private frame = 0;\n\n next(): string {\n const char = SPINNER_FRAMES[this.frame];\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n return char || '⠋';\n }\n\n reset(): void {\n this.frame = 0;\n }\n}\n","import type { MatrixState, ErrorEntry, JobResult } from '../model/types.js';\nimport { extractErrorSnippet } from '../utils/log.js';\n\nexport function aggregateErrors(state: MatrixState): ErrorEntry[] {\n const errors: ErrorEntry[] = [];\n\n for (const [, result] of state.results) {\n if (result.job.status === 'error') {\n const errorMessage = getErrorMessage(result);\n const logSnippet = extractErrorSnippet(result.stderr, result.stdout);\n\n errors.push({\n taskName: result.job.taskName,\n serviceName: result.job.serviceName,\n exitCode: result.exitCode,\n durationMs: result.durationMs,\n message: errorMessage,\n logSnippet,\n errorType: result.errorType,\n });\n }\n }\n\n errors.sort((a, b) => {\n if (a.taskName !== b.taskName) {\n return a.taskName.localeCompare(b.taskName);\n }\n return a.serviceName.localeCompare(b.serviceName);\n });\n\n return errors;\n}\n\nfunction getErrorMessage(result: JobResult): string {\n if (result.errorType === 'timeout') {\n return `Timeout after ${result.durationMs}ms`;\n }\n \n if (result.errorType === 'spawn') {\n return `Command not found: ${result.job.command}`;\n }\n \n if (result.errorType === 'signal') {\n return `Killed by signal: ${result.signal}`;\n }\n \n return `Exit code ${result.exitCode}`;\n}\n\nexport function formatErrors(errors: ErrorEntry[]): string {\n if (errors.length === 0) {\n return '';\n }\n\n const lines: string[] = ['', 'Errors:'];\n\n for (const error of errors) {\n lines.push(`- ${error.serviceName} has error in ${error.taskName} test:`);\n \n const snippet = error.logSnippet.trim();\n if (snippet) {\n const snippetLines = snippet.split('\\n').slice(0, 10);\n for (const line of snippetLines) {\n lines.push(` ${line}`);\n }\n } else {\n lines.push(` ${error.message}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["existsSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/config/loader.ts","../src/config/schema.ts","../src/runner/generator.ts","../src/runner/runner.ts","../src/runner/executor.ts","../src/utils/log.ts","../src/render/table.ts","../src/utils/colors.ts","../src/utils/spinner.ts","../src/report/aggregator.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { ConfigSchema } from './schema.js';\nimport type { Config } from '../model/types.js';\n\nconst CONFIG_FILES = ['qa.config.ts', 'qa.config.js', 'qa.config.mjs'];\n\nexport async function findConfig(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = resolve(startDir);\n const root = resolve('/');\n\n while (currentDir !== root) {\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(currentDir, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n currentDir = dirname(currentDir);\n }\n\n for (const configFile of CONFIG_FILES) {\n const configPath = resolve(root, configFile);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n\n return null;\n}\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const path = configPath || await findConfig();\n \n if (!path) {\n throw new Error(\n 'No config file found. Create qa.config.ts or qa.config.js in your project root.'\n );\n }\n\n if (!existsSync(path)) {\n throw new Error(`Config file not found: ${path}`);\n }\n\n try {\n const fileUrl = pathToFileURL(path).href;\n const module = await import(fileUrl);\n const configData = module.default || module;\n\n const result = ConfigSchema.safeParse(configData);\n \n if (!result.success) {\n const errors = result.error.errors.map(err => \n ` - ${err.path.join('.')}: ${err.message}`\n ).join('\\n');\n throw new Error(`Invalid config:\\n${errors}`);\n }\n\n const serviceNames = new Set<string>();\n for (const service of result.data.services) {\n if (serviceNames.has(service.name)) {\n throw new Error(`Duplicate service name: ${service.name}`);\n }\n serviceNames.add(service.name);\n }\n\n const config: Config = {\n services: result.data.services,\n tasks: Object.entries(result.data.tasks).reduce((acc, [name, def]) => {\n acc[name] = { name, ...def };\n return acc;\n }, {} as Record<string, any>),\n };\n\n return config;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to load config: ${String(error)}`);\n }\n}\n","import { z } from 'zod';\n\nexport const TaskDefSchema = z.object({\n name: z.string(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n optional: z.boolean().optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ServiceDefSchema = z.object({\n name: z.string(),\n cwd: z.string(),\n tasks: z.record(z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n shell: z.boolean().optional(),\n timeout: z.number().positive().optional(),\n priority: z.number().optional(),\n optional: z.boolean().optional(),\n env: z.record(z.string()).optional(),\n })).optional(),\n env: z.record(z.string()).optional(),\n});\n\nexport const ConfigSchema = z.object({\n services: z.array(ServiceDefSchema),\n tasks: z.record(TaskDefSchema.omit({ name: true })),\n});\n\nexport type ConfigInput = z.input<typeof ConfigSchema>;\nexport type ConfigOutput = z.output<typeof ConfigSchema>;\n","import { existsSync } from 'node:fs';\nimport type { Config, Job, TaskDef } from '../model/types.js';\n\nexport function generateJobs(\n config: Config,\n taskNames: string[],\n serviceNames: string[]\n): Job[] {\n const jobs: Job[] = [];\n const resolvedServiceNames = serviceNames.includes('all') \n ? config.services.map(s => s.name)\n : serviceNames;\n\n const resolvedTaskNames = taskNames.includes('all')\n ? Object.keys(config.tasks)\n : taskNames;\n\n for (const taskName of resolvedTaskNames) {\n const globalTask = config.tasks[taskName];\n \n if (!globalTask) {\n throw new Error(`Unknown task: ${taskName}`);\n }\n\n for (const serviceName of resolvedServiceNames) {\n const service = config.services.find(s => s.name === serviceName);\n \n if (!service) {\n throw new Error(`Unknown service: ${serviceName}`);\n }\n\n if (!existsSync(service.cwd)) {\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const taskOverride = service.tasks?.[taskName];\n const isOptional = taskOverride?.optional ?? globalTask.optional ?? false;\n \n const finalCommand = taskOverride?.command ?? globalTask.command;\n \n if (!finalCommand) {\n if (isOptional) {\n continue;\n }\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: '',\n status: 'skipped',\n });\n continue;\n }\n\n const mergedTask: TaskDef = {\n name: taskName,\n command: finalCommand,\n args: taskOverride?.args ?? globalTask.args,\n shell: taskOverride?.shell ?? globalTask.shell ?? true,\n timeout: taskOverride?.timeout ?? globalTask.timeout,\n priority: taskOverride?.priority ?? globalTask.priority,\n optional: isOptional,\n env: {\n ...globalTask.env,\n ...service.env,\n ...taskOverride?.env,\n },\n };\n\n jobs.push({\n id: `${taskName}:${serviceName}`,\n taskName,\n serviceName,\n cwd: service.cwd,\n command: mergedTask.command,\n args: mergedTask.args,\n shell: mergedTask.shell,\n timeout: mergedTask.timeout,\n priority: mergedTask.priority,\n optional: mergedTask.optional,\n env: mergedTask.env,\n status: 'queued',\n });\n }\n }\n\n return jobs;\n}\n","import PQueue from 'p-queue';\nimport { EventEmitter } from 'node:events';\nimport type { Job, JobResult, MatrixState, RunOptions } from '../model/types.js';\nimport { executeJob } from './executor.js';\n\nexport interface RunnerEvents {\n jobStart: (job: Job) => void;\n jobComplete: (result: JobResult) => void;\n jobError: (result: JobResult) => void;\n runComplete: (state: MatrixState) => void;\n runCanceled: () => void;\n}\n\nexport class Runner extends EventEmitter {\n private queue: PQueue;\n private state: MatrixState;\n private shouldStop = false;\n private options: RunOptions;\n\n constructor(options: RunOptions) {\n super();\n this.options = options;\n this.queue = new PQueue({ concurrency: options.concurrency });\n this.state = {\n jobs: new Map(),\n results: new Map(),\n errors: [],\n startedAt: Date.now(),\n };\n\n process.on('SIGINT', () => this.handleInterrupt());\n process.on('SIGTERM', () => this.handleInterrupt());\n }\n\n private handleInterrupt(): void {\n if (this.shouldStop) {\n process.exit(130);\n }\n \n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, job] of this.state.jobs) {\n if (job.status === 'queued') {\n job.status = 'canceled';\n this.state.jobs.set(id, job);\n }\n }\n \n this.emit('runCanceled');\n }\n\n async run(jobs: Job[]): Promise<MatrixState> {\n for (const job of jobs) {\n this.state.jobs.set(job.id, job);\n }\n\n let executableJobs = jobs.filter(j => j.status === 'queued');\n \n if (this.options.random) {\n executableJobs = this.shuffleJobs(executableJobs);\n } else {\n executableJobs = this.sortJobsByPriority(executableJobs);\n }\n \n for (const job of executableJobs) {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n continue;\n }\n\n this.queue.add(async () => {\n if (this.shouldStop) {\n job.status = 'canceled';\n this.state.jobs.set(job.id, job);\n return;\n }\n\n job.status = 'running';\n job.startedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.emit('jobStart', job);\n\n const result = await executeJob(job);\n \n job.status = result.exitCode === 0 ? 'done' : 'error';\n job.endedAt = Date.now();\n this.state.jobs.set(job.id, job);\n this.state.results.set(job.id, result);\n\n if (result.exitCode !== 0) {\n this.emit('jobError', result);\n \n if (this.options.failFast) {\n this.shouldStop = true;\n this.queue.clear();\n \n for (const [id, j] of this.state.jobs) {\n if (j.status === 'queued') {\n j.status = 'canceled';\n this.state.jobs.set(id, j);\n }\n }\n }\n } else {\n this.emit('jobComplete', result);\n }\n });\n }\n\n await this.queue.onIdle();\n \n this.state.endedAt = Date.now();\n this.emit('runComplete', this.state);\n \n return this.state;\n }\n\n getState(): MatrixState {\n return this.state;\n }\n\n private sortJobsByPriority(jobs: Job[]): Job[] {\n return jobs.slice().sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA;\n });\n }\n\n private shuffleJobs(jobs: Job[]): Job[] {\n const shuffled = jobs.slice();\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = shuffled[i];\n shuffled[i] = shuffled[j]!;\n shuffled[j] = temp!;\n }\n return shuffled;\n }\n}\n","import { execa, type ExecaError } from 'execa';\nimport type { Job, JobResult } from '../model/types.js';\nimport { LogBuffer } from '../utils/log.js';\n\nexport async function executeJob(job: Job): Promise<JobResult> {\n const startTime = Date.now();\n const stdoutBuffer = new LogBuffer();\n const stderrBuffer = new LogBuffer();\n\n try {\n const args = job.args || [];\n const options: any = {\n cwd: job.cwd,\n env: { ...process.env, ...job.env },\n shell: job.shell,\n timeout: job.timeout,\n reject: false,\n all: true,\n };\n\n const result = await execa(job.command, args, options);\n\n stdoutBuffer.append(result.stdout || '');\n stderrBuffer.append(result.stderr || '');\n\n const durationMs = Date.now() - startTime;\n\n if (result.exitCode === 0) {\n return {\n job,\n exitCode: 0,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n };\n }\n\n return {\n job,\n exitCode: result.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType: result.signal ? 'signal' : 'exit',\n signal: result.signal || undefined,\n };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const execaError = error as ExecaError;\n\n if (execaError.stdout) stdoutBuffer.append(execaError.stdout);\n if (execaError.stderr) stderrBuffer.append(execaError.stderr);\n\n let errorType: 'exit' | 'timeout' | 'spawn' | 'signal' = 'exit';\n \n if (execaError.timedOut) {\n errorType = 'timeout';\n } else if (execaError.signal) {\n errorType = 'signal';\n } else if (execaError.exitCode === 127 || (error as any).code === 'ENOENT') {\n errorType = 'spawn';\n }\n\n return {\n job,\n exitCode: execaError.exitCode,\n stdout: stdoutBuffer.getContent(),\n stderr: stderrBuffer.getContent(),\n durationMs,\n errorType,\n signal: execaError.signal || undefined,\n };\n }\n}\n","const MAX_LOG_LINES = 200;\n\nexport function truncateLog(log: string, maxLines: number = MAX_LOG_LINES): string {\n const lines = log.split('\\n');\n if (lines.length <= maxLines) {\n return log;\n }\n return lines.slice(-maxLines).join('\\n');\n}\n\nexport function extractErrorSnippet(stderr: string, stdout: string, maxLines: number = 10): string {\n const errorLog = stderr || stdout;\n const lines = errorLog.split('\\n').filter(line => line.trim());\n \n if (lines.length === 0) {\n return 'No error output';\n }\n \n const relevantLines = lines.slice(-maxLines);\n return relevantLines.join('\\n');\n}\n\nexport class LogBuffer {\n private lines: string[] = [];\n private maxLines: number;\n\n constructor(maxLines: number = MAX_LOG_LINES) {\n this.maxLines = maxLines;\n }\n\n append(data: string): void {\n const newLines = data.split('\\n');\n this.lines.push(...newLines);\n \n if (this.lines.length > this.maxLines) {\n this.lines = this.lines.slice(-this.maxLines);\n }\n }\n\n getContent(): string {\n return this.lines.join('\\n');\n }\n\n clear(): void {\n this.lines = [];\n }\n}\n","import Table from 'cli-table3';\nimport type { MatrixState, Job } from '../model/types.js';\nimport { getStatusColor } from '../utils/colors.js';\nimport { Spinner } from '../utils/spinner.js';\n\nconst spinner = new Spinner();\n\nexport function renderTable(state: MatrixState): string {\n const taskNames = getUniqueTaskNames(state);\n const serviceGroups = groupJobsByService(state);\n\n const table = new Table({\n head: ['Service', ...taskNames],\n style: {\n head: [],\n border: [],\n },\n });\n\n for (const [serviceName, jobs] of serviceGroups) {\n const row: string[] = [serviceName];\n \n for (const taskName of taskNames) {\n const job = jobs.find(j => j.taskName === taskName);\n \n if (job) {\n const statusColor = getStatusColor(job.status);\n let statusText: string = job.status;\n \n if (job.status === 'running') {\n statusText = `${spinner.next()} ${job.status}`;\n } else if (job.status === 'queued') {\n statusText = `⏳ ${job.status}`;\n }\n \n row.push(statusColor(statusText));\n } else {\n row.push('-');\n }\n }\n \n table.push(row);\n }\n\n return table.toString();\n}\n\nexport function renderMinimal(state: MatrixState): string {\n const lines: string[] = [];\n \n for (const [, job] of state.jobs) {\n const statusColor = getStatusColor(job.status);\n const statusText = statusColor(job.status);\n lines.push(`${job.taskName} ${job.serviceName} ${statusText}`);\n }\n \n return lines.join('\\n');\n}\n\nexport function renderJson(state: MatrixState): string {\n const matrix: Record<string, Record<string, any>> = {};\n \n for (const [, job] of state.jobs) {\n if (!matrix[job.taskName]) {\n matrix[job.taskName] = {};\n }\n \n const result = state.results.get(job.id);\n const taskMatrix = matrix[job.taskName];\n \n if (taskMatrix) {\n taskMatrix[job.serviceName] = {\n status: job.status,\n exitCode: result?.exitCode,\n durationMs: result?.durationMs,\n };\n }\n }\n\n const errors = Array.from(state.results.values())\n .filter(r => r.job.status === 'error')\n .map(r => ({\n taskName: r.job.taskName,\n serviceName: r.job.serviceName,\n exitCode: r.exitCode,\n durationMs: r.durationMs,\n errorType: r.errorType,\n }));\n\n const output = {\n startedAt: state.startedAt,\n endedAt: state.endedAt,\n durationMs: state.endedAt ? state.endedAt - state.startedAt : undefined,\n matrix,\n errors,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nfunction groupJobsByService(state: MatrixState): Map<string, Job[]> {\n const groups = new Map<string, Job[]>();\n \n for (const [, job] of state.jobs) {\n if (!groups.has(job.serviceName)) {\n groups.set(job.serviceName, []);\n }\n groups.get(job.serviceName)!.push(job);\n }\n \n return groups;\n}\n\nfunction getUniqueTaskNames(state: MatrixState): string[] {\n const taskNames = new Set<string>();\n \n for (const [, job] of state.jobs) {\n taskNames.add(job.taskName);\n }\n \n return Array.from(taskNames).sort();\n}\n","import kleur from 'kleur';\nimport type { JobStatus } from '../model/types.js';\n\nlet colorsEnabled = true;\n\nexport function setColorsEnabled(enabled: boolean): void {\n colorsEnabled = enabled;\n kleur.enabled = enabled;\n}\n\nexport function getStatusColor(status: JobStatus): (text: string) => string {\n if (!colorsEnabled) {\n return (text: string) => text;\n }\n\n switch (status) {\n case 'done':\n return kleur.green;\n case 'error':\n return kleur.red;\n case 'running':\n return kleur.blue;\n case 'queued':\n return kleur.yellow;\n case 'skipped':\n case 'canceled':\n return kleur.gray;\n default:\n return (text: string) => text;\n }\n}\n\nexport const colors = {\n error: kleur.red,\n success: kleur.green,\n warning: kleur.yellow,\n info: kleur.blue,\n dim: kleur.gray,\n bold: kleur.bold,\n};\n\nexport const green = kleur.green;\nexport const yellow = kleur.yellow;\nexport const red = kleur.red;\nexport const cyan = kleur.cyan;\nexport const dim = kleur.gray;\n","const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nexport class Spinner {\n private frame = 0;\n\n next(): string {\n const char = SPINNER_FRAMES[this.frame];\n this.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n return char || '⠋';\n }\n\n reset(): void {\n this.frame = 0;\n }\n}\n","import type { MatrixState, ErrorEntry, JobResult } from '../model/types.js';\nimport { extractErrorSnippet } from '../utils/log.js';\n\nexport function aggregateErrors(state: MatrixState): ErrorEntry[] {\n const errors: ErrorEntry[] = [];\n\n for (const [, result] of state.results) {\n if (result.job.status === 'error') {\n const errorMessage = getErrorMessage(result);\n const logSnippet = extractErrorSnippet(result.stderr, result.stdout);\n\n errors.push({\n taskName: result.job.taskName,\n serviceName: result.job.serviceName,\n exitCode: result.exitCode,\n durationMs: result.durationMs,\n message: errorMessage,\n logSnippet,\n errorType: result.errorType,\n });\n }\n }\n\n errors.sort((a, b) => {\n if (a.taskName !== b.taskName) {\n return a.taskName.localeCompare(b.taskName);\n }\n return a.serviceName.localeCompare(b.serviceName);\n });\n\n return errors;\n}\n\nfunction getErrorMessage(result: JobResult): string {\n if (result.errorType === 'timeout') {\n return `Timeout after ${result.durationMs}ms`;\n }\n \n if (result.errorType === 'spawn') {\n return `Command not found: ${result.job.command}`;\n }\n \n if (result.errorType === 'signal') {\n return `Killed by signal: ${result.signal}`;\n }\n \n return `Exit code ${result.exitCode}`;\n}\n\nexport function formatErrors(errors: ErrorEntry[]): string {\n if (errors.length === 0) {\n return '';\n }\n\n const lines: string[] = ['', 'Errors:'];\n\n for (const error of errors) {\n lines.push(`- ${error.serviceName} has error in ${error.taskName} test:`);\n \n const snippet = error.logSnippet.trim();\n if (snippet) {\n const snippetLines = snippet.split('\\n').slice(0, 10);\n for (const line of snippetLines) {\n lines.push(` ${line}`);\n }\n } else {\n lines.push(` ${error.message}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["existsSync"]}
|