@harness-engineering/core 0.9.2 → 0.10.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/dist/index.js +140 -85
- package/dist/index.mjs +140 -85
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5239,6 +5239,15 @@ var FAILURES_FILE = "failures.md";
|
|
|
5239
5239
|
var HANDOFF_FILE = "handoff.json";
|
|
5240
5240
|
var GATE_CONFIG_FILE = "gate.json";
|
|
5241
5241
|
var INDEX_FILE2 = "index.json";
|
|
5242
|
+
var MAX_CACHE_ENTRIES = 8;
|
|
5243
|
+
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
5244
|
+
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
5245
|
+
function evictIfNeeded(map) {
|
|
5246
|
+
if (map.size > MAX_CACHE_ENTRIES) {
|
|
5247
|
+
const oldest = map.keys().next().value;
|
|
5248
|
+
if (oldest !== void 0) map.delete(oldest);
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5242
5251
|
async function getStateDir(projectPath, stream) {
|
|
5243
5252
|
const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
|
|
5244
5253
|
const hasStreams = fs4.existsSync(streamsIndexPath);
|
|
@@ -5318,6 +5327,7 @@ ${entry}`);
|
|
|
5318
5327
|
} else {
|
|
5319
5328
|
fs4.appendFileSync(learningsPath, entry);
|
|
5320
5329
|
}
|
|
5330
|
+
learningsCacheMap.delete(learningsPath);
|
|
5321
5331
|
return (0, import_types.Ok)(void 0);
|
|
5322
5332
|
} catch (error) {
|
|
5323
5333
|
return (0, import_types.Err)(
|
|
@@ -5336,25 +5346,35 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
5336
5346
|
if (!fs4.existsSync(learningsPath)) {
|
|
5337
5347
|
return (0, import_types.Ok)([]);
|
|
5338
5348
|
}
|
|
5339
|
-
const
|
|
5340
|
-
const
|
|
5341
|
-
const
|
|
5342
|
-
let
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
const
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5349
|
+
const stats = fs4.statSync(learningsPath);
|
|
5350
|
+
const cacheKey = learningsPath;
|
|
5351
|
+
const cached = learningsCacheMap.get(cacheKey);
|
|
5352
|
+
let entries;
|
|
5353
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5354
|
+
entries = cached.entries;
|
|
5355
|
+
} else {
|
|
5356
|
+
const content = fs4.readFileSync(learningsPath, "utf-8");
|
|
5357
|
+
const lines = content.split("\n");
|
|
5358
|
+
entries = [];
|
|
5359
|
+
let currentBlock = [];
|
|
5360
|
+
for (const line of lines) {
|
|
5361
|
+
if (line.startsWith("# ")) continue;
|
|
5362
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
5363
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
5364
|
+
if (isDatedBullet || isHeading) {
|
|
5365
|
+
if (currentBlock.length > 0) {
|
|
5366
|
+
entries.push(currentBlock.join("\n"));
|
|
5367
|
+
}
|
|
5368
|
+
currentBlock = [line];
|
|
5369
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5370
|
+
currentBlock.push(line);
|
|
5350
5371
|
}
|
|
5351
|
-
currentBlock = [line];
|
|
5352
|
-
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5353
|
-
currentBlock.push(line);
|
|
5354
5372
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5373
|
+
if (currentBlock.length > 0) {
|
|
5374
|
+
entries.push(currentBlock.join("\n"));
|
|
5375
|
+
}
|
|
5376
|
+
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5377
|
+
evictIfNeeded(learningsCacheMap);
|
|
5358
5378
|
}
|
|
5359
5379
|
if (!skillName) {
|
|
5360
5380
|
return (0, import_types.Ok)(entries);
|
|
@@ -5387,6 +5407,7 @@ ${entry}`);
|
|
|
5387
5407
|
} else {
|
|
5388
5408
|
fs4.appendFileSync(failuresPath, entry);
|
|
5389
5409
|
}
|
|
5410
|
+
failuresCacheMap.delete(failuresPath);
|
|
5390
5411
|
return (0, import_types.Ok)(void 0);
|
|
5391
5412
|
} catch (error) {
|
|
5392
5413
|
return (0, import_types.Err)(
|
|
@@ -5405,6 +5426,12 @@ async function loadFailures(projectPath, stream) {
|
|
|
5405
5426
|
if (!fs4.existsSync(failuresPath)) {
|
|
5406
5427
|
return (0, import_types.Ok)([]);
|
|
5407
5428
|
}
|
|
5429
|
+
const stats = fs4.statSync(failuresPath);
|
|
5430
|
+
const cacheKey = failuresPath;
|
|
5431
|
+
const cached = failuresCacheMap.get(cacheKey);
|
|
5432
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5433
|
+
return (0, import_types.Ok)(cached.entries);
|
|
5434
|
+
}
|
|
5408
5435
|
const content = fs4.readFileSync(failuresPath, "utf-8");
|
|
5409
5436
|
const entries = [];
|
|
5410
5437
|
for (const line of content.split("\n")) {
|
|
@@ -5418,6 +5445,8 @@ async function loadFailures(projectPath, stream) {
|
|
|
5418
5445
|
});
|
|
5419
5446
|
}
|
|
5420
5447
|
}
|
|
5448
|
+
failuresCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5449
|
+
evictIfNeeded(failuresCacheMap);
|
|
5421
5450
|
return (0, import_types.Ok)(entries);
|
|
5422
5451
|
} catch (error) {
|
|
5423
5452
|
return (0, import_types.Err)(
|
|
@@ -5446,6 +5475,7 @@ async function archiveFailures(projectPath, stream) {
|
|
|
5446
5475
|
counter++;
|
|
5447
5476
|
}
|
|
5448
5477
|
fs4.renameSync(failuresPath, path3.join(archiveDir, archiveName));
|
|
5478
|
+
failuresCacheMap.delete(failuresPath);
|
|
5449
5479
|
return (0, import_types.Ok)(void 0);
|
|
5450
5480
|
} catch (error) {
|
|
5451
5481
|
return (0, import_types.Err)(
|
|
@@ -6516,14 +6546,22 @@ async function runCIChecks(input) {
|
|
|
6516
6546
|
const { projectRoot, config, skip = [], failOn = "error" } = input;
|
|
6517
6547
|
try {
|
|
6518
6548
|
const checks = [];
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
checks.push(result);
|
|
6525
|
-
}
|
|
6549
|
+
const skippedSet = new Set(skip);
|
|
6550
|
+
if (skippedSet.has("validate")) {
|
|
6551
|
+
checks.push({ name: "validate", status: "skip", issues: [], durationMs: 0 });
|
|
6552
|
+
} else {
|
|
6553
|
+
checks.push(await runSingleCheck("validate", projectRoot, config));
|
|
6526
6554
|
}
|
|
6555
|
+
const remainingChecks = ALL_CHECKS.slice(1);
|
|
6556
|
+
const phase2Results = await Promise.all(
|
|
6557
|
+
remainingChecks.map(async (name) => {
|
|
6558
|
+
if (skippedSet.has(name)) {
|
|
6559
|
+
return { name, status: "skip", issues: [], durationMs: 0 };
|
|
6560
|
+
}
|
|
6561
|
+
return runSingleCheck(name, projectRoot, config);
|
|
6562
|
+
})
|
|
6563
|
+
);
|
|
6564
|
+
checks.push(...phase2Results);
|
|
6527
6565
|
const summary = buildSummary(checks);
|
|
6528
6566
|
const exitCode = determineExitCode(summary, failOn);
|
|
6529
6567
|
const report = {
|
|
@@ -6648,76 +6686,93 @@ async function runMechanicalChecks(options) {
|
|
|
6648
6686
|
});
|
|
6649
6687
|
}
|
|
6650
6688
|
}
|
|
6689
|
+
const parallelChecks = [];
|
|
6651
6690
|
if (!skip.includes("check-docs")) {
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6691
|
+
parallelChecks.push(
|
|
6692
|
+
(async () => {
|
|
6693
|
+
const localFindings = [];
|
|
6694
|
+
try {
|
|
6695
|
+
const docsDir = path6.join(projectRoot, config.docsDir ?? "docs");
|
|
6696
|
+
const result = await checkDocCoverage("project", { docsDir });
|
|
6697
|
+
if (!result.ok) {
|
|
6698
|
+
statuses["check-docs"] = "warn";
|
|
6699
|
+
localFindings.push({
|
|
6700
|
+
tool: "check-docs",
|
|
6701
|
+
file: docsDir,
|
|
6702
|
+
message: result.error.message,
|
|
6703
|
+
severity: "warning"
|
|
6704
|
+
});
|
|
6705
|
+
} else if (result.value.gaps && result.value.gaps.length > 0) {
|
|
6706
|
+
statuses["check-docs"] = "warn";
|
|
6707
|
+
for (const gap of result.value.gaps) {
|
|
6708
|
+
localFindings.push({
|
|
6709
|
+
tool: "check-docs",
|
|
6710
|
+
file: gap.file,
|
|
6711
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6712
|
+
severity: "warning"
|
|
6713
|
+
});
|
|
6714
|
+
}
|
|
6715
|
+
} else {
|
|
6716
|
+
statuses["check-docs"] = "pass";
|
|
6717
|
+
}
|
|
6718
|
+
} catch (err) {
|
|
6719
|
+
statuses["check-docs"] = "warn";
|
|
6720
|
+
localFindings.push({
|
|
6667
6721
|
tool: "check-docs",
|
|
6668
|
-
file:
|
|
6669
|
-
message:
|
|
6722
|
+
file: path6.join(projectRoot, "docs"),
|
|
6723
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6670
6724
|
severity: "warning"
|
|
6671
6725
|
});
|
|
6672
6726
|
}
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
} catch (err) {
|
|
6677
|
-
statuses["check-docs"] = "warn";
|
|
6678
|
-
findings.push({
|
|
6679
|
-
tool: "check-docs",
|
|
6680
|
-
file: path6.join(projectRoot, "docs"),
|
|
6681
|
-
message: err instanceof Error ? err.message : String(err),
|
|
6682
|
-
severity: "warning"
|
|
6683
|
-
});
|
|
6684
|
-
}
|
|
6727
|
+
return localFindings;
|
|
6728
|
+
})()
|
|
6729
|
+
);
|
|
6685
6730
|
}
|
|
6686
6731
|
if (!skip.includes("security-scan")) {
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
findings.
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6732
|
+
parallelChecks.push(
|
|
6733
|
+
(async () => {
|
|
6734
|
+
const localFindings = [];
|
|
6735
|
+
try {
|
|
6736
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
6737
|
+
if (!securityConfig.enabled) {
|
|
6738
|
+
statuses["security-scan"] = "skip";
|
|
6739
|
+
} else {
|
|
6740
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
6741
|
+
scanner.configureForProject(projectRoot);
|
|
6742
|
+
const filesToScan = changedFiles ?? [];
|
|
6743
|
+
const scanResult = await scanner.scanFiles(filesToScan);
|
|
6744
|
+
if (scanResult.findings.length > 0) {
|
|
6745
|
+
statuses["security-scan"] = "warn";
|
|
6746
|
+
for (const f of scanResult.findings) {
|
|
6747
|
+
localFindings.push({
|
|
6748
|
+
tool: "security-scan",
|
|
6749
|
+
file: f.file,
|
|
6750
|
+
line: f.line,
|
|
6751
|
+
ruleId: f.ruleId,
|
|
6752
|
+
message: f.message,
|
|
6753
|
+
severity: f.severity === "info" ? "warning" : f.severity
|
|
6754
|
+
});
|
|
6755
|
+
}
|
|
6756
|
+
} else {
|
|
6757
|
+
statuses["security-scan"] = "pass";
|
|
6758
|
+
}
|
|
6707
6759
|
}
|
|
6708
|
-
}
|
|
6709
|
-
statuses["security-scan"] = "
|
|
6760
|
+
} catch (err) {
|
|
6761
|
+
statuses["security-scan"] = "warn";
|
|
6762
|
+
localFindings.push({
|
|
6763
|
+
tool: "security-scan",
|
|
6764
|
+
file: projectRoot,
|
|
6765
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6766
|
+
severity: "warning"
|
|
6767
|
+
});
|
|
6710
6768
|
}
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
severity: "warning"
|
|
6719
|
-
});
|
|
6720
|
-
}
|
|
6769
|
+
return localFindings;
|
|
6770
|
+
})()
|
|
6771
|
+
);
|
|
6772
|
+
}
|
|
6773
|
+
const parallelResults = await Promise.all(parallelChecks);
|
|
6774
|
+
for (const result of parallelResults) {
|
|
6775
|
+
findings.push(...result);
|
|
6721
6776
|
}
|
|
6722
6777
|
const hasErrors = findings.some((f) => f.severity === "error");
|
|
6723
6778
|
const stopPipeline = statuses.validate === "fail" || statuses["check-deps"] === "fail";
|
package/dist/index.mjs
CHANGED
|
@@ -5044,6 +5044,15 @@ var FAILURES_FILE = "failures.md";
|
|
|
5044
5044
|
var HANDOFF_FILE = "handoff.json";
|
|
5045
5045
|
var GATE_CONFIG_FILE = "gate.json";
|
|
5046
5046
|
var INDEX_FILE2 = "index.json";
|
|
5047
|
+
var MAX_CACHE_ENTRIES = 8;
|
|
5048
|
+
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
5049
|
+
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
5050
|
+
function evictIfNeeded(map) {
|
|
5051
|
+
if (map.size > MAX_CACHE_ENTRIES) {
|
|
5052
|
+
const oldest = map.keys().next().value;
|
|
5053
|
+
if (oldest !== void 0) map.delete(oldest);
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5047
5056
|
async function getStateDir(projectPath, stream) {
|
|
5048
5057
|
const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
|
|
5049
5058
|
const hasStreams = fs4.existsSync(streamsIndexPath);
|
|
@@ -5123,6 +5132,7 @@ ${entry}`);
|
|
|
5123
5132
|
} else {
|
|
5124
5133
|
fs4.appendFileSync(learningsPath, entry);
|
|
5125
5134
|
}
|
|
5135
|
+
learningsCacheMap.delete(learningsPath);
|
|
5126
5136
|
return Ok(void 0);
|
|
5127
5137
|
} catch (error) {
|
|
5128
5138
|
return Err(
|
|
@@ -5141,25 +5151,35 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
5141
5151
|
if (!fs4.existsSync(learningsPath)) {
|
|
5142
5152
|
return Ok([]);
|
|
5143
5153
|
}
|
|
5144
|
-
const
|
|
5145
|
-
const
|
|
5146
|
-
const
|
|
5147
|
-
let
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5154
|
+
const stats = fs4.statSync(learningsPath);
|
|
5155
|
+
const cacheKey = learningsPath;
|
|
5156
|
+
const cached = learningsCacheMap.get(cacheKey);
|
|
5157
|
+
let entries;
|
|
5158
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5159
|
+
entries = cached.entries;
|
|
5160
|
+
} else {
|
|
5161
|
+
const content = fs4.readFileSync(learningsPath, "utf-8");
|
|
5162
|
+
const lines = content.split("\n");
|
|
5163
|
+
entries = [];
|
|
5164
|
+
let currentBlock = [];
|
|
5165
|
+
for (const line of lines) {
|
|
5166
|
+
if (line.startsWith("# ")) continue;
|
|
5167
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
5168
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
5169
|
+
if (isDatedBullet || isHeading) {
|
|
5170
|
+
if (currentBlock.length > 0) {
|
|
5171
|
+
entries.push(currentBlock.join("\n"));
|
|
5172
|
+
}
|
|
5173
|
+
currentBlock = [line];
|
|
5174
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5175
|
+
currentBlock.push(line);
|
|
5155
5176
|
}
|
|
5156
|
-
currentBlock = [line];
|
|
5157
|
-
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5158
|
-
currentBlock.push(line);
|
|
5159
5177
|
}
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5178
|
+
if (currentBlock.length > 0) {
|
|
5179
|
+
entries.push(currentBlock.join("\n"));
|
|
5180
|
+
}
|
|
5181
|
+
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5182
|
+
evictIfNeeded(learningsCacheMap);
|
|
5163
5183
|
}
|
|
5164
5184
|
if (!skillName) {
|
|
5165
5185
|
return Ok(entries);
|
|
@@ -5192,6 +5212,7 @@ ${entry}`);
|
|
|
5192
5212
|
} else {
|
|
5193
5213
|
fs4.appendFileSync(failuresPath, entry);
|
|
5194
5214
|
}
|
|
5215
|
+
failuresCacheMap.delete(failuresPath);
|
|
5195
5216
|
return Ok(void 0);
|
|
5196
5217
|
} catch (error) {
|
|
5197
5218
|
return Err(
|
|
@@ -5210,6 +5231,12 @@ async function loadFailures(projectPath, stream) {
|
|
|
5210
5231
|
if (!fs4.existsSync(failuresPath)) {
|
|
5211
5232
|
return Ok([]);
|
|
5212
5233
|
}
|
|
5234
|
+
const stats = fs4.statSync(failuresPath);
|
|
5235
|
+
const cacheKey = failuresPath;
|
|
5236
|
+
const cached = failuresCacheMap.get(cacheKey);
|
|
5237
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5238
|
+
return Ok(cached.entries);
|
|
5239
|
+
}
|
|
5213
5240
|
const content = fs4.readFileSync(failuresPath, "utf-8");
|
|
5214
5241
|
const entries = [];
|
|
5215
5242
|
for (const line of content.split("\n")) {
|
|
@@ -5223,6 +5250,8 @@ async function loadFailures(projectPath, stream) {
|
|
|
5223
5250
|
});
|
|
5224
5251
|
}
|
|
5225
5252
|
}
|
|
5253
|
+
failuresCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5254
|
+
evictIfNeeded(failuresCacheMap);
|
|
5226
5255
|
return Ok(entries);
|
|
5227
5256
|
} catch (error) {
|
|
5228
5257
|
return Err(
|
|
@@ -5251,6 +5280,7 @@ async function archiveFailures(projectPath, stream) {
|
|
|
5251
5280
|
counter++;
|
|
5252
5281
|
}
|
|
5253
5282
|
fs4.renameSync(failuresPath, path3.join(archiveDir, archiveName));
|
|
5283
|
+
failuresCacheMap.delete(failuresPath);
|
|
5254
5284
|
return Ok(void 0);
|
|
5255
5285
|
} catch (error) {
|
|
5256
5286
|
return Err(
|
|
@@ -6321,14 +6351,22 @@ async function runCIChecks(input) {
|
|
|
6321
6351
|
const { projectRoot, config, skip = [], failOn = "error" } = input;
|
|
6322
6352
|
try {
|
|
6323
6353
|
const checks = [];
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
checks.push(result);
|
|
6330
|
-
}
|
|
6354
|
+
const skippedSet = new Set(skip);
|
|
6355
|
+
if (skippedSet.has("validate")) {
|
|
6356
|
+
checks.push({ name: "validate", status: "skip", issues: [], durationMs: 0 });
|
|
6357
|
+
} else {
|
|
6358
|
+
checks.push(await runSingleCheck("validate", projectRoot, config));
|
|
6331
6359
|
}
|
|
6360
|
+
const remainingChecks = ALL_CHECKS.slice(1);
|
|
6361
|
+
const phase2Results = await Promise.all(
|
|
6362
|
+
remainingChecks.map(async (name) => {
|
|
6363
|
+
if (skippedSet.has(name)) {
|
|
6364
|
+
return { name, status: "skip", issues: [], durationMs: 0 };
|
|
6365
|
+
}
|
|
6366
|
+
return runSingleCheck(name, projectRoot, config);
|
|
6367
|
+
})
|
|
6368
|
+
);
|
|
6369
|
+
checks.push(...phase2Results);
|
|
6332
6370
|
const summary = buildSummary(checks);
|
|
6333
6371
|
const exitCode = determineExitCode(summary, failOn);
|
|
6334
6372
|
const report = {
|
|
@@ -6453,76 +6491,93 @@ async function runMechanicalChecks(options) {
|
|
|
6453
6491
|
});
|
|
6454
6492
|
}
|
|
6455
6493
|
}
|
|
6494
|
+
const parallelChecks = [];
|
|
6456
6495
|
if (!skip.includes("check-docs")) {
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6496
|
+
parallelChecks.push(
|
|
6497
|
+
(async () => {
|
|
6498
|
+
const localFindings = [];
|
|
6499
|
+
try {
|
|
6500
|
+
const docsDir = path6.join(projectRoot, config.docsDir ?? "docs");
|
|
6501
|
+
const result = await checkDocCoverage("project", { docsDir });
|
|
6502
|
+
if (!result.ok) {
|
|
6503
|
+
statuses["check-docs"] = "warn";
|
|
6504
|
+
localFindings.push({
|
|
6505
|
+
tool: "check-docs",
|
|
6506
|
+
file: docsDir,
|
|
6507
|
+
message: result.error.message,
|
|
6508
|
+
severity: "warning"
|
|
6509
|
+
});
|
|
6510
|
+
} else if (result.value.gaps && result.value.gaps.length > 0) {
|
|
6511
|
+
statuses["check-docs"] = "warn";
|
|
6512
|
+
for (const gap of result.value.gaps) {
|
|
6513
|
+
localFindings.push({
|
|
6514
|
+
tool: "check-docs",
|
|
6515
|
+
file: gap.file,
|
|
6516
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6517
|
+
severity: "warning"
|
|
6518
|
+
});
|
|
6519
|
+
}
|
|
6520
|
+
} else {
|
|
6521
|
+
statuses["check-docs"] = "pass";
|
|
6522
|
+
}
|
|
6523
|
+
} catch (err) {
|
|
6524
|
+
statuses["check-docs"] = "warn";
|
|
6525
|
+
localFindings.push({
|
|
6472
6526
|
tool: "check-docs",
|
|
6473
|
-
file:
|
|
6474
|
-
message:
|
|
6527
|
+
file: path6.join(projectRoot, "docs"),
|
|
6528
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6475
6529
|
severity: "warning"
|
|
6476
6530
|
});
|
|
6477
6531
|
}
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
} catch (err) {
|
|
6482
|
-
statuses["check-docs"] = "warn";
|
|
6483
|
-
findings.push({
|
|
6484
|
-
tool: "check-docs",
|
|
6485
|
-
file: path6.join(projectRoot, "docs"),
|
|
6486
|
-
message: err instanceof Error ? err.message : String(err),
|
|
6487
|
-
severity: "warning"
|
|
6488
|
-
});
|
|
6489
|
-
}
|
|
6532
|
+
return localFindings;
|
|
6533
|
+
})()
|
|
6534
|
+
);
|
|
6490
6535
|
}
|
|
6491
6536
|
if (!skip.includes("security-scan")) {
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
findings.
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6537
|
+
parallelChecks.push(
|
|
6538
|
+
(async () => {
|
|
6539
|
+
const localFindings = [];
|
|
6540
|
+
try {
|
|
6541
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
6542
|
+
if (!securityConfig.enabled) {
|
|
6543
|
+
statuses["security-scan"] = "skip";
|
|
6544
|
+
} else {
|
|
6545
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
6546
|
+
scanner.configureForProject(projectRoot);
|
|
6547
|
+
const filesToScan = changedFiles ?? [];
|
|
6548
|
+
const scanResult = await scanner.scanFiles(filesToScan);
|
|
6549
|
+
if (scanResult.findings.length > 0) {
|
|
6550
|
+
statuses["security-scan"] = "warn";
|
|
6551
|
+
for (const f of scanResult.findings) {
|
|
6552
|
+
localFindings.push({
|
|
6553
|
+
tool: "security-scan",
|
|
6554
|
+
file: f.file,
|
|
6555
|
+
line: f.line,
|
|
6556
|
+
ruleId: f.ruleId,
|
|
6557
|
+
message: f.message,
|
|
6558
|
+
severity: f.severity === "info" ? "warning" : f.severity
|
|
6559
|
+
});
|
|
6560
|
+
}
|
|
6561
|
+
} else {
|
|
6562
|
+
statuses["security-scan"] = "pass";
|
|
6563
|
+
}
|
|
6512
6564
|
}
|
|
6513
|
-
}
|
|
6514
|
-
statuses["security-scan"] = "
|
|
6565
|
+
} catch (err) {
|
|
6566
|
+
statuses["security-scan"] = "warn";
|
|
6567
|
+
localFindings.push({
|
|
6568
|
+
tool: "security-scan",
|
|
6569
|
+
file: projectRoot,
|
|
6570
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6571
|
+
severity: "warning"
|
|
6572
|
+
});
|
|
6515
6573
|
}
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
severity: "warning"
|
|
6524
|
-
});
|
|
6525
|
-
}
|
|
6574
|
+
return localFindings;
|
|
6575
|
+
})()
|
|
6576
|
+
);
|
|
6577
|
+
}
|
|
6578
|
+
const parallelResults = await Promise.all(parallelChecks);
|
|
6579
|
+
for (const result of parallelResults) {
|
|
6580
|
+
findings.push(...result);
|
|
6526
6581
|
}
|
|
6527
6582
|
const hasErrors = findings.some((f) => f.severity === "error");
|
|
6528
6583
|
const stopPipeline = statuses.validate === "fail" || statuses["check-deps"] === "fail";
|