refactor: generate CI workflow from gate contracts
This commit is contained in:
@@ -10,6 +10,9 @@ import {
|
||||
} from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import { parseCiGateContract } from "./contracts/ci-gates.ts";
|
||||
import { generateCiWorkflow } from "./generate-ci-workflow.ts";
|
||||
|
||||
const fixtureRoot = path.resolve(".tmp/realtime-runtime-removal");
|
||||
const pnpmCli = requireEnvironment("npm_execpath");
|
||||
const runtimePaths = [
|
||||
@@ -31,6 +34,10 @@ const runtimeScripts = [
|
||||
"check:realtime-boundaries:fixture",
|
||||
"test:realtime-removal",
|
||||
] as const;
|
||||
const removedEvidencePathFragments = [
|
||||
"realtime-boundaries",
|
||||
"realtime-runtime-removal",
|
||||
] as const;
|
||||
const copyTargets = [
|
||||
"src",
|
||||
"tests",
|
||||
@@ -292,32 +299,81 @@ for (const scriptPath of [
|
||||
]) {
|
||||
await rm(path.join(fixtureRoot, scriptPath), { force: true });
|
||||
}
|
||||
// The root snapshot locks the full repository inventory. This removal fixture
|
||||
// validates its smaller registry through check:ci and its regenerated workflow.
|
||||
await rm(
|
||||
path.join(fixtureRoot, "tests/unit/ci-workflow-generation.test.ts"),
|
||||
{ force: true },
|
||||
);
|
||||
await rm(
|
||||
path.join(
|
||||
fixtureRoot,
|
||||
"tests/unit/__snapshots__/ci-workflow-generation.test.ts.snap",
|
||||
),
|
||||
{ force: true },
|
||||
);
|
||||
|
||||
const gatesPath = path.join(fixtureRoot, "config/ci/gates.json");
|
||||
const gatesDocument = JSON.parse(await readFile(gatesPath, "utf8")) as {
|
||||
gates: Record<
|
||||
string,
|
||||
{
|
||||
steps: Array<{ script: string }>;
|
||||
evidence: string[];
|
||||
}
|
||||
>;
|
||||
};
|
||||
for (const gate of Object.values(gatesDocument.gates)) {
|
||||
gate.steps = gate.steps.filter(
|
||||
({ script }) =>
|
||||
!runtimeScripts.some((runtimeScript) => runtimeScript === script),
|
||||
const gatesDocument = structuredClone(
|
||||
parseCiGateContract(JSON.parse(await readFile(gatesPath, "utf8"))),
|
||||
);
|
||||
const removedScripts = new Set<string>(runtimeScripts);
|
||||
const removedCommandIds = new Set(
|
||||
gatesDocument.commands
|
||||
.filter(({ script }) => removedScripts.has(script))
|
||||
.map(({ id }) => id),
|
||||
);
|
||||
if (removedCommandIds.size !== runtimeScripts.length) {
|
||||
throw new Error("Realtime CI command removal set is incomplete");
|
||||
}
|
||||
const removedArtifactIds = new Set(
|
||||
gatesDocument.artifacts
|
||||
.filter(({ path: artifactPath }) =>
|
||||
removedEvidencePathFragments.some((fragment) =>
|
||||
artifactPath.includes(fragment),
|
||||
),
|
||||
)
|
||||
.map(({ id }) => id),
|
||||
);
|
||||
for (const fragment of removedEvidencePathFragments) {
|
||||
if (
|
||||
!gatesDocument.artifacts.some(({ path: artifactPath }) =>
|
||||
artifactPath.includes(fragment),
|
||||
)
|
||||
) {
|
||||
throw new Error(`Realtime CI evidence is missing: ${fragment}`);
|
||||
}
|
||||
}
|
||||
gatesDocument.commands = gatesDocument.commands.filter(
|
||||
({ id }) => !removedCommandIds.has(id),
|
||||
);
|
||||
gatesDocument.artifacts = gatesDocument.artifacts.filter(
|
||||
({ id }) => !removedArtifactIds.has(id),
|
||||
);
|
||||
for (const gate of gatesDocument.gates) {
|
||||
gate.commandIds = gate.commandIds.filter(
|
||||
(commandId) => !removedCommandIds.has(commandId),
|
||||
);
|
||||
gate.evidence = gate.evidence.filter(
|
||||
(evidence) =>
|
||||
!evidence.includes("realtime-boundaries") &&
|
||||
!evidence.includes("realtime-runtime-removal"),
|
||||
gate.evidenceArtifactIds = gate.evidenceArtifactIds.filter(
|
||||
(artifactId) => !removedArtifactIds.has(artifactId),
|
||||
);
|
||||
}
|
||||
const referencedSchemaIds = new Set(
|
||||
gatesDocument.artifacts.map(({ schemaId }) => schemaId),
|
||||
);
|
||||
gatesDocument.artifactSchemas = gatesDocument.artifactSchemas.filter(
|
||||
({ id }) => referencedSchemaIds.has(id),
|
||||
);
|
||||
const validatedGates = parseCiGateContract(gatesDocument);
|
||||
await writeFile(
|
||||
gatesPath,
|
||||
`${JSON.stringify(gatesDocument, null, 2)}\n`,
|
||||
`${JSON.stringify(validatedGates, null, 2)}\n`,
|
||||
);
|
||||
await generateCiWorkflow({
|
||||
root: fixtureRoot,
|
||||
contract: validatedGates,
|
||||
check: false,
|
||||
});
|
||||
await assertNoRuntimeImports(fixtureRoot);
|
||||
|
||||
const checks: Array<readonly [string, boolean]> = [
|
||||
|
||||
Reference in New Issue
Block a user