verify-grid 0.2.2 → 0.3.1

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 CHANGED
@@ -1,48 +1,288 @@
1
- # verify-grid
1
+ <div align="center">
2
2
 
3
- Run matrix of tasks across services with parallel execution and status reporting.
3
+ # 🎯 verify-grid
4
4
 
5
- ## Installation
5
+ **Run matrix of tasks across services with parallel execution and live status reporting**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/verify-grid?color=blue)](https://www.npmjs.com/package/verify-grid)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org/)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue)](https://www.typescriptlang.org/)
11
+
12
+ <img src="./example.gif" alt="Demo" width="800"/>
13
+
14
+ [Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [CLI](#-cli-commands) • [Configuration](#-configuration)
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## ✨ Features
21
+
22
+ - 🚀 **Parallel Execution** - Run tasks concurrently with configurable limits
23
+ - 🎬 **Live Updates** - Animated spinners and real-time status in your terminal
24
+ - 🎯 **Priority Control** - Execute tasks in specific order or randomize for testing
25
+ - ⚡ **Fail-Fast Mode** - Stop immediately on first error
26
+ - 📊 **Multiple Formats** - Table, minimal, or JSON output
27
+ - 🎨 **Horizontal Layout** - Optimized for terminal space (services × tasks)
28
+ - 🔧 **Task Overrides** - Customize tasks per service
29
+ - ⏱️ **Timeout Support** - Set time limits for tasks
30
+ - 🌍 **Environment Variables** - Per-task and per-service configuration
31
+ - 🎲 **Random Execution** - Test robustness with randomized order
32
+ - 🔄 **CI/CD Ready** - Proper exit codes (0=success, 1=errors, 2=config error)
33
+
34
+ ## 📦 Installation
6
35
 
7
36
  ```bash
8
37
  npm install verify-grid
9
38
  ```
10
39
 
11
- ## Quick Start
40
+ ## 🚀 Quick Start
12
41
 
13
- Create `qa.config.js` or `qa.config.mjs`:
42
+ **1. Create configuration file** `qa.config.js`:
14
43
 
15
44
  ```javascript
16
45
  export default {
17
46
  tasks: {
18
- unit: { command: 'npm', args: ['test'] },
19
- lint: { command: 'npm', args: ['run', 'lint'] },
47
+ lint: { command: 'npm', args: ['run', 'lint'], priority: 10 },
48
+ test: { command: 'npm', args: ['test'], priority: 5 },
49
+ build: { command: 'npm', args: ['run', 'build'] },
20
50
  },
21
51
  services: [
22
- { name: 'service1', cwd: './services/service1' },
23
- { name: 'service2', cwd: './services/service2' },
52
+ { name: 'api', cwd: './services/api' },
53
+ { name: 'web', cwd: './services/web' },
24
54
  ],
25
55
  };
26
56
  ```
27
57
 
28
- Run tasks:
58
+ **2. Run tasks:**
29
59
 
30
60
  ```bash
31
- qa run --tasks unit,lint --services all
32
- qa run --tasks all --services all --fail-fast
33
- qa run --tasks unit --services all --watch
61
+ # Run all tasks on all services
62
+ qa run
63
+
64
+ # Run specific tasks
65
+ qa run --tasks lint,test
66
+
67
+ # Stop on first error
68
+ qa run --fail-fast
69
+
70
+ # Randomize execution order
71
+ qa run --random
72
+ ```
73
+
74
+ ## 🎮 CLI Commands
75
+
76
+ ### `qa run`
77
+
78
+ Execute tasks across services with live status updates.
79
+
80
+ #### Options
81
+
82
+ | Option | Description | Default |
83
+ |--------|-------------|---------|
84
+ | `--tasks <tasks>` | Comma-separated tasks or "all" | `all` |
85
+ | `--services <services>` | Comma-separated services or "all" | `all` |
86
+ | `--concurrency <n>` | Maximum parallel jobs | CPU count |
87
+ | `--fail-fast` | Stop on first error | `false` |
88
+ | `--random` | Randomize execution order | `false` |
89
+ | `--no-watch` | Disable live updates | enabled in TTY |
90
+ | `--format <format>` | Output: table, minimal, json | `table` |
91
+ | `--output <path>` | Save JSON to file | - |
92
+ | `--timeout <ms>` | Timeout per task (milliseconds) | - |
93
+ | `--no-color` | Disable colors | enabled in TTY |
94
+ | `--config <path>` | Custom config file path | `qa.config.js` |
95
+
96
+ #### Examples
97
+
98
+ ```bash
99
+ # Run all tasks
100
+ qa run
101
+
102
+ # Run specific tasks on specific services
103
+ qa run --tasks lint,test --services api,web
104
+
105
+ # Limit concurrency and stop on error
106
+ qa run --concurrency 2 --fail-fast
107
+
108
+ # Output as JSON
109
+ qa run --format json --output results.json
110
+
111
+ # Set timeout (30 seconds)
112
+ qa run --timeout 30000
113
+
114
+ # Randomize for testing
115
+ qa run --random
116
+ ```
117
+
118
+ ## ⚙️ Configuration
119
+
120
+ ### Task Definition
121
+
122
+ ```javascript
123
+ {
124
+ tasks: {
125
+ taskName: {
126
+ command: 'npm', // Command to execute (required)
127
+ args: ['test'], // Arguments (optional)
128
+ shell: false, // Run in shell (optional)
129
+ timeout: 30000, // Timeout in ms (optional)
130
+ priority: 10, // Higher = earlier (optional)
131
+ env: { // Environment variables (optional)
132
+ NODE_ENV: 'test'
133
+ }
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ **Task Properties:**
140
+
141
+ | Property | Type | Required | Description |
142
+ |----------|------|----------|-------------|
143
+ | `command` | `string` | ✅ | Command to execute |
144
+ | `args` | `string[]` | ❌ | Command arguments |
145
+ | `shell` | `boolean` | ❌ | Run in shell (default: false) |
146
+ | `timeout` | `number` | ❌ | Timeout in milliseconds |
147
+ | `priority` | `number` | ❌ | Execution priority (higher first, default: 0) |
148
+ | `env` | `Record<string, string>` | ❌ | Environment variables |
149
+
150
+ ### Service Definition
151
+
152
+ ```javascript
153
+ {
154
+ services: [
155
+ {
156
+ name: 'api', // Service name (required)
157
+ cwd: './services/api', // Working directory (required)
158
+ tasks: { // Task overrides (optional)
159
+ test: {
160
+ args: ['test', '--coverage'],
161
+ priority: 5
162
+ }
163
+ },
164
+ env: { // Environment variables (optional)
165
+ PORT: '3000'
166
+ }
167
+ }
168
+ ]
169
+ }
170
+ ```
171
+
172
+ **Service Properties:**
173
+
174
+ | Property | Type | Required | Description |
175
+ |----------|------|----------|-------------|
176
+ | `name` | `string` | ✅ | Service name |
177
+ | `cwd` | `string` | ✅ | Working directory path |
178
+ | `tasks` | `Record<string, Partial<TaskDef>>` | ❌ | Override task properties |
179
+ | `env` | `Record<string, string>` | ❌ | Environment variables |
180
+
181
+ ### Full Example
182
+
183
+ ```javascript
184
+ export default {
185
+ tasks: {
186
+ lint: {
187
+ command: 'npm',
188
+ args: ['run', 'lint'],
189
+ timeout: 30000,
190
+ priority: 10,
191
+ },
192
+ test: {
193
+ command: 'npm',
194
+ args: ['test'],
195
+ priority: 5,
196
+ env: { NODE_ENV: 'test' },
197
+ },
198
+ build: {
199
+ command: 'npm',
200
+ args: ['run', 'build'],
201
+ timeout: 60000,
202
+ },
203
+ typecheck: {
204
+ command: 'tsc',
205
+ args: ['--noEmit'],
206
+ },
207
+ },
208
+ services: [
209
+ {
210
+ name: 'api',
211
+ cwd: './services/api',
212
+ env: { PORT: '3000' },
213
+ tasks: {
214
+ test: {
215
+ args: ['test', '--coverage'],
216
+ timeout: 45000,
217
+ },
218
+ },
219
+ },
220
+ {
221
+ name: 'web',
222
+ cwd: './services/web',
223
+ env: { PORT: '3001' },
224
+ },
225
+ {
226
+ name: 'shared',
227
+ cwd: './packages/shared',
228
+ tasks: {
229
+ build: { command: 'tsup' },
230
+ },
231
+ },
232
+ ],
233
+ };
234
+ ```
235
+
236
+ ## 📊 Output Formats
237
+
238
+ ### Table (default)
239
+
240
+ Horizontal matrix with live updates:
241
+
242
+ ```
243
+ Service | lint | test | build
244
+ ---------|-----------|--------------|-------
245
+ api | ✓ done | ⠋ running | ⏳ queued
246
+ web | ✓ done | ✓ done | ⏳ queued
247
+ shared | ✓ done | ✗ error | - skipped
248
+ ```
249
+
250
+ ### Minimal
251
+
252
+ Simple text output:
253
+
254
+ ```
255
+ lint api done
256
+ lint web done
257
+ test api running
258
+ ```
259
+
260
+ ### JSON
261
+
262
+ Structured output for automation:
263
+
264
+ ```json
265
+ {
266
+ "startedAt": 1234567890,
267
+ "endedAt": 1234567900,
268
+ "durationMs": 10000,
269
+ "matrix": {
270
+ "lint": {
271
+ "api": { "status": "done", "exitCode": 0, "durationMs": 1234 }
272
+ }
273
+ },
274
+ "errors": []
275
+ }
34
276
  ```
35
277
 
36
- ## Features
278
+ ## 🚦 Exit Codes
37
279
 
38
- - Parallel execution with configurable concurrency
39
- - Fail-fast mode to stop on first error
40
- - Watch mode with live updates
41
- - Multiple output formats (table, minimal, json)
42
- - Proper exit codes for CI/CD (0=success, 1=errors, 2=config error)
43
- - Task overrides per service
44
- - Timeout support
280
+ | Code | Meaning |
281
+ |------|---------|
282
+ | `0` | All tasks completed successfully ✅ |
283
+ | `1` | One or more tasks failed ❌ |
284
+ | `2` | Configuration error or invalid arguments ⚠️ |
45
285
 
46
- ## License
286
+ ## 📝 License
47
287
 
48
288
  MIT
package/dist/cli.js CHANGED
@@ -17,6 +17,7 @@ var TaskDefSchema = z.object({
17
17
  args: z.array(z.string()).optional(),
18
18
  shell: z.boolean().optional(),
19
19
  timeout: z.number().positive().optional(),
20
+ priority: z.number().optional(),
20
21
  env: z.record(z.string()).optional()
21
22
  });
22
23
  var ServiceDefSchema = z.object({
@@ -27,6 +28,7 @@ var ServiceDefSchema = z.object({
27
28
  args: z.array(z.string()).optional(),
28
29
  shell: z.boolean().optional(),
29
30
  timeout: z.number().positive().optional(),
31
+ priority: z.number().optional(),
30
32
  env: z.record(z.string()).optional()
31
33
  })).optional(),
32
34
  env: z.record(z.string()).optional()
@@ -148,6 +150,7 @@ function generateJobs(config, taskNames, serviceNames) {
148
150
  args: taskOverride?.args ?? globalTask.args,
149
151
  shell: taskOverride?.shell ?? globalTask.shell ?? true,
150
152
  timeout: taskOverride?.timeout ?? globalTask.timeout,
153
+ priority: taskOverride?.priority ?? globalTask.priority,
151
154
  env: {
152
155
  ...globalTask.env,
153
156
  ...service.env,
@@ -163,6 +166,7 @@ function generateJobs(config, taskNames, serviceNames) {
163
166
  args: mergedTask.args,
164
167
  shell: mergedTask.shell,
165
168
  timeout: mergedTask.timeout,
169
+ priority: mergedTask.priority,
166
170
  env: mergedTask.env,
167
171
  status: "queued"
168
172
  });
@@ -309,7 +313,12 @@ var Runner = class extends EventEmitter {
309
313
  for (const job of jobs) {
310
314
  this.state.jobs.set(job.id, job);
311
315
  }
312
- const executableJobs = jobs.filter((j) => j.status === "queued");
316
+ let executableJobs = jobs.filter((j) => j.status === "queued");
317
+ if (this.options.random) {
318
+ executableJobs = this.shuffleJobs(executableJobs);
319
+ } else {
320
+ executableJobs = this.sortJobsByPriority(executableJobs);
321
+ }
313
322
  for (const job of executableJobs) {
314
323
  if (this.shouldStop) {
315
324
  job.status = "canceled";
@@ -356,6 +365,23 @@ var Runner = class extends EventEmitter {
356
365
  getState() {
357
366
  return this.state;
358
367
  }
368
+ sortJobsByPriority(jobs) {
369
+ return jobs.slice().sort((a, b) => {
370
+ const priorityA = a.priority ?? 0;
371
+ const priorityB = b.priority ?? 0;
372
+ return priorityB - priorityA;
373
+ });
374
+ }
375
+ shuffleJobs(jobs) {
376
+ const shuffled = jobs.slice();
377
+ for (let i = shuffled.length - 1; i > 0; i--) {
378
+ const j = Math.floor(Math.random() * (i + 1));
379
+ const temp = shuffled[i];
380
+ shuffled[i] = shuffled[j];
381
+ shuffled[j] = temp;
382
+ }
383
+ return shuffled;
384
+ }
359
385
  };
360
386
 
361
387
  // src/render/table.ts
@@ -419,32 +445,33 @@ var Spinner = class {
419
445
  // src/render/table.ts
420
446
  var spinner = new Spinner();
421
447
  function renderTable(state) {
448
+ const taskNames = getUniqueTaskNames(state);
449
+ const serviceGroups = groupJobsByService(state);
422
450
  const table = new Table({
423
- head: ["Task", "Service", "Status"],
451
+ head: ["Service", ...taskNames],
424
452
  style: {
425
453
  head: [],
426
454
  border: []
427
455
  }
428
456
  });
429
- const taskGroups = groupJobsByTask(state);
430
- for (const [taskName, jobs] of taskGroups) {
431
- let isFirstRow = true;
432
- for (const job of jobs) {
433
- const statusColor = getStatusColor(job.status);
434
- let statusText = job.status;
435
- if (job.status === "running") {
436
- statusText = `${spinner.next()} ${job.status}`;
437
- } else if (job.status === "queued") {
438
- statusText = `\u23F3 ${job.status}`;
457
+ for (const [serviceName, jobs] of serviceGroups) {
458
+ const row = [serviceName];
459
+ for (const taskName of taskNames) {
460
+ const job = jobs.find((j) => j.taskName === taskName);
461
+ if (job) {
462
+ const statusColor = getStatusColor(job.status);
463
+ let statusText = job.status;
464
+ if (job.status === "running") {
465
+ statusText = `${spinner.next()} ${job.status}`;
466
+ } else if (job.status === "queued") {
467
+ statusText = `\u23F3 ${job.status}`;
468
+ }
469
+ row.push(statusColor(statusText));
470
+ } else {
471
+ row.push("-");
439
472
  }
440
- const coloredStatus = statusColor(statusText);
441
- table.push([
442
- isFirstRow ? taskName : "",
443
- job.serviceName,
444
- coloredStatus
445
- ]);
446
- isFirstRow = false;
447
473
  }
474
+ table.push(row);
448
475
  }
449
476
  return table.toString();
450
477
  }
@@ -489,16 +516,23 @@ function renderJson(state) {
489
516
  };
490
517
  return JSON.stringify(output, null, 2);
491
518
  }
492
- function groupJobsByTask(state) {
519
+ function groupJobsByService(state) {
493
520
  const groups = /* @__PURE__ */ new Map();
494
521
  for (const [, job] of state.jobs) {
495
- if (!groups.has(job.taskName)) {
496
- groups.set(job.taskName, []);
522
+ if (!groups.has(job.serviceName)) {
523
+ groups.set(job.serviceName, []);
497
524
  }
498
- groups.get(job.taskName).push(job);
525
+ groups.get(job.serviceName).push(job);
499
526
  }
500
527
  return groups;
501
528
  }
529
+ function getUniqueTaskNames(state) {
530
+ const taskNames = /* @__PURE__ */ new Set();
531
+ for (const [, job] of state.jobs) {
532
+ taskNames.add(job.taskName);
533
+ }
534
+ return Array.from(taskNames).sort();
535
+ }
502
536
 
503
537
  // src/render/watch.ts
504
538
  import logUpdate from "log-update";
@@ -591,7 +625,7 @@ function formatErrors(errors) {
591
625
  // src/cli.ts
592
626
  var cli = cac("qa");
593
627
  cli.usage(`${green("qa")} ${cyan("<command>")} ${dim("[options]")}`);
594
- cli.command("run", "Run tasks across services").option("--tasks <tasks>", 'Comma-separated list of tasks or "all"', { default: "all" }).option("--services <services>", 'Comma-separated list of services or "all"', { default: "all" }).option("--concurrency <n>", "Maximum parallel jobs", { default: cpus().length }).option("--parallel", "Alias for concurrency").option("--fail-fast", "Stop on first error", { default: false }).option("--watch", "Watch mode with live updates", { default: false }).option("--format <format>", "Output format: table, minimal, json", { default: "table" }).option("--output <path>", "Save JSON output to file").option("--verbose", "Verbose output", { default: false }).option("--quiet", "Minimal output", { default: false }).option("--timeout <ms>", "Timeout per task in milliseconds").option("--no-color", "Disable colors").option("--config <path>", "Path to config file").action(async (options) => {
628
+ cli.command("run", "Run tasks across services").option("--tasks <tasks>", 'Comma-separated list of tasks or "all"', { default: "all" }).option("--services <services>", 'Comma-separated list of services or "all"', { default: "all" }).option("--concurrency <n>", "Maximum parallel jobs", { default: cpus().length }).option("--parallel", "Alias for concurrency").option("--fail-fast", "Stop on first error", { default: false }).option("--random", "Randomize job execution order", { default: false }).option("--no-watch", "Disable live updates").option("--format <format>", "Output format: table, minimal, json", { default: "table" }).option("--output <path>", "Save JSON output to file").option("--verbose", "Verbose output", { default: false }).option("--quiet", "Minimal output", { default: false }).option("--timeout <ms>", "Timeout per task in milliseconds").option("--no-color", "Disable colors").option("--config <path>", "Path to config file").action(async (options) => {
595
629
  try {
596
630
  const isTTY = process.stdout.isTTY;
597
631
  const colorsEnabled2 = options.color !== false && isTTY;
@@ -618,7 +652,8 @@ cli.command("run", "Run tasks across services").option("--tasks <tasks>", 'Comma
618
652
  verbose: options.verbose,
619
653
  quiet: options.quiet,
620
654
  timeout: options.timeout ? Number(options.timeout) : void 0,
621
- noColor: !colorsEnabled2
655
+ noColor: !colorsEnabled2,
656
+ random: options.random
622
657
  };
623
658
  let jobs;
624
659
  try {
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('--watch', 'Watch mode with live updates', { default: false })\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 };\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) {\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 (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 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 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 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 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 const executableJobs = jobs.filter(j => j.status === 'queued');\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","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 table = new Table({\n head: ['Task', 'Service', 'Status'],\n style: {\n head: [],\n border: [],\n },\n });\n\n const taskGroups = groupJobsByTask(state);\n\n for (const [taskName, jobs] of taskGroups) {\n let isFirstRow = true;\n \n for (const job of jobs) {\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 const coloredStatus = statusColor(statusText);\n \n table.push([\n isFirstRow ? taskName : '',\n job.serviceName,\n coloredStatus,\n ]);\n \n isFirstRow = false;\n }\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 groupJobsByTask(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.taskName)) {\n groups.set(job.taskName, []);\n }\n groups.get(job.taskName)!.push(job);\n }\n \n return groups;\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) {\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 (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"]}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ interface TaskDef {
8
8
  shell?: boolean;
9
9
  timeout?: number;
10
10
  env?: Record<string, string>;
11
+ priority?: number;
11
12
  }
12
13
  interface ServiceDef {
13
14
  name: string;
@@ -25,6 +26,7 @@ interface Job {
25
26
  shell?: boolean;
26
27
  timeout?: number;
27
28
  env?: Record<string, string>;
29
+ priority?: number;
28
30
  status: JobStatus;
29
31
  startedAt?: number;
30
32
  endedAt?: number;
@@ -66,6 +68,7 @@ interface RunOptions {
66
68
  quiet: boolean;
67
69
  timeout?: number;
68
70
  noColor: boolean;
71
+ random?: boolean;
69
72
  }
70
73
  interface Config {
71
74
  services: ServiceDef[];
@@ -86,6 +89,8 @@ declare class Runner extends EventEmitter {
86
89
  private handleInterrupt;
87
90
  run(jobs: Job[]): Promise<MatrixState>;
88
91
  getState(): MatrixState;
92
+ private sortJobsByPriority;
93
+ private shuffleJobs;
89
94
  }
90
95
 
91
96
  declare function renderTable(state: MatrixState): string;
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ var TaskDefSchema = z.object({
11
11
  args: z.array(z.string()).optional(),
12
12
  shell: z.boolean().optional(),
13
13
  timeout: z.number().positive().optional(),
14
+ priority: z.number().optional(),
14
15
  env: z.record(z.string()).optional()
15
16
  });
16
17
  var ServiceDefSchema = z.object({
@@ -21,6 +22,7 @@ var ServiceDefSchema = z.object({
21
22
  args: z.array(z.string()).optional(),
22
23
  shell: z.boolean().optional(),
23
24
  timeout: z.number().positive().optional(),
25
+ priority: z.number().optional(),
24
26
  env: z.record(z.string()).optional()
25
27
  })).optional(),
26
28
  env: z.record(z.string()).optional()
@@ -142,6 +144,7 @@ function generateJobs(config, taskNames, serviceNames) {
142
144
  args: taskOverride?.args ?? globalTask.args,
143
145
  shell: taskOverride?.shell ?? globalTask.shell ?? true,
144
146
  timeout: taskOverride?.timeout ?? globalTask.timeout,
147
+ priority: taskOverride?.priority ?? globalTask.priority,
145
148
  env: {
146
149
  ...globalTask.env,
147
150
  ...service.env,
@@ -157,6 +160,7 @@ function generateJobs(config, taskNames, serviceNames) {
157
160
  args: mergedTask.args,
158
161
  shell: mergedTask.shell,
159
162
  timeout: mergedTask.timeout,
163
+ priority: mergedTask.priority,
160
164
  env: mergedTask.env,
161
165
  status: "queued"
162
166
  });
@@ -303,7 +307,12 @@ var Runner = class extends EventEmitter {
303
307
  for (const job of jobs) {
304
308
  this.state.jobs.set(job.id, job);
305
309
  }
306
- const executableJobs = jobs.filter((j) => j.status === "queued");
310
+ let executableJobs = jobs.filter((j) => j.status === "queued");
311
+ if (this.options.random) {
312
+ executableJobs = this.shuffleJobs(executableJobs);
313
+ } else {
314
+ executableJobs = this.sortJobsByPriority(executableJobs);
315
+ }
307
316
  for (const job of executableJobs) {
308
317
  if (this.shouldStop) {
309
318
  job.status = "canceled";
@@ -350,6 +359,23 @@ var Runner = class extends EventEmitter {
350
359
  getState() {
351
360
  return this.state;
352
361
  }
362
+ sortJobsByPriority(jobs) {
363
+ return jobs.slice().sort((a, b) => {
364
+ const priorityA = a.priority ?? 0;
365
+ const priorityB = b.priority ?? 0;
366
+ return priorityB - priorityA;
367
+ });
368
+ }
369
+ shuffleJobs(jobs) {
370
+ const shuffled = jobs.slice();
371
+ for (let i = shuffled.length - 1; i > 0; i--) {
372
+ const j = Math.floor(Math.random() * (i + 1));
373
+ const temp = shuffled[i];
374
+ shuffled[i] = shuffled[j];
375
+ shuffled[j] = temp;
376
+ }
377
+ return shuffled;
378
+ }
353
379
  };
354
380
 
355
381
  // src/render/table.ts
@@ -409,32 +435,33 @@ var Spinner = class {
409
435
  // src/render/table.ts
410
436
  var spinner = new Spinner();
411
437
  function renderTable(state) {
438
+ const taskNames = getUniqueTaskNames(state);
439
+ const serviceGroups = groupJobsByService(state);
412
440
  const table = new Table({
413
- head: ["Task", "Service", "Status"],
441
+ head: ["Service", ...taskNames],
414
442
  style: {
415
443
  head: [],
416
444
  border: []
417
445
  }
418
446
  });
419
- const taskGroups = groupJobsByTask(state);
420
- for (const [taskName, jobs] of taskGroups) {
421
- let isFirstRow = true;
422
- for (const job of jobs) {
423
- const statusColor = getStatusColor(job.status);
424
- let statusText = job.status;
425
- if (job.status === "running") {
426
- statusText = `${spinner.next()} ${job.status}`;
427
- } else if (job.status === "queued") {
428
- statusText = `\u23F3 ${job.status}`;
447
+ for (const [serviceName, jobs] of serviceGroups) {
448
+ const row = [serviceName];
449
+ for (const taskName of taskNames) {
450
+ const job = jobs.find((j) => j.taskName === taskName);
451
+ if (job) {
452
+ const statusColor = getStatusColor(job.status);
453
+ let statusText = job.status;
454
+ if (job.status === "running") {
455
+ statusText = `${spinner.next()} ${job.status}`;
456
+ } else if (job.status === "queued") {
457
+ statusText = `\u23F3 ${job.status}`;
458
+ }
459
+ row.push(statusColor(statusText));
460
+ } else {
461
+ row.push("-");
429
462
  }
430
- const coloredStatus = statusColor(statusText);
431
- table.push([
432
- isFirstRow ? taskName : "",
433
- job.serviceName,
434
- coloredStatus
435
- ]);
436
- isFirstRow = false;
437
463
  }
464
+ table.push(row);
438
465
  }
439
466
  return table.toString();
440
467
  }
@@ -479,16 +506,23 @@ function renderJson(state) {
479
506
  };
480
507
  return JSON.stringify(output, null, 2);
481
508
  }
482
- function groupJobsByTask(state) {
509
+ function groupJobsByService(state) {
483
510
  const groups = /* @__PURE__ */ new Map();
484
511
  for (const [, job] of state.jobs) {
485
- if (!groups.has(job.taskName)) {
486
- groups.set(job.taskName, []);
512
+ if (!groups.has(job.serviceName)) {
513
+ groups.set(job.serviceName, []);
487
514
  }
488
- groups.get(job.taskName).push(job);
515
+ groups.get(job.serviceName).push(job);
489
516
  }
490
517
  return groups;
491
518
  }
519
+ function getUniqueTaskNames(state) {
520
+ const taskNames = /* @__PURE__ */ new Set();
521
+ for (const [, job] of state.jobs) {
522
+ taskNames.add(job.taskName);
523
+ }
524
+ return Array.from(taskNames).sort();
525
+ }
492
526
 
493
527
  // src/report/aggregator.ts
494
528
  function aggregateErrors(state) {
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 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 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 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 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 const executableJobs = jobs.filter(j => j.status === 'queued');\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","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 table = new Table({\n head: ['Task', 'Service', 'Status'],\n style: {\n head: [],\n border: [],\n },\n });\n\n const taskGroups = groupJobsByTask(state);\n\n for (const [taskName, jobs] of taskGroups) {\n let isFirstRow = true;\n \n for (const job of jobs) {\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 const coloredStatus = statusColor(statusText);\n \n table.push([\n isFirstRow ? taskName : '',\n job.serviceName,\n coloredStatus,\n ]);\n \n isFirstRow = false;\n }\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 groupJobsByTask(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.taskName)) {\n groups.set(job.taskName, []);\n }\n groups.get(job.taskName)!.push(job);\n }\n \n return groups;\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 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"]}
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "verify-grid",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Run matrix of tasks across services with parallel execution and status reporting",
5
5
  "type": "module",
6
6
  "author": "",
7
7
  "license": "MIT",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/your-username/verify-grid.git"
10
+ "url": "https://github.com/kirull1/verify-grid.git"
11
11
  },
12
12
  "bugs": {
13
- "url": "https://github.com/your-username/verify-grid/issues"
13
+ "url": "https://github.com/kirull1/verify-grid/issues"
14
14
  },
15
- "homepage": "https://github.com/your-username/verify-grid#readme",
15
+ "homepage": "https://github.com/kirull1/verify-grid#readme",
16
16
  "bin": {
17
17
  "qa": "./dist/cli.js"
18
18
  },