@number10/ci-runner-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/dist/.tsbuildinfo.build +1 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +852 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/cliOptions.d.ts +36 -0
  9. package/dist/cliOptions.d.ts.map +1 -0
  10. package/dist/config/loadConfig.d.ts +14 -0
  11. package/dist/config/loadConfig.d.ts.map +1 -0
  12. package/dist/config/mapConfigToRun.d.ts +60 -0
  13. package/dist/config/mapConfigToRun.d.ts.map +1 -0
  14. package/dist/config/types.d.ts +70 -0
  15. package/dist/config/types.d.ts.map +1 -0
  16. package/dist/index.d.ts +8 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/internal/core/contracts/executor.d.ts +35 -0
  19. package/dist/internal/core/contracts/executor.d.ts.map +1 -0
  20. package/dist/internal/core/contracts/parser.d.ts +45 -0
  21. package/dist/internal/core/contracts/parser.d.ts.map +1 -0
  22. package/dist/internal/core/contracts/reporter.d.ts +34 -0
  23. package/dist/internal/core/contracts/reporter.d.ts.map +1 -0
  24. package/dist/internal/core/contracts/run.d.ts +60 -0
  25. package/dist/internal/core/contracts/run.d.ts.map +1 -0
  26. package/dist/internal/core/contracts/step.d.ts +82 -0
  27. package/dist/internal/core/contracts/step.d.ts.map +1 -0
  28. package/dist/internal/core/execution/nodeCommandExecutor.d.ts +8 -0
  29. package/dist/internal/core/execution/nodeCommandExecutor.d.ts.map +1 -0
  30. package/dist/internal/core/index.d.ts +10 -0
  31. package/dist/internal/core/index.d.ts.map +1 -0
  32. package/dist/internal/core/parsers/parserRegistry.d.ts +29 -0
  33. package/dist/internal/core/parsers/parserRegistry.d.ts.map +1 -0
  34. package/dist/internal/core/reporters/jsonFormatter.d.ts +10 -0
  35. package/dist/internal/core/reporters/jsonFormatter.d.ts.map +1 -0
  36. package/dist/internal/core/runner/pipelineRunner.d.ts +34 -0
  37. package/dist/internal/core/runner/pipelineRunner.d.ts.map +1 -0
  38. package/dist/parsers/defaultStepParsers.d.ts +8 -0
  39. package/dist/parsers/defaultStepParsers.d.ts.map +1 -0
  40. package/dist/reporters/prettyReporter.d.ts +46 -0
  41. package/dist/reporters/prettyReporter.d.ts.map +1 -0
  42. package/dist/runPipeline.d.ts +26 -0
  43. package/dist/runPipeline.d.ts.map +1 -0
  44. package/package.json +42 -0
