refactor(action): replace bash blocks with zero-dependency Node.js modules
Philipp/Global-Workflows: Pre-commit Lint (Scoped) / pre-commit (pull_request) Successful in 1m53s

Extract ~450 lines of complex bash from action.yaml into modular,
unit-tested Node.js scripts in lib/. All logic is covered by 26 native
node:test unit tests (node --test, no external dependencies).

Modules added:
- lib/platforms.js   - platform parsing and OCI manifest auto-discovery
- lib/distro.js      - SPDX PURL distro detection (os_name/os_version)
- lib/verifier.js    - Trivy arch mapping and SHA256 verification
- lib/diagnostics.js - CI terminal diagnostic formatters
- lib/install-trivy.js  - Trivy install/verify entrypoint (replaces bash step)
- lib/extract-sboms.js  - SBOM extraction entrypoint (replaces bash step)
- lib/scan-sboms.js     - SBOM scan + diff entrypoint (replaces bash step)

Tests added (all pass, auto-discovered by
ode --test):
- test/platforms.test.js
- test/distro.test.js
- test/verifier.test.js
- test/diagnostics.test.js
- test/orchestration.test.js

action.yaml shrinks from 554 to 107 lines. Inputs are bridged to Node
scripts via INPUT_* env vars so modules are fully decoupled from the
Actions runner and testable in isolation.
This commit is contained in:
Philipp committed 2026-09-15 00:34:23 +02:00
1 parent 6643b7449e
commit 599b74ef49
13 files changed
+1145 -347

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
/**
* Diagnostic formatting and reporting utilities.
*/
function formatSbomOverview({
headImage,
targetImage,
headFiles = [],
targetFiles = [],
headError = null,
targetError = null,
}) {
const lines = [
'',
'================================================================================',
' SBOM EXTRACTION DIAGNOSTIC OVERVIEW ',
'================================================================================',
];
if (headError) {
lines.push(`HEAD Image (${headImage}): ❌ FAILED - ${headError}`);
} else {
lines.push(`HEAD Image (${headImage}): ✅ SUCCESS`);
for (const f of headFiles) {
lines.push(` - ${f.name} (${f.size} bytes)`);
}
}
if (targetError) {
lines.push(`TARGET Baseline Image (${targetImage}): ❌ FAILED - ${targetError}`);
} else {
lines.push(`TARGET Baseline Image (${targetImage}): ✅ SUCCESS`);
for (const f of targetFiles) {
lines.push(` - ${f.name} (${f.size} bytes)`);
}
}
lines.push('================================================================================');
lines.push('');
return lines.join('\n');
}
function formatScanOverview({
headImage,
targetImage,
targetBranch = 'main',
strategy = null,
headSummary = [],
targetSummary = [],
}) {
const lines = [
'',
'================================================================================',
' CVE VULNERABILITY SCAN DIAGNOSTIC OVERVIEW ',
'================================================================================',
];
if (strategy) {
lines.push(`Scan Strategy: ${strategy}`);
lines.push('--------------------------------------------------------------------------------');
}
lines.push(`HEAD Image: ${headImage}`);
for (const line of headSummary) {
lines.push(line);
}
lines.push('');
lines.push(`TARGET Baseline Image: ${targetImage} (branch: '${targetBranch}')`);
for (const line of targetSummary) {
lines.push(line);
}
lines.push('================================================================================');
lines.push('');
return lines.join('\n');
}
function countVulnerabilities(report) {
if (!report) return 0;
let data = report;
if (typeof report === 'string' || Buffer.isBuffer(report)) {
try {
data = JSON.parse(report.toString('utf8'));
} catch {
return 0;
}
}
if (!data || typeof data !== 'object') return 0;
let count = 0;
if (Array.isArray(data.Results)) {
for (const res of data.Results) {
if (res && Array.isArray(res.Vulnerabilities)) {
count += res.Vulnerabilities.length;
}
}
}
return count;
}
module.exports = {
formatSbomOverview,
formatScanOverview,
countVulnerabilities,
};