@halecraft/verify 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -23
- package/bin/verify.mjs +20 -9
- package/dist/index.d.ts +56 -18
- package/dist/index.js +218 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,11 +10,12 @@ var ConfigError = class extends Error {
|
|
|
10
10
|
this.name = "ConfigError";
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
+
var EnvSchema = z.record(z.string(), z.string().nullable()).optional();
|
|
13
14
|
var VerificationCommandSchema = z.object({
|
|
14
15
|
cmd: z.string(),
|
|
15
16
|
args: z.array(z.string()),
|
|
16
17
|
cwd: z.string().optional(),
|
|
17
|
-
env:
|
|
18
|
+
env: EnvSchema,
|
|
18
19
|
timeout: z.number().positive().optional()
|
|
19
20
|
});
|
|
20
21
|
var VerificationNodeSchema = z.lazy(
|
|
@@ -30,7 +31,8 @@ var VerificationNodeSchema = z.lazy(
|
|
|
30
31
|
successLabel: z.string().optional(),
|
|
31
32
|
failureLabel: z.string().optional(),
|
|
32
33
|
reportingDependsOn: z.array(z.string()).optional(),
|
|
33
|
-
timeout: z.number().positive().optional()
|
|
34
|
+
timeout: z.number().positive().optional(),
|
|
35
|
+
env: EnvSchema
|
|
34
36
|
})
|
|
35
37
|
);
|
|
36
38
|
var VerifyOptionsSchema = z.object({
|
|
@@ -40,7 +42,8 @@ var VerifyOptionsSchema = z.object({
|
|
|
40
42
|
cwd: z.string().optional(),
|
|
41
43
|
noColor: z.boolean().optional(),
|
|
42
44
|
topLevelOnly: z.boolean().optional(),
|
|
43
|
-
noTty: z.boolean().optional()
|
|
45
|
+
noTty: z.boolean().optional(),
|
|
46
|
+
passthrough: z.array(z.string()).optional()
|
|
44
47
|
});
|
|
45
48
|
var PackageDiscoveryOptionsSchema = z.object({
|
|
46
49
|
patterns: z.array(z.string()).optional(),
|
|
@@ -50,7 +53,8 @@ var PackageDiscoveryOptionsSchema = z.object({
|
|
|
50
53
|
var VerifyConfigSchema = z.object({
|
|
51
54
|
tasks: z.array(VerificationNodeSchema),
|
|
52
55
|
packages: PackageDiscoveryOptionsSchema.optional(),
|
|
53
|
-
options: VerifyOptionsSchema.optional()
|
|
56
|
+
options: VerifyOptionsSchema.optional(),
|
|
57
|
+
env: EnvSchema
|
|
54
58
|
});
|
|
55
59
|
function validateConfig(value, configPath) {
|
|
56
60
|
const result = VerifyConfigSchema.safeParse(value);
|
|
@@ -118,7 +122,9 @@ function mergeOptions(configOptions, cliOptions) {
|
|
|
118
122
|
cwd: cliOptions?.cwd ?? configOptions?.cwd ?? process.cwd(),
|
|
119
123
|
noColor: cliOptions?.noColor ?? configOptions?.noColor ?? false,
|
|
120
124
|
topLevelOnly: cliOptions?.topLevelOnly ?? configOptions?.topLevelOnly ?? false,
|
|
121
|
-
noTty: cliOptions?.noTty ?? configOptions?.noTty ?? false
|
|
125
|
+
noTty: cliOptions?.noTty ?? configOptions?.noTty ?? false,
|
|
126
|
+
quiet: cliOptions?.quiet ?? configOptions?.quiet ?? false,
|
|
127
|
+
passthrough: cliOptions?.passthrough ?? configOptions?.passthrough
|
|
122
128
|
};
|
|
123
129
|
}
|
|
124
130
|
|
|
@@ -508,7 +514,7 @@ function extractOptimizedCommand(cwd, scriptContent) {
|
|
|
508
514
|
const match = scriptContent.match(tool.pattern);
|
|
509
515
|
if (match && binaryExists(cwd, tool.binary)) {
|
|
510
516
|
const args = tool.getArgs(match, scriptContent);
|
|
511
|
-
const command = args ?
|
|
517
|
+
const command = args ? `${tool.binary} ${args}` : tool.binary;
|
|
512
518
|
return { command, parser: tool.parser };
|
|
513
519
|
}
|
|
514
520
|
}
|
|
@@ -585,7 +591,7 @@ function detectTasks(cwd) {
|
|
|
585
591
|
const packageManager = detectPackageManager(cwd);
|
|
586
592
|
const tasks = detectFromPackageJson(cwd);
|
|
587
593
|
return tasks.map((task) => {
|
|
588
|
-
if (task.command.startsWith("
|
|
594
|
+
if (!task.command.startsWith("npm run ")) {
|
|
589
595
|
return task;
|
|
590
596
|
}
|
|
591
597
|
return {
|
|
@@ -621,12 +627,15 @@ function generateSkeleton(format) {
|
|
|
621
627
|
return `${importStatement}
|
|
622
628
|
|
|
623
629
|
export default defineConfig({
|
|
630
|
+
env: {
|
|
631
|
+
NO_COLOR: "1",
|
|
632
|
+
},
|
|
624
633
|
tasks: [
|
|
625
634
|
// Add your verification tasks here
|
|
626
635
|
// Example:
|
|
627
|
-
// { key: "format", run: "
|
|
628
|
-
// { key: "types", run: "
|
|
629
|
-
// { key: "test", run: "
|
|
636
|
+
// { key: "format", run: "biome check ." },
|
|
637
|
+
// { key: "types", run: "tsc --noEmit" },
|
|
638
|
+
// { key: "test", run: "vitest run" },
|
|
630
639
|
],
|
|
631
640
|
})
|
|
632
641
|
`;
|
|
@@ -641,6 +650,9 @@ function generateConfigContent(tasks, format) {
|
|
|
641
650
|
return `${importStatement}
|
|
642
651
|
|
|
643
652
|
export default defineConfig({
|
|
653
|
+
env: {
|
|
654
|
+
NO_COLOR: "1",
|
|
655
|
+
},
|
|
644
656
|
tasks: [
|
|
645
657
|
${taskLines.join(",\n")},
|
|
646
658
|
],
|
|
@@ -945,20 +957,36 @@ var vitestParser = {
|
|
|
945
957
|
id: "vitest",
|
|
946
958
|
parse(output, exitCode) {
|
|
947
959
|
const cleanOutput = stripAnsi(output);
|
|
948
|
-
const
|
|
960
|
+
const testsLineMatch = cleanOutput.match(/Tests\s+(.+?)\s*\((\d+)\)/m);
|
|
949
961
|
const durationMatch = cleanOutput.match(/Duration\s+([\d.]+(?:ms|s))\b/m);
|
|
950
|
-
if (!
|
|
962
|
+
if (!testsLineMatch) {
|
|
951
963
|
return null;
|
|
952
964
|
}
|
|
953
|
-
const
|
|
954
|
-
const total = Number.parseInt(
|
|
965
|
+
const statsStr = testsLineMatch[1];
|
|
966
|
+
const total = Number.parseInt(testsLineMatch[2], 10);
|
|
967
|
+
const passedMatch = statsStr.match(/(\d+)\s+passed/);
|
|
968
|
+
const failedMatch = statsStr.match(/(\d+)\s+failed/);
|
|
969
|
+
const skippedMatch = statsStr.match(/(\d+)\s+skipped/);
|
|
970
|
+
const passed = passedMatch ? Number.parseInt(passedMatch[1], 10) : 0;
|
|
971
|
+
const skipped = skippedMatch ? Number.parseInt(skippedMatch[1], 10) : 0;
|
|
955
972
|
const duration = durationMatch ? durationMatch[1] : void 0;
|
|
973
|
+
const failed = failedMatch ? Number.parseInt(failedMatch[1], 10) : total - passed - skipped;
|
|
974
|
+
const ran = passed + failed;
|
|
975
|
+
let summary;
|
|
976
|
+
if (ran === 0 && skipped > 0) {
|
|
977
|
+
summary = `${skipped} tests skipped (no matches)`;
|
|
978
|
+
} else if (skipped > 0) {
|
|
979
|
+
summary = exitCode === 0 ? `passed ${passed}/${ran} tests (${skipped} skipped)` : `passed ${passed}/${ran} tests (${skipped} skipped, some failed)`;
|
|
980
|
+
} else {
|
|
981
|
+
summary = exitCode === 0 ? `passed ${passed}/${total} tests` : `passed ${passed}/${total} tests (some failed)`;
|
|
982
|
+
}
|
|
956
983
|
return {
|
|
957
|
-
summary
|
|
984
|
+
summary,
|
|
958
985
|
metrics: {
|
|
959
986
|
passed,
|
|
960
987
|
total,
|
|
961
|
-
failed
|
|
988
|
+
failed,
|
|
989
|
+
skipped,
|
|
962
990
|
duration
|
|
963
991
|
}
|
|
964
992
|
};
|
|
@@ -1095,20 +1123,31 @@ var cursor = {
|
|
|
1095
1123
|
moveToStart: "\x1B[0G",
|
|
1096
1124
|
clearLine: "\x1B[2K"
|
|
1097
1125
|
};
|
|
1098
|
-
function
|
|
1126
|
+
function defaultTerminalContext() {
|
|
1127
|
+
return { isTTY: !!process.stdout.isTTY, env: process.env };
|
|
1128
|
+
}
|
|
1129
|
+
function shouldUseColors(options, ctx) {
|
|
1099
1130
|
if (options.noColor) return false;
|
|
1100
1131
|
if (options.format === "json") return false;
|
|
1101
|
-
if (!
|
|
1102
|
-
if ("NO_COLOR" in
|
|
1103
|
-
if (
|
|
1132
|
+
if (!ctx.isTTY) return false;
|
|
1133
|
+
if ("NO_COLOR" in ctx.env) return false;
|
|
1134
|
+
if (ctx.env.TERM === "dumb") return false;
|
|
1135
|
+
return true;
|
|
1136
|
+
}
|
|
1137
|
+
function shouldUseLiveDashboard(options, ctx) {
|
|
1138
|
+
if (options.noTty) return false;
|
|
1139
|
+
if (ctx.env.TURBO_IS_TUI === "true") return false;
|
|
1140
|
+
if (ctx.isTTY && ctx.env.TURBO_HASH != null) return false;
|
|
1141
|
+
if (!ctx.isTTY) return false;
|
|
1104
1142
|
return true;
|
|
1105
1143
|
}
|
|
1106
1144
|
var BaseReporter = class {
|
|
1107
1145
|
colorEnabled;
|
|
1108
1146
|
stream;
|
|
1109
1147
|
taskDepths = /* @__PURE__ */ new Map();
|
|
1110
|
-
constructor(options = {}) {
|
|
1111
|
-
|
|
1148
|
+
constructor(options = {}, ctx) {
|
|
1149
|
+
const termCtx = ctx ?? defaultTerminalContext();
|
|
1150
|
+
this.colorEnabled = shouldUseColors(options, termCtx);
|
|
1112
1151
|
this.stream = options.format === "json" ? process.stderr : process.stdout;
|
|
1113
1152
|
}
|
|
1114
1153
|
/**
|
|
@@ -1130,10 +1169,10 @@ var BaseReporter = class {
|
|
|
1130
1169
|
return this.colorEnabled ? this.c(ansi.red, "\u2717") : "FAIL";
|
|
1131
1170
|
}
|
|
1132
1171
|
/**
|
|
1133
|
-
* Get
|
|
1172
|
+
* Get blocked mark (⊘ or BLOCK)
|
|
1134
1173
|
*/
|
|
1135
|
-
|
|
1136
|
-
return this.colorEnabled ? this.c(ansi.yellow, "\u2298") : "
|
|
1174
|
+
blockedMark() {
|
|
1175
|
+
return this.colorEnabled ? this.c(ansi.yellow, "\u2298") : "BLOCK";
|
|
1137
1176
|
}
|
|
1138
1177
|
/**
|
|
1139
1178
|
* Get arrow symbol (→ or ->)
|
|
@@ -1161,6 +1200,22 @@ var BaseReporter = class {
|
|
|
1161
1200
|
this.taskDepths.set(path, depth);
|
|
1162
1201
|
});
|
|
1163
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Format a completed task result line in name-first format.
|
|
1205
|
+
* Shared by LiveDashboardReporter and SequentialReporter.
|
|
1206
|
+
*/
|
|
1207
|
+
formatResultLine(name, result) {
|
|
1208
|
+
const duration = this.c(ansi.dim, `${result.durationMs}ms`);
|
|
1209
|
+
if (result.blocked) {
|
|
1210
|
+
const reason = result.blockedBy ? `by ${result.blockedBy}` : "by dependency";
|
|
1211
|
+
return `${this.blockedMark()} ${this.c(ansi.bold, name)} blocked ${this.c(ansi.dim, `(${reason}, ${duration})`)}`;
|
|
1212
|
+
}
|
|
1213
|
+
const summary = this.extractSummary(result);
|
|
1214
|
+
if (result.ok) {
|
|
1215
|
+
return `${this.okMark()} ${this.c(ansi.bold, name)} verified ${this.c(ansi.dim, `(${summary}, ${duration})`)}`;
|
|
1216
|
+
}
|
|
1217
|
+
return `${this.failMark()} ${this.c(ansi.bold, name)} failed ${this.c(ansi.dim, `(${summary}, ${duration})`)}`;
|
|
1218
|
+
}
|
|
1164
1219
|
/**
|
|
1165
1220
|
* Extract summary from task result
|
|
1166
1221
|
*/
|
|
@@ -1196,7 +1251,7 @@ var BaseReporter = class {
|
|
|
1196
1251
|
for (const r of flatResults) {
|
|
1197
1252
|
if (r.children) continue;
|
|
1198
1253
|
if (logsMode === "failed" && r.ok) continue;
|
|
1199
|
-
if (r.
|
|
1254
|
+
if (r.blocked) continue;
|
|
1200
1255
|
const status = r.ok ? this.c(ansi.green, "OK") : this.c(ansi.red, "FAIL");
|
|
1201
1256
|
this.stream.write(
|
|
1202
1257
|
`
|
|
@@ -1221,8 +1276,8 @@ var LiveDashboardReporter = class extends BaseReporter {
|
|
|
1221
1276
|
taskOrder = [];
|
|
1222
1277
|
spinner;
|
|
1223
1278
|
lineCount = 0;
|
|
1224
|
-
constructor(options = {}) {
|
|
1225
|
-
super(options);
|
|
1279
|
+
constructor(options = {}, ctx) {
|
|
1280
|
+
super(options, ctx);
|
|
1226
1281
|
this.topLevelOnly = options.topLevelOnly ?? false;
|
|
1227
1282
|
this.spinner = new SpinnerManager();
|
|
1228
1283
|
const cleanup = () => {
|
|
@@ -1283,16 +1338,7 @@ var LiveDashboardReporter = class extends BaseReporter {
|
|
|
1283
1338
|
return `${indent}${this.arrow()} verifying ${this.c(ansi.bold, displayKey)} ${spinnerChar}`;
|
|
1284
1339
|
}
|
|
1285
1340
|
if (task.status === "completed" && task.result) {
|
|
1286
|
-
|
|
1287
|
-
if (task.result.suppressed) {
|
|
1288
|
-
const reason = task.result.suppressedBy ? `${task.result.suppressedBy} failed` : "dependency failed";
|
|
1289
|
-
return `${indent}${this.suppressedMark()} suppressed ${this.c(ansi.bold, displayKey)} ${this.c(ansi.dim, `(${reason}, ${duration})`)}`;
|
|
1290
|
-
}
|
|
1291
|
-
const summary = this.extractSummary(task.result);
|
|
1292
|
-
if (task.result.ok) {
|
|
1293
|
-
return `${indent}${this.okMark()} verified ${this.c(ansi.bold, displayKey)} ${this.c(ansi.dim, `(${summary}, ${duration})`)}`;
|
|
1294
|
-
}
|
|
1295
|
-
return `${indent}${this.failMark()} failed ${this.c(ansi.bold, displayKey)} ${this.c(ansi.dim, `(${summary}, ${duration})`)}`;
|
|
1341
|
+
return `${indent}${this.formatResultLine(displayKey, task.result)}`;
|
|
1296
1342
|
}
|
|
1297
1343
|
return "";
|
|
1298
1344
|
}
|
|
@@ -1342,8 +1388,8 @@ var LiveDashboardReporter = class extends BaseReporter {
|
|
|
1342
1388
|
};
|
|
1343
1389
|
var SequentialReporter = class extends BaseReporter {
|
|
1344
1390
|
topLevelOnly;
|
|
1345
|
-
constructor(options = {}) {
|
|
1346
|
-
super(options);
|
|
1391
|
+
constructor(options = {}, ctx) {
|
|
1392
|
+
super(options, ctx);
|
|
1347
1393
|
this.topLevelOnly = options.topLevelOnly ?? false;
|
|
1348
1394
|
}
|
|
1349
1395
|
onStart(tasks) {
|
|
@@ -1363,27 +1409,8 @@ var SequentialReporter = class extends BaseReporter {
|
|
|
1363
1409
|
}
|
|
1364
1410
|
onTaskComplete(result) {
|
|
1365
1411
|
if (!this.shouldDisplay(result.path)) return;
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
const reason = result.suppressedBy ? `${result.suppressedBy} failed` : "dependency failed";
|
|
1369
|
-
this.stream.write(
|
|
1370
|
-
`${this.suppressedMark()} suppressed ${this.c(ansi.bold, result.path)} ${this.c(ansi.dim, `(${reason}, ${duration})`)}
|
|
1371
|
-
`
|
|
1372
|
-
);
|
|
1373
|
-
return;
|
|
1374
|
-
}
|
|
1375
|
-
const summary = this.extractSummary(result);
|
|
1376
|
-
if (result.ok) {
|
|
1377
|
-
this.stream.write(
|
|
1378
|
-
`${this.okMark()} verified ${this.c(ansi.bold, result.path)} ${this.c(ansi.dim, `(${summary}, ${duration})`)}
|
|
1379
|
-
`
|
|
1380
|
-
);
|
|
1381
|
-
} else {
|
|
1382
|
-
this.stream.write(
|
|
1383
|
-
`${this.failMark()} failed ${this.c(ansi.bold, result.path)} ${this.c(ansi.dim, `(${summary}, ${duration})`)}
|
|
1384
|
-
`
|
|
1385
|
-
);
|
|
1386
|
-
}
|
|
1412
|
+
this.stream.write(`${this.formatResultLine(result.path, result)}
|
|
1413
|
+
`);
|
|
1387
1414
|
}
|
|
1388
1415
|
onFinish() {
|
|
1389
1416
|
}
|
|
@@ -1418,8 +1445,8 @@ var JSONReporter = class {
|
|
|
1418
1445
|
code: t.code,
|
|
1419
1446
|
durationMs: t.durationMs,
|
|
1420
1447
|
summaryLine: t.summaryLine,
|
|
1421
|
-
...t.
|
|
1422
|
-
...t.
|
|
1448
|
+
...t.blocked ? { blocked: t.blocked } : {},
|
|
1449
|
+
...t.blockedBy ? { blockedBy: t.blockedBy } : {},
|
|
1423
1450
|
...t.children ? { children: this.serializeTasks(t.children) } : {}
|
|
1424
1451
|
}));
|
|
1425
1452
|
}
|
|
@@ -1442,17 +1469,22 @@ var QuietReporter = class extends BaseReporter {
|
|
|
1442
1469
|
}
|
|
1443
1470
|
};
|
|
1444
1471
|
function createReporter(options) {
|
|
1472
|
+
const ctx = defaultTerminalContext();
|
|
1445
1473
|
if (options.format === "json") {
|
|
1446
1474
|
return new JSONReporter();
|
|
1447
1475
|
}
|
|
1448
|
-
if (
|
|
1449
|
-
return new
|
|
1476
|
+
if (options.quiet) {
|
|
1477
|
+
return new QuietReporter(options, ctx);
|
|
1450
1478
|
}
|
|
1451
|
-
|
|
1479
|
+
if (shouldUseLiveDashboard(options, ctx)) {
|
|
1480
|
+
return new LiveDashboardReporter(options, ctx);
|
|
1481
|
+
}
|
|
1482
|
+
return new SequentialReporter(options, ctx);
|
|
1452
1483
|
}
|
|
1453
1484
|
|
|
1454
1485
|
// src/runner.ts
|
|
1455
1486
|
import { spawn } from "child_process";
|
|
1487
|
+
import { dirname, join as join4, resolve as resolve3 } from "path";
|
|
1456
1488
|
import treeKill2 from "tree-kill";
|
|
1457
1489
|
|
|
1458
1490
|
// src/dependency-tracker.ts
|
|
@@ -1634,9 +1666,9 @@ var ReportingDependencyTracker = class {
|
|
|
1634
1666
|
if (this.results.has(pathOrKey)) {
|
|
1635
1667
|
return Promise.resolve();
|
|
1636
1668
|
}
|
|
1637
|
-
return new Promise((
|
|
1669
|
+
return new Promise((resolve4) => {
|
|
1638
1670
|
const waiters = this.waiters.get(pathOrKey) ?? [];
|
|
1639
|
-
waiters.push(
|
|
1671
|
+
waiters.push(resolve4);
|
|
1640
1672
|
this.waiters.set(pathOrKey, waiters);
|
|
1641
1673
|
});
|
|
1642
1674
|
}
|
|
@@ -1704,17 +1736,78 @@ var ReportingDependencyTracker = class {
|
|
|
1704
1736
|
};
|
|
1705
1737
|
|
|
1706
1738
|
// src/runner.ts
|
|
1707
|
-
function
|
|
1739
|
+
function mergeEnv(base, overlay) {
|
|
1740
|
+
if (!overlay) return { ...base };
|
|
1741
|
+
const result = { ...base };
|
|
1742
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
1743
|
+
if (value === null) {
|
|
1744
|
+
result[key] = null;
|
|
1745
|
+
} else {
|
|
1746
|
+
result[key] = value;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return result;
|
|
1750
|
+
}
|
|
1751
|
+
function buildFinalEnv(processEnv, path, overlay) {
|
|
1752
|
+
const result = {};
|
|
1753
|
+
for (const [key, value] of Object.entries(processEnv)) {
|
|
1754
|
+
if (value !== void 0) {
|
|
1755
|
+
result[key] = value;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
result.PATH = path;
|
|
1759
|
+
if (overlay) {
|
|
1760
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
1761
|
+
if (value === null) {
|
|
1762
|
+
delete result[key];
|
|
1763
|
+
} else {
|
|
1764
|
+
result[key] = value;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return result;
|
|
1769
|
+
}
|
|
1770
|
+
function shellEscape(arg) {
|
|
1771
|
+
if (/[\s"'`$\\!&|;<>(){}[\]*?#~]/.test(arg)) {
|
|
1772
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
1773
|
+
}
|
|
1774
|
+
return arg;
|
|
1775
|
+
}
|
|
1776
|
+
function buildNodeModulesPath(cwd) {
|
|
1777
|
+
const pathSeparator = process.platform === "win32" ? ";" : ":";
|
|
1778
|
+
const existingPath = process.env.PATH ?? "";
|
|
1779
|
+
const binPaths = [];
|
|
1780
|
+
let current = resolve3(cwd);
|
|
1781
|
+
while (true) {
|
|
1782
|
+
binPaths.push(join4(current, "node_modules", ".bin"));
|
|
1783
|
+
const parent = dirname(current);
|
|
1784
|
+
if (parent === current) break;
|
|
1785
|
+
current = parent;
|
|
1786
|
+
}
|
|
1787
|
+
return [...binPaths, existingPath].join(pathSeparator);
|
|
1788
|
+
}
|
|
1789
|
+
function normalizeCommand(run, nodeTimeout, passthrough, inheritedEnv) {
|
|
1708
1790
|
if (typeof run === "string") {
|
|
1791
|
+
let cmd = run;
|
|
1792
|
+
if (passthrough && passthrough.length > 0) {
|
|
1793
|
+
const escapedArgs = passthrough.map(shellEscape).join(" ");
|
|
1794
|
+
cmd = `${run} ${escapedArgs}`;
|
|
1795
|
+
}
|
|
1709
1796
|
return {
|
|
1710
|
-
cmd
|
|
1797
|
+
cmd,
|
|
1711
1798
|
args: [],
|
|
1712
1799
|
shell: true,
|
|
1713
|
-
timeout: nodeTimeout
|
|
1800
|
+
timeout: nodeTimeout,
|
|
1801
|
+
env: inheritedEnv
|
|
1714
1802
|
};
|
|
1715
1803
|
}
|
|
1804
|
+
const args = passthrough ? [...run.args, ...passthrough] : run.args;
|
|
1805
|
+
const env = mergeEnv(inheritedEnv ?? {}, run.env);
|
|
1716
1806
|
return {
|
|
1717
|
-
|
|
1807
|
+
cmd: run.cmd,
|
|
1808
|
+
args,
|
|
1809
|
+
cwd: run.cwd,
|
|
1810
|
+
env: Object.keys(env).length > 0 ? env : void 0,
|
|
1718
1811
|
shell: false,
|
|
1719
1812
|
// Command-level timeout takes precedence over node-level timeout
|
|
1720
1813
|
timeout: run.timeout ?? nodeTimeout
|
|
@@ -1722,12 +1815,18 @@ function normalizeCommand(run, nodeTimeout) {
|
|
|
1722
1815
|
}
|
|
1723
1816
|
async function executeCommand(command, cwd, tracker, path) {
|
|
1724
1817
|
const start = Date.now();
|
|
1725
|
-
return new Promise((
|
|
1818
|
+
return new Promise((resolve4) => {
|
|
1726
1819
|
const useShell = command.shell || process.platform === "win32";
|
|
1820
|
+
const effectiveCwd = command.cwd ?? cwd;
|
|
1821
|
+
const finalEnv = buildFinalEnv(
|
|
1822
|
+
process.env,
|
|
1823
|
+
buildNodeModulesPath(effectiveCwd),
|
|
1824
|
+
command.env
|
|
1825
|
+
);
|
|
1727
1826
|
const proc = spawn(command.cmd, command.args, {
|
|
1728
1827
|
shell: useShell,
|
|
1729
|
-
cwd:
|
|
1730
|
-
env:
|
|
1828
|
+
cwd: effectiveCwd,
|
|
1829
|
+
env: finalEnv
|
|
1731
1830
|
});
|
|
1732
1831
|
if (tracker && path) {
|
|
1733
1832
|
tracker.registerProcess(path, proc);
|
|
@@ -1760,7 +1859,7 @@ async function executeCommand(command, cwd, tracker, path) {
|
|
|
1760
1859
|
}
|
|
1761
1860
|
const durationMs = Date.now() - start;
|
|
1762
1861
|
const killed = signal === "SIGTERM" || code === 143 || (tracker?.wasKilled(path ?? "") ?? false);
|
|
1763
|
-
|
|
1862
|
+
resolve4({
|
|
1764
1863
|
code: code ?? 1,
|
|
1765
1864
|
output,
|
|
1766
1865
|
durationMs,
|
|
@@ -1777,7 +1876,7 @@ async function executeCommand(command, cwd, tracker, path) {
|
|
|
1777
1876
|
tracker.unregisterProcess(path);
|
|
1778
1877
|
}
|
|
1779
1878
|
const durationMs = Date.now() - start;
|
|
1780
|
-
|
|
1879
|
+
resolve4({
|
|
1781
1880
|
code: 1,
|
|
1782
1881
|
output: `Failed to execute command: ${err.message}`,
|
|
1783
1882
|
durationMs,
|
|
@@ -1812,11 +1911,13 @@ var VerificationRunner = class {
|
|
|
1812
1911
|
options;
|
|
1813
1912
|
callbacks;
|
|
1814
1913
|
dependencyTracker;
|
|
1815
|
-
|
|
1914
|
+
configEnv;
|
|
1915
|
+
constructor(options = {}, registry = defaultRegistry, callbacks = {}, configEnv = {}) {
|
|
1816
1916
|
this.options = options;
|
|
1817
1917
|
this.registry = registry;
|
|
1818
1918
|
this.callbacks = callbacks;
|
|
1819
1919
|
this.dependencyTracker = new ReportingDependencyTracker();
|
|
1920
|
+
this.configEnv = configEnv;
|
|
1820
1921
|
}
|
|
1821
1922
|
/**
|
|
1822
1923
|
* Run all verification tasks
|
|
@@ -1825,7 +1926,8 @@ var VerificationRunner = class {
|
|
|
1825
1926
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1826
1927
|
const wallStart = Date.now();
|
|
1827
1928
|
this.dependencyTracker.initialize(tasks);
|
|
1828
|
-
const
|
|
1929
|
+
const baseEnv = mergeEnv({}, this.configEnv);
|
|
1930
|
+
const results = await this.runNodes(tasks, "", "parallel", baseEnv);
|
|
1829
1931
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1830
1932
|
const durationMs = Date.now() - wallStart;
|
|
1831
1933
|
const allOk = results.every((r) => r.ok);
|
|
@@ -1840,7 +1942,7 @@ var VerificationRunner = class {
|
|
|
1840
1942
|
/**
|
|
1841
1943
|
* Run a list of nodes with the appropriate strategy
|
|
1842
1944
|
*/
|
|
1843
|
-
async runNodes(nodes, parentPath, strategy = "parallel") {
|
|
1945
|
+
async runNodes(nodes, parentPath, strategy = "parallel", inheritedEnv = {}) {
|
|
1844
1946
|
const filteredNodes = nodes.filter(
|
|
1845
1947
|
(node) => hasMatchingDescendant(node, parentPath, this.options.filter)
|
|
1846
1948
|
);
|
|
@@ -1850,19 +1952,21 @@ var VerificationRunner = class {
|
|
|
1850
1952
|
switch (strategy) {
|
|
1851
1953
|
case "parallel":
|
|
1852
1954
|
return Promise.all(
|
|
1853
|
-
filteredNodes.map(
|
|
1955
|
+
filteredNodes.map(
|
|
1956
|
+
(node) => this.runNode(node, parentPath, inheritedEnv)
|
|
1957
|
+
)
|
|
1854
1958
|
);
|
|
1855
1959
|
case "sequential": {
|
|
1856
1960
|
const results = [];
|
|
1857
1961
|
for (const node of filteredNodes) {
|
|
1858
|
-
results.push(await this.runNode(node, parentPath));
|
|
1962
|
+
results.push(await this.runNode(node, parentPath, inheritedEnv));
|
|
1859
1963
|
}
|
|
1860
1964
|
return results;
|
|
1861
1965
|
}
|
|
1862
1966
|
case "fail-fast": {
|
|
1863
1967
|
const results = [];
|
|
1864
1968
|
for (const node of filteredNodes) {
|
|
1865
|
-
const result = await this.runNode(node, parentPath);
|
|
1969
|
+
const result = await this.runNode(node, parentPath, inheritedEnv);
|
|
1866
1970
|
results.push(result);
|
|
1867
1971
|
if (!result.ok) {
|
|
1868
1972
|
break;
|
|
@@ -1875,8 +1979,9 @@ var VerificationRunner = class {
|
|
|
1875
1979
|
/**
|
|
1876
1980
|
* Run a single node (leaf or group)
|
|
1877
1981
|
*/
|
|
1878
|
-
async runNode(node, parentPath) {
|
|
1982
|
+
async runNode(node, parentPath, inheritedEnv = {}) {
|
|
1879
1983
|
const path = buildTaskPath(parentPath, node.key);
|
|
1984
|
+
const nodeEnv = mergeEnv(inheritedEnv, node.env);
|
|
1880
1985
|
this.dependencyTracker.markActive(path);
|
|
1881
1986
|
this.callbacks.onTaskStart?.(path, node.key);
|
|
1882
1987
|
if (node.children && node.children.length > 0) {
|
|
@@ -1884,12 +1989,13 @@ var VerificationRunner = class {
|
|
|
1884
1989
|
const childResults = await this.runNodes(
|
|
1885
1990
|
node.children,
|
|
1886
1991
|
path,
|
|
1887
|
-
node.strategy ?? "parallel"
|
|
1992
|
+
node.strategy ?? "parallel",
|
|
1993
|
+
nodeEnv
|
|
1888
1994
|
);
|
|
1889
1995
|
const durationMs2 = Date.now() - start;
|
|
1890
|
-
const allOk = childResults.every((r) => r.ok || r.
|
|
1891
|
-
const
|
|
1892
|
-
const
|
|
1996
|
+
const allOk = childResults.every((r) => r.ok || r.blocked);
|
|
1997
|
+
const allBlocked = childResults.length > 0 && childResults.every((r) => r.blocked);
|
|
1998
|
+
const anyBlocked = childResults.some((r) => r.blocked);
|
|
1893
1999
|
const result2 = {
|
|
1894
2000
|
key: node.key,
|
|
1895
2001
|
path,
|
|
@@ -1900,10 +2006,10 @@ var VerificationRunner = class {
|
|
|
1900
2006
|
summaryLine: allOk ? node.successLabel ?? `${node.key}: all passed` : node.failureLabel ?? `${node.key}: some failed`,
|
|
1901
2007
|
children: childResults
|
|
1902
2008
|
};
|
|
1903
|
-
if (
|
|
1904
|
-
result2.
|
|
1905
|
-
result2.
|
|
1906
|
-
} else if (
|
|
2009
|
+
if (allBlocked) {
|
|
2010
|
+
result2.blocked = true;
|
|
2011
|
+
result2.blockedBy = childResults[0].blockedBy;
|
|
2012
|
+
} else if (anyBlocked && !allOk) {
|
|
1907
2013
|
}
|
|
1908
2014
|
this.dependencyTracker.recordResult(result2);
|
|
1909
2015
|
this.callbacks.onTaskComplete?.(result2);
|
|
@@ -1923,7 +2029,13 @@ var VerificationRunner = class {
|
|
|
1923
2029
|
this.callbacks.onTaskComplete?.(result2);
|
|
1924
2030
|
return result2;
|
|
1925
2031
|
}
|
|
1926
|
-
const
|
|
2032
|
+
const passthrough = this.options.passthrough;
|
|
2033
|
+
const command = normalizeCommand(
|
|
2034
|
+
node.run,
|
|
2035
|
+
node.timeout,
|
|
2036
|
+
passthrough,
|
|
2037
|
+
nodeEnv
|
|
2038
|
+
);
|
|
1927
2039
|
const cwd = this.options.cwd ?? process.cwd();
|
|
1928
2040
|
const { code, output, durationMs, killed, timedOut } = await executeCommand(
|
|
1929
2041
|
command,
|
|
@@ -1958,8 +2070,8 @@ var VerificationRunner = class {
|
|
|
1958
2070
|
durationMs,
|
|
1959
2071
|
output,
|
|
1960
2072
|
summaryLine: `${node.key}: terminated`,
|
|
1961
|
-
|
|
1962
|
-
|
|
2073
|
+
blocked: true,
|
|
2074
|
+
blockedBy: failedDep ?? "unknown"
|
|
1963
2075
|
};
|
|
1964
2076
|
this.dependencyTracker.recordResult(result2);
|
|
1965
2077
|
this.callbacks.onTaskComplete?.(result2);
|
|
@@ -1995,8 +2107,8 @@ var VerificationRunner = class {
|
|
|
1995
2107
|
if (failedDep) {
|
|
1996
2108
|
result = {
|
|
1997
2109
|
...result,
|
|
1998
|
-
|
|
1999
|
-
|
|
2110
|
+
blocked: true,
|
|
2111
|
+
blockedBy: failedDep
|
|
2000
2112
|
};
|
|
2001
2113
|
}
|
|
2002
2114
|
}
|
|
@@ -2021,10 +2133,15 @@ async function verify(config, cliOptions) {
|
|
|
2021
2133
|
}
|
|
2022
2134
|
const reporter = createReporter(options);
|
|
2023
2135
|
reporter.onStart?.(config.tasks);
|
|
2024
|
-
const runner = new VerificationRunner(
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2136
|
+
const runner = new VerificationRunner(
|
|
2137
|
+
options,
|
|
2138
|
+
void 0,
|
|
2139
|
+
{
|
|
2140
|
+
onTaskStart: (path, key) => reporter.onTaskStart(path, key),
|
|
2141
|
+
onTaskComplete: (result2) => reporter.onTaskComplete(result2)
|
|
2142
|
+
},
|
|
2143
|
+
config.env
|
|
2144
|
+
);
|
|
2028
2145
|
const result = await runner.run(config.tasks);
|
|
2029
2146
|
reporter.onFinish?.();
|
|
2030
2147
|
reporter.outputLogs(result.tasks, options.logs ?? "failed");
|