91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
import {
|
|
buildProductionModuleInventory,
|
|
evaluateRiskCoverage,
|
|
parseRepositoryRiskCoveragePolicy,
|
|
} from "./lib/risk-coverage.ts";
|
|
|
|
function argumentValue(name: string, fallback?: string): string | undefined {
|
|
const index = process.argv.indexOf(name);
|
|
return index >= 0 ? process.argv[index + 1] : fallback;
|
|
}
|
|
|
|
function requiredArgument(name: string, fallback: string): string {
|
|
const value = argumentValue(name, fallback);
|
|
if (!value) throw new TypeError(`${name} requires a path`);
|
|
return value;
|
|
}
|
|
|
|
function parseChangedFiles(value: unknown): readonly string[] {
|
|
if (
|
|
!Array.isArray(value) ||
|
|
value.some((entry) => typeof entry !== "string") ||
|
|
new Set(value).size !== value.length
|
|
) {
|
|
throw new TypeError("changed files input must be an array of unique paths");
|
|
}
|
|
return Object.freeze([...value] as string[]);
|
|
}
|
|
|
|
const repositoryRoot = path.resolve(
|
|
requiredArgument("--repository-root", process.cwd()),
|
|
);
|
|
const policyPath = requiredArgument(
|
|
"--policy",
|
|
"config/testing/risk-coverage.json",
|
|
);
|
|
const summaryPath = requiredArgument(
|
|
"--summary",
|
|
"artifacts/tests/coverage/coverage-summary.json",
|
|
);
|
|
const artifactPath = requiredArgument(
|
|
"--artifact",
|
|
"artifacts/quality/risk-coverage.json",
|
|
);
|
|
const changedFilesPath = argumentValue("--changed-files");
|
|
const policy = parseRepositoryRiskCoveragePolicy(
|
|
JSON.parse(await readFile(path.resolve(repositoryRoot, policyPath), "utf8")) as unknown,
|
|
);
|
|
const inventory = await buildProductionModuleInventory({
|
|
repositoryRoot,
|
|
generatedPaths: policy.generatedPaths,
|
|
});
|
|
const changedFiles = changedFilesPath
|
|
? parseChangedFiles(
|
|
JSON.parse(
|
|
await readFile(path.resolve(repositoryRoot, changedFilesPath), "utf8"),
|
|
) as unknown,
|
|
)
|
|
: [];
|
|
const result = evaluateRiskCoverage({
|
|
repositoryRoot,
|
|
inventory,
|
|
policy,
|
|
summary: JSON.parse(
|
|
await readFile(path.resolve(repositoryRoot, summaryPath), "utf8"),
|
|
) as unknown,
|
|
changedFiles,
|
|
});
|
|
const artifact = {
|
|
schemaVersion: 2,
|
|
policy: policyPath,
|
|
summary: summaryPath,
|
|
changedFiles: changedFilesPath ?? null,
|
|
...result,
|
|
};
|
|
const resolvedArtifactPath = path.resolve(repositoryRoot, artifactPath);
|
|
await mkdir(path.dirname(resolvedArtifactPath), { recursive: true });
|
|
await writeFile(resolvedArtifactPath, `${JSON.stringify(artifact, null, 2)}\n`);
|
|
|
|
if (result.failures.length > 0) {
|
|
process.stderr.write(
|
|
`Risk coverage failed:\n- ${result.failures.join("\n- ")}\n`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
process.stdout.write(
|
|
`Risk coverage: PASS (${result.selectedTotal}/${result.repositoryTotal} production modules, ${result.results.length} thresholds)\n`,
|
|
);
|