package/dist/cli.js ADDED
@@ -0,0 +1,852 @@
1
+ #!/usr/bin/env node
2
+ import { resolve as m, dirname as R, relative as k } from "node:path";
3
+ import { watch as j } from "node:fs";
4
+ import { spawn as T } from "node:child_process";
5
+ import { readFile as v, mkdtemp as A, writeFile as P, rm as N } from "node:fs/promises";
6
+ import { tmpdir as _ } from "node:os";
7
+ import { pathToFileURL as W } from "node:url";
8
+ import b from "typescript";
9
+ const D = (t, e) => {
10
+ let r, s = "pretty", n = !1, o = !1, i = !1, p = !1, c = e;
11
+ for (let u = 0; u < t.length; u += 1) {
12
+ const d = t[u];
13
+ if (d) {
14
+ if (d === "--help" || d === "-h") {
15
+ p = !0;
16
+ continue;
17
+ }
18
+ if (d === "--verbose") {
19
+ n = !0;
20
+ continue;
21
+ }
22
+ if (d === "--watch") {
23
+ o = !0;
24
+ continue;
25
+ }
26
+ if (d === "--fail-fast") {
27
+ i = !0;
28
+ continue;
29
+ }
30
+ if (d === "--format") {
31
+ const a = t[u + 1];
32
+ if (!a)
33
+ throw new Error("--format requires a value");
34
+ if (a !== "pretty" && a !== "json")
35
+ throw new Error('--format must be "pretty" or "json"');
36
+ s = a, u += 1;
37
+ continue;
38
+ }
39
+ if (d.startsWith("--format=")) {
40
+ const a = d.slice(9);
41
+ if (a !== "pretty" && a !== "json")
42
+ throw new Error('--format must be "pretty" or "json"');
43
+ s = a;
44
+ continue;
45
+ }
46
+ if (d === "--config") {
47
+ const a = t[u + 1];
48
+ if (!a)
49
+ throw new Error("--config requires a value");
50
+ r = a, u += 1;
51
+ continue;
52
+ }
53
+ if (d.startsWith("--config=")) {
54
+ r = d.slice(9);
55
+ continue;
56
+ }
57
+ if (d === "--cwd") {
58
+ const a = t[u + 1];
59
+ if (!a)
60
+ throw new Error("--cwd requires a value");
61
+ c = m(e, a), u += 1;
62
+ continue;
63
+ }
64
+ if (d.startsWith("--cwd=")) {
65
+ c = m(e, d.slice(6));
66
+ continue;
67
+ }
68
+ throw new Error(`Unknown argument: ${d}`);
69
+ }
70
+ }
71
+ return {
72
+ cwd: c,
73
+ configPath: r,
74
+ format: s,
75
+ verbose: n,
76
+ watch: o,
77
+ failFast: i,
78
+ help: p
79
+ };
80
+ }, F = () => [
81
+ "Usage: ci-runner [options]",
82
+ "",
83
+ "Options:",
84
+ " --config <path> Config file path (default: ci.config.ts or ci.config.json)",
85
+ " --format <type> Output format: pretty | json (default: pretty)",
86
+ " --verbose Show stdout/stderr for successful steps",
87
+ " --watch Re-run on file changes",
88
+ " --fail-fast Stop after first non-optional failure",
89
+ " --cwd <path> Base working directory",
90
+ " -h, --help Show this help"
91
+ ].join(`
92
+ `), B = () => async (t) => {
93
+ const e = Date.now();
94
+ return await new Promise((r) => {
95
+ const s = { ...process.env, ...t.env }, n = T(t.command, {
96
+ cwd: t.cwd,
97
+ env: s,
98
+ shell: !0,
99
+ stdio: ["ignore", "pipe", "pipe"]
100
+ });
101
+ let o = "", i = "", p = !1, c, u = !1;
102
+ const d = typeof t.timeoutMs == "number" && t.timeoutMs > 0 ? setTimeout(() => {
103
+ p = !0, n.kill("SIGTERM");
104
+ }, t.timeoutMs) : null;
105
+ n.stdout.on("data", (a) => {
106
+ o += a.toString("utf8");
107
+ }), n.stderr.on("data", (a) => {
108
+ i += a.toString("utf8");
109
+ }), n.on("error", (a) => {
110
+ c = a;
111
+ }), n.on("close", (a, w) => {
112
+ if (u)
113
+ return;
114
+ u = !0, d && clearTimeout(d);
115
+ const M = Date.now() - e;
116
+ r({
117
+ successful: !p && a === 0 && c === void 0,
118
+ timedOut: p,
119
+ durationMs: M,
120
+ exitCode: a,
121
+ signal: w,
122
+ stdout: o,
123
+ stderr: i,
124
+ error: c
125
+ });
126
+ });
127
+ });
128
+ };
129
+ class H {
130
+ parsers;
131
+ /**
132
+ * Creates a parser registry.
133
+ *
134
+ * @param parsers Initial parser list.
135
+ */
136
+ constructor(e = []) {
137
+ this.parsers = [...e];
138
+ }
139
+ /**
140
+ * Adds a parser to the registry.
141
+ *
142
+ * @param parser Parser instance.
143
+ */
144
+ register(e) {
145
+ this.parsers.push(e);
146
+ }
147
+ /**
148
+ * Parses output using the first matching parser that returns a metric.
149
+ *
150
+ * @param step Step definition.
151
+ * @param output Step output payload.
152
+ * @returns Parsed metric or null.
153
+ */
154
+ parse(e, r) {
155
+ for (const s of this.parsers) {
156
+ if (!s.matches(e))
157
+ continue;
158
+ const n = s.parse(r);
159
+ if (n)
160
+ return n;
161
+ }
162
+ return null;
163
+ }
164
+ }
165
+ const S = (t, e = 2) => JSON.stringify(t, null, e);
166
+ class I {
167
+ options;
168
+ /**
169
+ * Creates a pipeline runner.
170
+ *
171
+ * @param options Runtime options.
172
+ */
173
+ constructor(e) {
174
+ this.options = {
175
+ ...e,
176
+ continueOnError: e.continueOnError ?? !0,
177
+ now: e.now ?? Date.now,
178
+ sleep: e.sleep ?? ((r) => new Promise((s) => {
179
+ setTimeout(s, r);
180
+ }))
181
+ };
182
+ }
183
+ /**
184
+ * Executes all configured pipeline steps.
185
+ *
186
+ * @returns Final pipeline result.
187
+ */
188
+ async run() {
189
+ const e = this.options.now(), r = [];
190
+ await this.emitPipelineStart();
191
+ for (const [p, c] of this.options.steps.entries()) {
192
+ await this.emitStepStart(c, p);
193
+ const u = await this.executeStep(c);
194
+ r.push(u), await this.emitStepComplete(u, p);
195
+ const d = u.status === "failed" || u.status === "timed_out";
196
+ if (!this.options.continueOnError && d)
197
+ break;
198
+ }
199
+ const s = this.options.now(), n = q(r, s - e), o = n.failed > 0 || n.timedOut > 0 ? 1 : 0, i = {
200
+ steps: r,
201
+ summary: n,
202
+ exitCode: o,
203
+ startedAt: e,
204
+ finishedAt: s
205
+ };
206
+ return await this.emitPipelineComplete(i), i;
207
+ }
208
+ async executeStep(e) {
209
+ const r = this.options.now(), s = V(e), n = { ...this.options.env, ...e.env };
210
+ let o = 0, i = null;
211
+ for (; o < s.maxAttempts; ) {
212
+ o += 1;
213
+ const c = await this.options.executor({
214
+ command: e.command,
215
+ cwd: e.cwd ?? this.options.cwd ?? process.cwd(),
216
+ env: n,
217
+ timeoutMs: e.timeoutMs
218
+ });
219
+ if (i = c, c.successful)
220
+ return this.buildStepResult({
221
+ step: e,
222
+ status: "passed",
223
+ reason: void 0,
224
+ attempts: o,
225
+ startedAt: r,
226
+ output: c
227
+ });
228
+ const u = c.timedOut, d = s.retryOnTimeout && u, a = !u;
229
+ if (o < s.maxAttempts && (a || d) && s.maxAttempts > 1) {
230
+ s.delayMs > 0 && await this.options.sleep(s.delayMs);
231
+ continue;
232
+ }
233
+ return this.buildFailedResult(e, o, r, c);
234
+ }
235
+ const p = z();
236
+ return this.buildStepResult({
237
+ step: e,
238
+ status: "failed",
239
+ reason: "command_failed",
240
+ attempts: o,
241
+ startedAt: r,
242
+ output: i ?? p
243
+ });
244
+ }
245
+ buildFailedResult(e, r, s, n) {
246
+ return e.optional ? this.buildStepResult({
247
+ step: e,
248
+ status: "skipped",
249
+ reason: "optional_step_failed",
250
+ attempts: r,
251
+ startedAt: s,
252
+ output: n
253
+ }) : n.timedOut ? this.buildStepResult({
254
+ step: e,
255
+ status: "timed_out",
256
+ reason: "command_timeout",
257
+ attempts: r,
258
+ startedAt: s,
259
+ output: n
260
+ }) : this.buildStepResult({
261
+ step: e,
262
+ status: "failed",
263
+ reason: "command_failed",
264
+ attempts: r,
265
+ startedAt: s,
266
+ output: n
267
+ });
268
+ }
269
+ buildStepResult(e) {
270
+ const r = this.options.now(), s = this.options.parserResolver?.parse(e.step, e.output) ?? null;
271
+ return {
272
+ id: e.step.id,
273
+ name: e.step.name,
274
+ status: e.status,
275
+ reason: e.reason,
276
+ attempts: e.attempts,
277
+ retried: e.attempts > 1,
278
+ startedAt: e.startedAt,
279
+ finishedAt: r,
280
+ durationMs: r - e.startedAt,
281
+ output: {
282
+ exitCode: e.output.exitCode,
283
+ signal: e.output.signal,
284
+ stdout: e.output.stdout,
285
+ stderr: e.output.stderr
286
+ },
287
+ metrics: s
288
+ };
289
+ }
290
+ async emitPipelineStart() {
291
+ const e = this.options.reporters ?? [];
292
+ for (const r of e)
293
+ await r.onPipelineStart?.(this.options.steps);
294
+ }
295
+ async emitStepStart(e, r) {
296
+ const s = this.options.reporters ?? [];
297
+ for (const n of s)
298
+ await n.onStepStart?.(e, r);
299
+ }
300
+ async emitStepComplete(e, r) {
301
+ const s = this.options.reporters ?? [];
302
+ for (const n of s)
303
+ await n.onStepComplete?.(e, r);
304
+ }
305
+ async emitPipelineComplete(e) {
306
+ const r = this.options.reporters ?? [];
307
+ for (const s of r)
308
+ await s.onPipelineComplete?.(e);
309
+ }
310
+ }
311
+ const L = (t) => new I(t), V = (t) => {
312
+ const e = Math.max(1, t.retry?.maxAttempts ?? 1), r = Math.max(0, t.retry?.delayMs ?? 0), s = t.retry?.retryOnTimeout ?? !1;
313
+ return {
314
+ maxAttempts: e,
315
+ delayMs: r,
316
+ retryOnTimeout: s
317
+ };
318
+ }, q = (t, e) => {
319
+ const r = t.filter((i) => i.status === "passed").length, s = t.filter((i) => i.status === "failed").length, n = t.filter((i) => i.status === "skipped").length, o = t.filter((i) => i.status === "timed_out").length;
320
+ return {
321
+ total: t.length,
322
+ passed: r,
323
+ failed: s,
324
+ skipped: n,
325
+ timedOut: o,
326
+ durationMs: e
327
+ };
328
+ }, z = () => ({
329
+ successful: !1,
330
+ timedOut: !1,
331
+ durationMs: 0,
332
+ exitCode: null,
333
+ signal: null,
334
+ stdout: "",
335
+ stderr: ""
336
+ }), U = (t, e, r) => {
337
+ const s = t.cwd ? m(e, t.cwd) : e, n = { ...process.env, ...t.env }, o = [], i = [];
338
+ for (const c of t.steps) {
339
+ const u = J(c, n);
340
+ if (u) {
341
+ i.push({
342
+ id: c.id,
343
+ name: c.name,
344
+ reason: u.reason,
345
+ requiredEnv: u.requiredEnv
346
+ });
347
+ continue;
348
+ }
349
+ o.push(G(c, s));
350
+ }
351
+ const p = r ? !1 : t.continueOnError ?? !0;
352
+ return {
353
+ steps: o,
354
+ excludedSteps: i,
355
+ cwd: s,
356
+ env: n,
357
+ continueOnError: p
358
+ };
359
+ }, G = (t, e) => ({
360
+ id: t.id,
361
+ name: t.name,
362
+ command: t.command,
363
+ cwd: t.cwd ? m(e, t.cwd) : e,
364
+ env: t.env,
365
+ optional: t.optional,
366
+ timeoutMs: t.timeoutMs,
367
+ retry: t.retry
368
+ }), J = (t, e) => {
369
+ if (t.enabled === !1)
370
+ return { reason: "disabled" };
371
+ const r = t.when?.env;
372
+ if (!r)
373
+ return null;
374
+ const s = {};
375
+ for (const [n, o] of Object.entries(r))
376
+ e[n] !== o && (s[n] = o);
377
+ return Object.keys(s).length > 0 ? {
378
+ reason: "env_mismatch",
379
+ requiredEnv: s
380
+ } : null;
381
+ }, K = async (t, e) => {
382
+ const r = await Q(t, e);
383
+ if (!r)
384
+ throw new Error("No config file found. Expected ci.config.ts or ci.config.json");
385
+ const s = await X(r);
386
+ return {
387
+ config: tt(s),
388
+ configFilePath: r
389
+ };
390
+ }, Q = async (t, e) => {
391
+ if (e)
392
+ return m(t, e);
393
+ const r = [m(t, "ci.config.ts"), m(t, "ci.config.json")];
394
+ for (const s of r)
395
+ try {
396
+ return await v(s, "utf8"), s;
397
+ } catch {
398
+ continue;
399
+ }
400
+ return null;
401
+ }, X = async (t) => {
402
+ if (t.endsWith(".json")) {
403
+ const e = await v(t, "utf8");
404
+ return JSON.parse(e);
405
+ }
406
+ if (t.endsWith(".ts"))
407
+ return await Y(t);
408
+ throw new Error(`Unsupported config extension: ${t}`);
409
+ }, Y = async (t) => {
410
+ const e = await v(t, "utf8"), r = b.transpileModule(e, {
411
+ compilerOptions: {
412
+ module: b.ModuleKind.ESNext,
413
+ target: b.ScriptTarget.ES2022,
414
+ esModuleInterop: !0
415
+ },
416
+ fileName: t,
417
+ reportDiagnostics: !0
418
+ });
419
+ if (r.diagnostics && r.diagnostics.length > 0) {
420
+ const o = b.formatDiagnosticsWithColorAndContext(r.diagnostics, {
421
+ getCurrentDirectory: () => R(t),
422
+ getCanonicalFileName: (i) => i,
423
+ getNewLine: () => `
424
+ `
425
+ });
426
+ throw new Error(`Failed to transpile ${t}
427
+ ${o}`);
428
+ }
429
+ const s = await A(m(_(), "ci-runner-config-")), n = m(s, "config.mjs");
430
+ try {
431
+ await P(n, r.outputText, "utf8");
432
+ const i = await import(`${W(n).href}?v=${Date.now()}`);
433
+ if (i.default !== void 0)
434
+ return Z(i.default);
435
+ if (i.config !== void 0)
436
+ return i.config;
437
+ throw new Error(`Config module ${t} must export default or named "config"`);
438
+ } finally {
439
+ await N(s, { recursive: !0, force: !0 });
440
+ }
441
+ }, Z = (t) => f(t) && "default" in t ? t.default : t, tt = (t) => {
442
+ if (!f(t))
443
+ throw new Error("Config must be an object");
444
+ const e = t.steps;
445
+ if (!Array.isArray(e))
446
+ throw new Error("Config must provide a steps array");
447
+ const r = e.map(et), s = y(t.continueOnError, "continueOnError"), n = O(t.env, "env"), o = E(t.cwd, "cwd"), i = rt(t.output);
448
+ return {
449
+ steps: r,
450
+ continueOnError: s,
451
+ env: n,
452
+ cwd: o,
453
+ output: i
454
+ };
455
+ }, et = (t, e) => {
456
+ if (!f(t))
457
+ throw new Error(`steps[${e}] must be an object`);
458
+ const r = $(t.id, `steps[${e}].id`), s = $(t.name, `steps[${e}].name`), n = $(t.command, `steps[${e}].command`), o = y(t.enabled, `steps[${e}].enabled`), i = E(t.cwd, `steps[${e}].cwd`), p = O(t.env, `steps[${e}].env`), c = y(t.optional, `steps[${e}].optional`), u = x(t.timeoutMs, `steps[${e}].timeoutMs`), d = st(t.retry, `steps[${e}].retry`), a = nt(t.when, `steps[${e}].when`);
459
+ return {
460
+ id: r,
461
+ name: s,
462
+ command: n,
463
+ enabled: o,
464
+ cwd: i,
465
+ env: p,
466
+ optional: c,
467
+ timeoutMs: u,
468
+ retry: d,
469
+ when: a
470
+ };
471
+ }, rt = (t) => {
472
+ if (t === void 0)
473
+ return;
474
+ if (!f(t))
475
+ throw new Error("output must be an object");
476
+ const e = t.format;
477
+ if (e !== void 0 && e !== "pretty" && e !== "json")
478
+ throw new Error('output.format must be "pretty" or "json"');
479
+ const r = y(t.verbose, "output.verbose");
480
+ return {
481
+ format: e,
482
+ verbose: r
483
+ };
484
+ }, st = (t, e) => {
485
+ if (t === void 0)
486
+ return;
487
+ if (!f(t))
488
+ throw new Error(`${e} must be an object`);
489
+ const r = ot(t.maxAttempts, `${e}.maxAttempts`), s = x(t.delayMs, `${e}.delayMs`), n = y(t.retryOnTimeout, `${e}.retryOnTimeout`);
490
+ return {
491
+ maxAttempts: r,
492
+ delayMs: s,
493
+ retryOnTimeout: n
494
+ };
495
+ }, nt = (t, e) => {
496
+ if (t === void 0)
497
+ return;
498
+ if (!f(t))
499
+ throw new Error(`${e} must be an object`);
500
+ return {
501
+ env: O(t.env, `${e}.env`)
502
+ };
503
+ }, $ = (t, e) => {
504
+ if (typeof t != "string" || t.length === 0)
505
+ throw new Error(`${e} must be a non-empty string`);
506
+ return t;
507
+ }, ot = (t, e) => {
508
+ if (typeof t != "number" || Number.isNaN(t))
509
+ throw new Error(`${e} must be a valid number`);
510
+ return t;
511
+ }, E = (t, e) => {
512
+ if (t !== void 0) {
513
+ if (typeof t != "string")
514
+ throw new Error(`${e} must be a string`);
515
+ return t;
516
+ }
517
+ }, x = (t, e) => {
518
+ if (t !== void 0) {
519
+ if (typeof t != "number" || Number.isNaN(t))
520
+ throw new Error(`${e} must be a valid number`);
521
+ return t;
522
+ }
523
+ }, y = (t, e) => {
524
+ if (t !== void 0) {
525
+ if (typeof t != "boolean")
526
+ throw new Error(`${e} must be a boolean`);
527
+ return t;
528
+ }
529
+ }, O = (t, e) => {
530
+ if (t === void 0)
531
+ return;
532
+ if (!f(t))
533
+ throw new Error(`${e} must be an object`);
534
+ const r = Object.entries(t), s = {};
535
+ for (const [n, o] of r) {
536
+ if (typeof o != "string")
537
+ throw new Error(`${e}.${n} must be a string`);
538
+ s[n] = o;
539
+ }
540
+ return s;
541
+ }, f = (t) => typeof t == "object" && t !== null && !Array.isArray(t), it = () => [
542
+ {
543
+ id: "vitest-summary-parser",
544
+ matches: (t) => h(t, "vitest"),
545
+ parse: (t) => {
546
+ const r = g(`${t.stdout}
547
+ ${t.stderr}`).match(/\bTests?\s+(\d+)\s+passed\b/i);
548
+ return r ? {
549
+ label: "tests_passed",
550
+ value: Number(r[1])
551
+ } : null;
552
+ }
553
+ },
554
+ {
555
+ id: "playwright-summary-parser",
556
+ matches: (t) => h(t, "playwright") || h(t, "e2e"),
557
+ parse: (t) => {
558
+ const r = g(`${t.stdout}
559
+ ${t.stderr}`).match(/^\s*(\d+)\s+passed(?:\s|\()/im);
560
+ return r ? {
561
+ label: "tests_passed",
562
+ value: Number(r[1])
563
+ } : null;
564
+ }
565
+ },
566
+ {
567
+ id: "generic-tests-parser",
568
+ matches: (t) => h(t, "test"),
569
+ parse: (t) => {
570
+ const r = g(`${t.stdout}
571
+ ${t.stderr}`).match(/(^|\s)(\d+)\s+passed(\s|$)/i);
572
+ return r ? {
573
+ label: "tests_passed",
574
+ value: Number(r[2])
575
+ } : null;
576
+ }
577
+ },
578
+ {
579
+ id: "lint-warning-summary-parser",
580
+ matches: (t) => h(t, "lint"),
581
+ parse: (t) => {
582
+ const e = g(`${t.stdout}
583
+ ${t.stderr}`), r = ct(e);
584
+ return r === null ? null : {
585
+ label: "warnings",
586
+ value: r
587
+ };
588
+ }
589
+ },
590
+ {
591
+ id: "workspace-task-summary-parser",
592
+ matches: () => !0,
593
+ parse: (t) => {
594
+ const e = g(`${t.stdout}
595
+ ${t.stderr}`);
596
+ return at(e);
597
+ }
598
+ }
599
+ ], at = (t) => {
600
+ const e = t.match(/Tasks:\s*\d+\s+\w+,\s*(\d+)\s+total/i);
601
+ if (e)
602
+ return {
603
+ label: "tasks",
604
+ value: Number(e[1])
605
+ };
606
+ const r = t.match(
607
+ /Scope:\s*(?:all\s+)?(\d+)(?:\s+of\s+\d+)?\s+workspace\s+projects?/i
608
+ );
609
+ if (r)
610
+ return {
611
+ label: "tasks",
612
+ value: Number(r[1])
613
+ };
614
+ const s = t.match(/Successfully ran target .* for (\d+) projects?/i);
615
+ if (s)
616
+ return {
617
+ label: "tasks",
618
+ value: Number(s[1])
619
+ };
620
+ const n = ut(t);
621
+ return n.length > 0 ? {
622
+ label: "tasks",
623
+ value: n.length
624
+ } : null;
625
+ }, ct = (t) => {
626
+ const e = [
627
+ ...t.matchAll(/✖\s+\d+\s+problems?\s+\(\s*\d+\s+errors?,\s*(\d+)\s+warnings?\s*\)/gi)
628
+ ];
629
+ return e.length === 0 ? null : e.reduce((s, n) => {
630
+ const o = n[1];
631
+ return o ? s + Number(o) : s;
632
+ }, 0);
633
+ }, ut = (t) => {
634
+ const r = (t.match(/(^|\n)(?!\s*[|│])([^\n$]+?\s+[a-z0-9:_-]+\$\s[^\n]*)/gi) ?? []).map((s) => s.replace(/^\s+/, "").trim()).map((s) => s.replace(/\s+/g, " "));
635
+ return [...new Set(r)];
636
+ }, h = (t, e) => `${t.id} ${t.name} ${t.command}`.toLowerCase().includes(e), g = (t) => t.replace(/\x1b\[[0-9;]*m/g, "");
637
+ class dt {
638
+ options;
639
+ /**
640
+ * Creates a pretty reporter.
641
+ *
642
+ * @param options Reporter options.
643
+ */
644
+ constructor(e) {
645
+ this.options = e;
646
+ }
647
+ /**
648
+ * Handles pipeline start.
649
+ *
650
+ * @param steps Pipeline steps.
651
+ */
652
+ onPipelineStart(e) {
653
+ process.stdout.write(l(`ci-runner: executing ${e.length} steps
654
+ `, "blue"));
655
+ }
656
+ /**
657
+ * Handles step start.
658
+ *
659
+ * @param step Current step.
660
+ */
661
+ onStepStart(e) {
662
+ process.stdout.write(l(`-> ${e.name}
663
+ `, "blue"));
664
+ }
665
+ /**
666
+ * Handles step completion.
667
+ *
668
+ * @param result Step result.
669
+ */
670
+ onStepComplete(e) {
671
+ const r = `${e.durationMs}ms`;
672
+ if (e.status === "passed") {
673
+ const s = e.metrics && typeof e.metrics.value == "number" ? ` (${e.metrics.value} ${e.metrics.label})` : "";
674
+ process.stdout.write(l(`✓ ${e.name} ${r}${s}
675
+ `, "green")), this.options.verbose && this.printOutput(e);
676
+ return;
677
+ }
678
+ if (e.status === "skipped") {
679
+ process.stdout.write(
680
+ l(
681
+ `ℹ ${e.name} skipped (${e.reason ?? "no reason"}, ${r})
682
+ `,
683
+ "yellow"
684
+ )
685
+ );
686
+ const s = pt(e);
687
+ if (s) {
688
+ process.stdout.write(l(` note: missing script "${s}"
689
+ `, "yellow")), this.options.verbose && this.printOutput(e);
690
+ return;
691
+ }
692
+ this.printOutput(e);
693
+ return;
694
+ }
695
+ process.stdout.write(
696
+ l(
697
+ `✗ ${e.name} ${e.status} (${e.reason ?? "no reason"}, ${r})
698
+ `,
699
+ "red"
700
+ )
701
+ ), this.printOutput(e);
702
+ }
703
+ /**
704
+ * Handles pipeline completion.
705
+ *
706
+ * @param result Pipeline result.
707
+ */
708
+ onPipelineComplete(e) {
709
+ const r = e.summary;
710
+ if (process.stdout.write(`
711
+ `), process.stdout.write(
712
+ `Summary: total=${r.total} passed=${r.passed} skipped=${r.skipped} failed=${r.failed} timedOut=${r.timedOut} duration=${r.durationMs}ms
713
+ `
714
+ ), e.exitCode === 0) {
715
+ process.stdout.write(l(`Result: ✅ PASS
716
+ `, "green"));
717
+ return;
718
+ }
719
+ process.stdout.write(l(`Result: FAIL
720
+ `, "red"));
721
+ }
722
+ printOutput(e) {
723
+ const r = e.output.stdout.trim(), s = e.output.stderr.trim();
724
+ r && (process.stdout.write(l(` stdout:
725
+ `, "yellow")), process.stdout.write(C(r)), process.stdout.write(`
726
+ `)), s && (process.stdout.write(l(` stderr:
727
+ `, "yellow")), process.stdout.write(C(s)), process.stdout.write(`
728
+ `));
729
+ }
730
+ }
731
+ const C = (t) => t.split(`
732
+ `).map((e) => ` ${e}`).join(`
733
+ `), l = (t, e) => `${{
734
+ red: "\x1B[31m",
735
+ green: "\x1B[32m",
736
+ yellow: "\x1B[33m",
737
+ blue: "\x1B[34m"
738
+ }[e]}${t}\x1B[0m`, pt = (t) => {
739
+ const r = `${t.output.stdout}
740
+ ${t.output.stderr}`.match(/Missing script:\s*"?([a-z0-9:_-]+)"?/i);
741
+ return !r || !r[1] ? null : r[1];
742
+ }, lt = async (t) => {
743
+ const e = await K(t.cwd, t.configPath), r = e.config.output?.format ?? t.format, s = e.config.output?.verbose ?? t.verbose, n = async () => {
744
+ const o = U(e.config, t.cwd, t.failFast);
745
+ mt(o.excludedSteps, r);
746
+ const i = new H(it());
747
+ return await L({
748
+ ...o,
749
+ executor: B(),
750
+ parserResolver: i,
751
+ reporters: r === "pretty" ? [new dt({ verbose: s })] : []
752
+ }).run();
753
+ };
754
+ if (!t.watch) {
755
+ const o = await n();
756
+ return r === "json" && process.stdout.write(`${S(o)}
757
+ `), o.exitCode;
758
+ }
759
+ return await wt(t.cwd, e.configFilePath, r, n);
760
+ }, mt = (t, e) => {
761
+ if (!(e !== "pretty" || t.length === 0))
762
+ for (const r of t) {
763
+ if (r.reason === "disabled") {
764
+ process.stdout.write(`ℹ️ Skipping ${r.name} (enabled=false)
765
+ `);
766
+ continue;
767
+ }
768
+ if (r.reason === "env_mismatch" && r.requiredEnv) {
769
+ process.stdout.write(
770
+ `ℹ️ Skipping ${r.name} (set ${ft(r.requiredEnv)} to enable)
771
+ `
772
+ );
773
+ continue;
774
+ }
775
+ process.stdout.write(`ℹ️ Skipping ${r.name}
776
+ `);
777
+ }
778
+ }, ft = (t) => Object.entries(t).sort(([e], [r]) => e.localeCompare(r)).map(([e, r]) => `${e}=${r}`).join(" "), wt = async (t, e, r, s) => {
779
+ const n = await s();
780
+ r === "json" && process.stdout.write(`${S(n)}
781
+ `), process.stdout.write(`Watch mode enabled. Waiting for file changes...
782
+ `);
783
+ let o = n.exitCode, i = !1, p = !1, c = null;
784
+ const u = async () => {
785
+ if (i) {
786
+ p = !0;
787
+ return;
788
+ }
789
+ i = !0;
790
+ try {
791
+ do {
792
+ p = !1;
793
+ const a = await s();
794
+ o = a.exitCode, r === "json" && process.stdout.write(`${S(a)}
795
+ `);
796
+ } while (p);
797
+ } finally {
798
+ i = !1;
799
+ }
800
+ }, d = ht(t, e, () => {
801
+ c && clearTimeout(c), c = setTimeout(() => {
802
+ u();
803
+ }, 250);
804
+ });
805
+ return await new Promise((a) => {
806
+ const w = () => {
807
+ d.close(), c && clearTimeout(c), a();
808
+ };
809
+ process.on("SIGINT", w), process.on("SIGTERM", w);
810
+ }), o;
811
+ }, ht = (t, e, r) => {
812
+ const s = ["node_modules", ".git", "dist", "coverage", "out", "build", ".tmp"];
813
+ try {
814
+ const n = j(t, { recursive: !0 }, (o, i) => {
815
+ if (!i)
816
+ return;
817
+ const p = i.toString();
818
+ if (gt(p, s))
819
+ return;
820
+ const c = k(t, e), u = p === c ? `${p} (config)` : p;
821
+ process.stdout.write(`
822
+ Change detected: ${u}
823
+ `), r();
824
+ });
825
+ return {
826
+ close: () => n.close()
827
+ };
828
+ } catch {
829
+ return process.stdout.write(
830
+ `Watch mode is not supported recursively on this platform. Running once without watch.
831
+ `
832
+ ), {
833
+ close: () => {
834
+ }
835
+ };
836
+ }
837
+ }, gt = (t, e) => e.some((r) => t === r || t.startsWith(`${r}/`)), yt = async () => {
838
+ const t = D(process.argv.slice(2), process.cwd());
839
+ if (t.help) {
840
+ process.stdout.write(`${F()}
841
+ `), process.exitCode = 0;
842
+ return;
843
+ }
844
+ const e = await lt(t);
845
+ process.exitCode = e;
846
+ };
847
+ yt().catch((t) => {
848
+ const e = t instanceof Error ? `${t.name}: ${t.message}` : String(t);
849
+ process.stderr.write(`${e}
850
+ `), process.exitCode = 1;
851
+ });
852
+ //# sourceMappingURL=cli.js.map