@harness-engineering/core 0.9.2 → 0.10.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/dist/index.js +137 -85
- package/dist/index.mjs +137 -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);
|
|
@@ -5336,25 +5345,35 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
5336
5345
|
if (!fs4.existsSync(learningsPath)) {
|
|
5337
5346
|
return (0, import_types.Ok)([]);
|
|
5338
5347
|
}
|
|
5339
|
-
const
|
|
5340
|
-
const
|
|
5341
|
-
const
|
|
5342
|
-
let
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
const
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5348
|
+
const stats = fs4.statSync(learningsPath);
|
|
5349
|
+
const cacheKey = learningsPath;
|
|
5350
|
+
const cached = learningsCacheMap.get(cacheKey);
|
|
5351
|
+
let entries;
|
|
5352
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5353
|
+
entries = cached.entries;
|
|
5354
|
+
} else {
|
|
5355
|
+
const content = fs4.readFileSync(learningsPath, "utf-8");
|
|
5356
|
+
const lines = content.split("\n");
|
|
5357
|
+
entries = [];
|
|
5358
|
+
let currentBlock = [];
|
|
5359
|
+
for (const line of lines) {
|
|
5360
|
+
if (line.startsWith("# ")) continue;
|
|
5361
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
5362
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
5363
|
+
if (isDatedBullet || isHeading) {
|
|
5364
|
+
if (currentBlock.length > 0) {
|
|
5365
|
+
entries.push(currentBlock.join("\n"));
|
|
5366
|
+
}
|
|
5367
|
+
currentBlock = [line];
|
|
5368
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5369
|
+
currentBlock.push(line);
|
|
5350
5370
|
}
|
|
5351
|
-
currentBlock = [line];
|
|
5352
|
-
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5353
|
-
currentBlock.push(line);
|
|
5354
5371
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5372
|
+
if (currentBlock.length > 0) {
|
|
5373
|
+
entries.push(currentBlock.join("\n"));
|
|
5374
|
+
}
|
|
5375
|
+
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5376
|
+
evictIfNeeded(learningsCacheMap);
|
|
5358
5377
|
}
|
|
5359
5378
|
if (!skillName) {
|
|
5360
5379
|
return (0, import_types.Ok)(entries);
|
|
@@ -5405,6 +5424,12 @@ async function loadFailures(projectPath, stream) {
|
|
|
5405
5424
|
if (!fs4.existsSync(failuresPath)) {
|
|
5406
5425
|
return (0, import_types.Ok)([]);
|
|
5407
5426
|
}
|
|
5427
|
+
const stats = fs4.statSync(failuresPath);
|
|
5428
|
+
const cacheKey = failuresPath;
|
|
5429
|
+
const cached = failuresCacheMap.get(cacheKey);
|
|
5430
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5431
|
+
return (0, import_types.Ok)(cached.entries);
|
|
5432
|
+
}
|
|
5408
5433
|
const content = fs4.readFileSync(failuresPath, "utf-8");
|
|
5409
5434
|
const entries = [];
|
|
5410
5435
|
for (const line of content.split("\n")) {
|
|
@@ -5418,6 +5443,8 @@ async function loadFailures(projectPath, stream) {
|
|
|
5418
5443
|
});
|
|
5419
5444
|
}
|
|
5420
5445
|
}
|
|
5446
|
+
failuresCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5447
|
+
evictIfNeeded(failuresCacheMap);
|
|
5421
5448
|
return (0, import_types.Ok)(entries);
|
|
5422
5449
|
} catch (error) {
|
|
5423
5450
|
return (0, import_types.Err)(
|
|
@@ -6516,14 +6543,22 @@ async function runCIChecks(input) {
|
|
|
6516
6543
|
const { projectRoot, config, skip = [], failOn = "error" } = input;
|
|
6517
6544
|
try {
|
|
6518
6545
|
const checks = [];
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
checks.push(result);
|
|
6525
|
-
}
|
|
6546
|
+
const skippedSet = new Set(skip);
|
|
6547
|
+
if (skippedSet.has("validate")) {
|
|
6548
|
+
checks.push({ name: "validate", status: "skip", issues: [], durationMs: 0 });
|
|
6549
|
+
} else {
|
|
6550
|
+
checks.push(await runSingleCheck("validate", projectRoot, config));
|
|
6526
6551
|
}
|
|
6552
|
+
const remainingChecks = ALL_CHECKS.slice(1);
|
|
6553
|
+
const phase2Results = await Promise.all(
|
|
6554
|
+
remainingChecks.map(async (name) => {
|
|
6555
|
+
if (skippedSet.has(name)) {
|
|
6556
|
+
return { name, status: "skip", issues: [], durationMs: 0 };
|
|
6557
|
+
}
|
|
6558
|
+
return runSingleCheck(name, projectRoot, config);
|
|
6559
|
+
})
|
|
6560
|
+
);
|
|
6561
|
+
checks.push(...phase2Results);
|
|
6527
6562
|
const summary = buildSummary(checks);
|
|
6528
6563
|
const exitCode = determineExitCode(summary, failOn);
|
|
6529
6564
|
const report = {
|
|
@@ -6648,76 +6683,93 @@ async function runMechanicalChecks(options) {
|
|
|
6648
6683
|
});
|
|
6649
6684
|
}
|
|
6650
6685
|
}
|
|
6686
|
+
const parallelChecks = [];
|
|
6651
6687
|
if (!skip.includes("check-docs")) {
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6688
|
+
parallelChecks.push(
|
|
6689
|
+
(async () => {
|
|
6690
|
+
const localFindings = [];
|
|
6691
|
+
try {
|
|
6692
|
+
const docsDir = path6.join(projectRoot, config.docsDir ?? "docs");
|
|
6693
|
+
const result = await checkDocCoverage("project", { docsDir });
|
|
6694
|
+
if (!result.ok) {
|
|
6695
|
+
statuses["check-docs"] = "warn";
|
|
6696
|
+
localFindings.push({
|
|
6697
|
+
tool: "check-docs",
|
|
6698
|
+
file: docsDir,
|
|
6699
|
+
message: result.error.message,
|
|
6700
|
+
severity: "warning"
|
|
6701
|
+
});
|
|
6702
|
+
} else if (result.value.gaps && result.value.gaps.length > 0) {
|
|
6703
|
+
statuses["check-docs"] = "warn";
|
|
6704
|
+
for (const gap of result.value.gaps) {
|
|
6705
|
+
localFindings.push({
|
|
6706
|
+
tool: "check-docs",
|
|
6707
|
+
file: gap.file,
|
|
6708
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6709
|
+
severity: "warning"
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6712
|
+
} else {
|
|
6713
|
+
statuses["check-docs"] = "pass";
|
|
6714
|
+
}
|
|
6715
|
+
} catch (err) {
|
|
6716
|
+
statuses["check-docs"] = "warn";
|
|
6717
|
+
localFindings.push({
|
|
6667
6718
|
tool: "check-docs",
|
|
6668
|
-
file:
|
|
6669
|
-
message:
|
|
6719
|
+
file: path6.join(projectRoot, "docs"),
|
|
6720
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6670
6721
|
severity: "warning"
|
|
6671
6722
|
});
|
|
6672
6723
|
}
|
|
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
|
-
}
|
|
6724
|
+
return localFindings;
|
|
6725
|
+
})()
|
|
6726
|
+
);
|
|
6685
6727
|
}
|
|
6686
6728
|
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
|
-
|
|
6729
|
+
parallelChecks.push(
|
|
6730
|
+
(async () => {
|
|
6731
|
+
const localFindings = [];
|
|
6732
|
+
try {
|
|
6733
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
6734
|
+
if (!securityConfig.enabled) {
|
|
6735
|
+
statuses["security-scan"] = "skip";
|
|
6736
|
+
} else {
|
|
6737
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
6738
|
+
scanner.configureForProject(projectRoot);
|
|
6739
|
+
const filesToScan = changedFiles ?? [];
|
|
6740
|
+
const scanResult = await scanner.scanFiles(filesToScan);
|
|
6741
|
+
if (scanResult.findings.length > 0) {
|
|
6742
|
+
statuses["security-scan"] = "warn";
|
|
6743
|
+
for (const f of scanResult.findings) {
|
|
6744
|
+
localFindings.push({
|
|
6745
|
+
tool: "security-scan",
|
|
6746
|
+
file: f.file,
|
|
6747
|
+
line: f.line,
|
|
6748
|
+
ruleId: f.ruleId,
|
|
6749
|
+
message: f.message,
|
|
6750
|
+
severity: f.severity === "info" ? "warning" : f.severity
|
|
6751
|
+
});
|
|
6752
|
+
}
|
|
6753
|
+
} else {
|
|
6754
|
+
statuses["security-scan"] = "pass";
|
|
6755
|
+
}
|
|
6707
6756
|
}
|
|
6708
|
-
}
|
|
6709
|
-
statuses["security-scan"] = "
|
|
6757
|
+
} catch (err) {
|
|
6758
|
+
statuses["security-scan"] = "warn";
|
|
6759
|
+
localFindings.push({
|
|
6760
|
+
tool: "security-scan",
|
|
6761
|
+
file: projectRoot,
|
|
6762
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6763
|
+
severity: "warning"
|
|
6764
|
+
});
|
|
6710
6765
|
}
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
severity: "warning"
|
|
6719
|
-
});
|
|
6720
|
-
}
|
|
6766
|
+
return localFindings;
|
|
6767
|
+
})()
|
|
6768
|
+
);
|
|
6769
|
+
}
|
|
6770
|
+
const parallelResults = await Promise.all(parallelChecks);
|
|
6771
|
+
for (const result of parallelResults) {
|
|
6772
|
+
findings.push(...result);
|
|
6721
6773
|
}
|
|
6722
6774
|
const hasErrors = findings.some((f) => f.severity === "error");
|
|
6723
6775
|
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);
|
|
@@ -5141,25 +5150,35 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
5141
5150
|
if (!fs4.existsSync(learningsPath)) {
|
|
5142
5151
|
return Ok([]);
|
|
5143
5152
|
}
|
|
5144
|
-
const
|
|
5145
|
-
const
|
|
5146
|
-
const
|
|
5147
|
-
let
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5153
|
+
const stats = fs4.statSync(learningsPath);
|
|
5154
|
+
const cacheKey = learningsPath;
|
|
5155
|
+
const cached = learningsCacheMap.get(cacheKey);
|
|
5156
|
+
let entries;
|
|
5157
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5158
|
+
entries = cached.entries;
|
|
5159
|
+
} else {
|
|
5160
|
+
const content = fs4.readFileSync(learningsPath, "utf-8");
|
|
5161
|
+
const lines = content.split("\n");
|
|
5162
|
+
entries = [];
|
|
5163
|
+
let currentBlock = [];
|
|
5164
|
+
for (const line of lines) {
|
|
5165
|
+
if (line.startsWith("# ")) continue;
|
|
5166
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
5167
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
5168
|
+
if (isDatedBullet || isHeading) {
|
|
5169
|
+
if (currentBlock.length > 0) {
|
|
5170
|
+
entries.push(currentBlock.join("\n"));
|
|
5171
|
+
}
|
|
5172
|
+
currentBlock = [line];
|
|
5173
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5174
|
+
currentBlock.push(line);
|
|
5155
5175
|
}
|
|
5156
|
-
currentBlock = [line];
|
|
5157
|
-
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
5158
|
-
currentBlock.push(line);
|
|
5159
5176
|
}
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5177
|
+
if (currentBlock.length > 0) {
|
|
5178
|
+
entries.push(currentBlock.join("\n"));
|
|
5179
|
+
}
|
|
5180
|
+
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5181
|
+
evictIfNeeded(learningsCacheMap);
|
|
5163
5182
|
}
|
|
5164
5183
|
if (!skillName) {
|
|
5165
5184
|
return Ok(entries);
|
|
@@ -5210,6 +5229,12 @@ async function loadFailures(projectPath, stream) {
|
|
|
5210
5229
|
if (!fs4.existsSync(failuresPath)) {
|
|
5211
5230
|
return Ok([]);
|
|
5212
5231
|
}
|
|
5232
|
+
const stats = fs4.statSync(failuresPath);
|
|
5233
|
+
const cacheKey = failuresPath;
|
|
5234
|
+
const cached = failuresCacheMap.get(cacheKey);
|
|
5235
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
5236
|
+
return Ok(cached.entries);
|
|
5237
|
+
}
|
|
5213
5238
|
const content = fs4.readFileSync(failuresPath, "utf-8");
|
|
5214
5239
|
const entries = [];
|
|
5215
5240
|
for (const line of content.split("\n")) {
|
|
@@ -5223,6 +5248,8 @@ async function loadFailures(projectPath, stream) {
|
|
|
5223
5248
|
});
|
|
5224
5249
|
}
|
|
5225
5250
|
}
|
|
5251
|
+
failuresCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
5252
|
+
evictIfNeeded(failuresCacheMap);
|
|
5226
5253
|
return Ok(entries);
|
|
5227
5254
|
} catch (error) {
|
|
5228
5255
|
return Err(
|
|
@@ -6321,14 +6348,22 @@ async function runCIChecks(input) {
|
|
|
6321
6348
|
const { projectRoot, config, skip = [], failOn = "error" } = input;
|
|
6322
6349
|
try {
|
|
6323
6350
|
const checks = [];
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
checks.push(result);
|
|
6330
|
-
}
|
|
6351
|
+
const skippedSet = new Set(skip);
|
|
6352
|
+
if (skippedSet.has("validate")) {
|
|
6353
|
+
checks.push({ name: "validate", status: "skip", issues: [], durationMs: 0 });
|
|
6354
|
+
} else {
|
|
6355
|
+
checks.push(await runSingleCheck("validate", projectRoot, config));
|
|
6331
6356
|
}
|
|
6357
|
+
const remainingChecks = ALL_CHECKS.slice(1);
|
|
6358
|
+
const phase2Results = await Promise.all(
|
|
6359
|
+
remainingChecks.map(async (name) => {
|
|
6360
|
+
if (skippedSet.has(name)) {
|
|
6361
|
+
return { name, status: "skip", issues: [], durationMs: 0 };
|
|
6362
|
+
}
|
|
6363
|
+
return runSingleCheck(name, projectRoot, config);
|
|
6364
|
+
})
|
|
6365
|
+
);
|
|
6366
|
+
checks.push(...phase2Results);
|
|
6332
6367
|
const summary = buildSummary(checks);
|
|
6333
6368
|
const exitCode = determineExitCode(summary, failOn);
|
|
6334
6369
|
const report = {
|
|
@@ -6453,76 +6488,93 @@ async function runMechanicalChecks(options) {
|
|
|
6453
6488
|
});
|
|
6454
6489
|
}
|
|
6455
6490
|
}
|
|
6491
|
+
const parallelChecks = [];
|
|
6456
6492
|
if (!skip.includes("check-docs")) {
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6493
|
+
parallelChecks.push(
|
|
6494
|
+
(async () => {
|
|
6495
|
+
const localFindings = [];
|
|
6496
|
+
try {
|
|
6497
|
+
const docsDir = path6.join(projectRoot, config.docsDir ?? "docs");
|
|
6498
|
+
const result = await checkDocCoverage("project", { docsDir });
|
|
6499
|
+
if (!result.ok) {
|
|
6500
|
+
statuses["check-docs"] = "warn";
|
|
6501
|
+
localFindings.push({
|
|
6502
|
+
tool: "check-docs",
|
|
6503
|
+
file: docsDir,
|
|
6504
|
+
message: result.error.message,
|
|
6505
|
+
severity: "warning"
|
|
6506
|
+
});
|
|
6507
|
+
} else if (result.value.gaps && result.value.gaps.length > 0) {
|
|
6508
|
+
statuses["check-docs"] = "warn";
|
|
6509
|
+
for (const gap of result.value.gaps) {
|
|
6510
|
+
localFindings.push({
|
|
6511
|
+
tool: "check-docs",
|
|
6512
|
+
file: gap.file,
|
|
6513
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6514
|
+
severity: "warning"
|
|
6515
|
+
});
|
|
6516
|
+
}
|
|
6517
|
+
} else {
|
|
6518
|
+
statuses["check-docs"] = "pass";
|
|
6519
|
+
}
|
|
6520
|
+
} catch (err) {
|
|
6521
|
+
statuses["check-docs"] = "warn";
|
|
6522
|
+
localFindings.push({
|
|
6472
6523
|
tool: "check-docs",
|
|
6473
|
-
file:
|
|
6474
|
-
message:
|
|
6524
|
+
file: path6.join(projectRoot, "docs"),
|
|
6525
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6475
6526
|
severity: "warning"
|
|
6476
6527
|
});
|
|
6477
6528
|
}
|
|
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
|
-
}
|
|
6529
|
+
return localFindings;
|
|
6530
|
+
})()
|
|
6531
|
+
);
|
|
6490
6532
|
}
|
|
6491
6533
|
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
|
-
|
|
6534
|
+
parallelChecks.push(
|
|
6535
|
+
(async () => {
|
|
6536
|
+
const localFindings = [];
|
|
6537
|
+
try {
|
|
6538
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
6539
|
+
if (!securityConfig.enabled) {
|
|
6540
|
+
statuses["security-scan"] = "skip";
|
|
6541
|
+
} else {
|
|
6542
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
6543
|
+
scanner.configureForProject(projectRoot);
|
|
6544
|
+
const filesToScan = changedFiles ?? [];
|
|
6545
|
+
const scanResult = await scanner.scanFiles(filesToScan);
|
|
6546
|
+
if (scanResult.findings.length > 0) {
|
|
6547
|
+
statuses["security-scan"] = "warn";
|
|
6548
|
+
for (const f of scanResult.findings) {
|
|
6549
|
+
localFindings.push({
|
|
6550
|
+
tool: "security-scan",
|
|
6551
|
+
file: f.file,
|
|
6552
|
+
line: f.line,
|
|
6553
|
+
ruleId: f.ruleId,
|
|
6554
|
+
message: f.message,
|
|
6555
|
+
severity: f.severity === "info" ? "warning" : f.severity
|
|
6556
|
+
});
|
|
6557
|
+
}
|
|
6558
|
+
} else {
|
|
6559
|
+
statuses["security-scan"] = "pass";
|
|
6560
|
+
}
|
|
6512
6561
|
}
|
|
6513
|
-
}
|
|
6514
|
-
statuses["security-scan"] = "
|
|
6562
|
+
} catch (err) {
|
|
6563
|
+
statuses["security-scan"] = "warn";
|
|
6564
|
+
localFindings.push({
|
|
6565
|
+
tool: "security-scan",
|
|
6566
|
+
file: projectRoot,
|
|
6567
|
+
message: err instanceof Error ? err.message : String(err),
|
|
6568
|
+
severity: "warning"
|
|
6569
|
+
});
|
|
6515
6570
|
}
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
severity: "warning"
|
|
6524
|
-
});
|
|
6525
|
-
}
|
|
6571
|
+
return localFindings;
|
|
6572
|
+
})()
|
|
6573
|
+
);
|
|
6574
|
+
}
|
|
6575
|
+
const parallelResults = await Promise.all(parallelChecks);
|
|
6576
|
+
for (const result of parallelResults) {
|
|
6577
|
+
findings.push(...result);
|
|
6526
6578
|
}
|
|
6527
6579
|
const hasErrors = findings.some((f) => f.severity === "error");
|
|
6528
6580
|
const stopPipeline = statuses.validate === "fail" || statuses["check-deps"] === "fail";
|