Files
moltbot/test/scripts/generate-dependency-release-evidence.test.ts
Josh Avant bd4db5ee62 Add dependency release safety evidence and PR awareness (#81325)
* test: cover dependency pin guard

* build: add dependency vulnerability gate

* build: add dependency risk report

* build: add dependency drift reports

* build: include dependency ownership surface evidence

* build: rename dependency report commands

* build: respect release age exclusions in risk report

* build: clarify transitive risk accounting

* build: remove transitive risk exception registry

* build: clarify transitive risk signal wording

* ci: attach dependency evidence to release preflight

* ci: extract dependency release evidence generator

* build: rename ownership surface dependency report

* ci: clarify release evidence naming

* build: clarify recently published risk report

* build: reorder transitive risk report sections

* build: fix ownership surface pluralization

* ci: surface dependency changes on PRs

* ci: harden dependency change awareness

* ci: use dependency changed PR label

* build: fix dependency report lint

* docs: add dependency safety changelog
2026-05-13 03:05:09 -05:00

173 lines
5.9 KiB
TypeScript

import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
DEPENDENCY_EVIDENCE_REPORTS,
collectDependencyEvidenceSummaryCounts,
createDependencyEvidenceManifest,
renderDependencyEvidenceStepSummary,
renderDependencyEvidenceSummary,
resolvePreviousReleaseTag,
resolveReleaseTag,
} from "../../scripts/generate-dependency-release-evidence.mjs";
async function writeJson(dir: string, fileName: string, value: unknown) {
await writeFile(path.join(dir, fileName), `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
describe("generate-dependency-release-evidence", () => {
it("defines the release evidence command list and policy classifications", () => {
expect(DEPENDENCY_EVIDENCE_REPORTS.map(({ command, policy }) => ({ command, policy }))).toEqual(
[
{ command: "pnpm deps:vuln:gate", policy: "hard-blocking" },
{ command: "pnpm deps:transitive-risk:report", policy: "report-only" },
{ command: "pnpm deps:ownership-surface:report", policy: "report-only" },
{ command: "pnpm deps:changes:report", policy: "report-only" },
],
);
});
it("creates the dependency evidence manifest shape", () => {
const manifest = createDependencyEvidenceManifest({
generatedAt: "2026-05-13T00:00:00.000Z",
releaseTag: "v2026.5.13-beta.1",
releaseRef: "v2026.5.13-beta.1",
releaseSha: "abc123",
npmDistTag: "beta",
packageVersion: "2026.5.13-beta.1",
workflowRunId: "123",
workflowRunAttempt: "2",
dependencyChangeBaseRef: "v2026.5.1",
});
expect(manifest).toEqual({
schemaVersion: 1,
generatedAt: "2026-05-13T00:00:00.000Z",
releaseTag: "v2026.5.13-beta.1",
releaseRef: "v2026.5.13-beta.1",
releaseSha: "abc123",
npmDistTag: "beta",
packageName: "openclaw",
packageVersion: "2026.5.13-beta.1",
workflowRunId: "123",
workflowRunAttempt: "2",
dependencyChangeBaseRef: "v2026.5.1",
reports: DEPENDENCY_EVIDENCE_REPORTS,
});
});
it("uses a synthetic release tag for validation-only SHA preflight input", () => {
expect(
resolveReleaseTag({
releaseRef: "0123456789abcdef0123456789abcdef01234567",
packageVersion: "2026.5.13",
}),
).toBe("v2026.5.13");
expect(
resolveReleaseTag({
releaseRef: "v2026.5.13-beta.1",
packageVersion: "2026.5.13-beta.1",
}),
).toBe("v2026.5.13-beta.1");
});
it("falls back to fetching tags when local previous-release resolution misses", () => {
const calls: Array<{ command: string; args: string[] }> = [];
let describeCalls = 0;
const execFileSyncImpl = (command: string, args: string[] = []) => {
calls.push({ command, args });
if (command !== "git") {
throw new Error(`unexpected command: ${command}`);
}
if (args[0] === "describe") {
describeCalls += 1;
if (describeCalls === 1) {
throw new Error("tag not found");
}
return "v2026.5.1\n";
}
if (args[0] === "fetch") {
return "";
}
throw new Error(`unexpected git args: ${args.join(" ")}`);
};
expect(
resolvePreviousReleaseTag({
rootDir: "/repo",
execFileSyncImpl,
}),
).toBe("v2026.5.1");
expect(calls.map(({ args }) => args[0])).toEqual(["describe", "fetch", "describe"]);
expect(calls[1].args).toEqual(["fetch", "--tags", "--force", "origin"]);
});
it("collects report counts and renders human summaries", async () => {
const dir = await mkdtemp(path.join(tmpdir(), "openclaw-release-dependency-evidence-test-"));
await writeJson(dir, "dependency-vulnerability-gate.json", {
blockers: [{ id: "GHSA-blocker" }],
findings: [{ id: "GHSA-blocker" }, { id: "GHSA-report" }],
});
await writeJson(dir, "transitive-manifest-risk-report.json", {
findingCount: 17,
workspaceExcludedFindingCount: 3,
metadataFailures: [{ packageName: "missing" }],
});
await writeJson(dir, "dependency-ownership-surface-report.json", {
summary: {
lockfilePackageCount: 101,
buildRiskPackageCount: 8,
},
});
await writeJson(dir, "dependency-changes-report.json", {
summary: {
dependencyFileChanges: 4,
addedPackages: 5,
removedPackages: 6,
changedPackages: 7,
},
});
const counts = await collectDependencyEvidenceSummaryCounts(dir);
expect(counts).toEqual({
vulnerabilityBlockers: 1,
vulnerabilityFindings: 2,
transitiveRiskSignals: 17,
workspaceExcludedTransitiveSignals: 3,
transitiveMetadataFailures: 1,
ownershipLockfilePackages: 101,
ownershipBuildRiskPackages: 8,
dependencyFileChanges: 4,
dependencyAddedPackages: 5,
dependencyRemovedPackages: 6,
dependencyChangedPackages: 7,
});
const summary = renderDependencyEvidenceSummary({
releaseTag: "v2026.5.13",
releaseSha: "abc123",
baseRef: "v2026.5.1",
counts,
});
expect(summary).toContain("- npm advisory vulnerability hard blockers: 1");
expect(summary).toContain("- Transitive manifest reported risk signals: 17");
expect(summary).toContain("- Dependency change baseline: `v2026.5.1`");
expect(summary).toContain("- Resolved package changes: +5 -6 changed 7");
const stepSummary = renderDependencyEvidenceStepSummary({
evidenceArtifactName: "openclaw-release-dependency-evidence-v2026.5.13",
baseRef: "v2026.5.1",
counts,
});
expect(stepSummary).toContain(
"- Evidence artifact: `openclaw-release-dependency-evidence-v2026.5.13`",
);
expect(stepSummary).toContain("- npm advisory vulnerability hard blockers: `1`");
await expect(
readFile(path.join(dir, "dependency-vulnerability-gate.json"), "utf8"),
).resolves.toContain("GHSA-blocker");
});
});