refactor: validate generated evidence artifacts

This commit is contained in:
DongHyeonka
2026-08-02 04:33:01 +09:00
parent 2c3cab2518
commit c9f5887cac
18 changed files with 1874 additions and 320 deletions
+3 -1
View File
@@ -70,6 +70,8 @@
"test:all": "corepack pnpm test:runtime-schema && corepack pnpm test:unit && corepack pnpm test:component && corepack pnpm test:integration && corepack pnpm test:reference-feature && corepack pnpm test:recipes",
"verify:lockfile": "corepack pnpm install --frozen-lockfile",
"check:frozen-lockfile:fixture": "node scripts/check-frozen-lockfile-fixture.ts",
"generate:artifact-schemas": "node scripts/generate-artifact-schemas.ts",
"check:artifact-schemas": "node scripts/generate-artifact-schemas.ts --check",
"generate:supply-chain": "node scripts/generate-supply-chain.ts",
"verify:supply-chain": "node scripts/verify-supply-chain-artifacts.ts",
"update:dependency-baseline": "node scripts/update-dependency-baseline.ts",
@@ -101,7 +103,7 @@
"drill:runbook": "node scripts/drill-runbook.ts",
"drill:runbooks": "corepack pnpm drill:runbook -- FE-RB-001 && corepack pnpm drill:runbook -- FE-RB-002 && corepack pnpm drill:runbook -- FE-RB-003 && corepack pnpm drill:runbook -- FE-RB-004 && corepack pnpm drill:runbook -- FE-RB-005",
"ci:gate": "node scripts/run-ci-gate.ts",
"check:ci": "node scripts/check-ci-contract.ts",
"check:ci": "corepack pnpm check:artifact-schemas && node scripts/check-ci-contract.ts",
"verify:documentation": "node scripts/verify-documentation-readiness.ts",
"build:app-only": "vite build && node scripts/generate-build-manifest.ts",
"generate:contract-set": "node scripts/generate-contract-set.ts",
+56 -56
View File
@@ -1,51 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"properties": {
"schemaVersion": {
"type": "number",
"const": 1
},
"buildId": {
"type": "string",
"minLength": 1
},
"commitSha": {
"type": "string",
"minLength": 1
},
"releaseId": {
"type": "string",
"minLength": 1
},
"moduleInventoryHash": {
"type": "string",
"minLength": 1
},
"generatedAt": {
"type": "string",
"minLength": 1
},
"buildContext": {
"type": "object",
"additionalProperties": false,
"properties": {
"nodeVersion": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"packageManagerVersion": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"runnerImage": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"sourceDateEpoch": {
"anyOf": [
{
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
{
"type": "null"
@@ -59,37 +35,53 @@
"runnerImage",
"sourceDateEpoch"
],
"additionalProperties": false
"type": "object"
},
"buildId": {
"minLength": 1,
"type": "string"
},
"commitSha": {
"minLength": 1,
"type": "string"
},
"generatedAt": {
"minLength": 1,
"type": "string"
},
"moduleInventoryHash": {
"minLength": 1,
"type": "string"
},
"outputs": {
"type": "object",
"additionalProperties": false,
"properties": {
"directory": {
"type": "string",
"minLength": 1
},
"viteManifest": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"moduleInventory": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"routeChunks": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1
},
"additionalProperties": {
"type": "string",
"minLength": 1
}
"minLength": 1,
"type": "string"
},
"propertyNames": {
"minLength": 1,
"type": "string"
},
"type": "object"
},
"runtimeConfigSchema": {
"type": "string",
"minLength": 1
"minLength": 1,
"type": "string"
},
"viteManifest": {
"minLength": 1,
"type": "string"
}
},
"required": [
@@ -99,7 +91,15 @@
"routeChunks",
"runtimeConfigSchema"
],
"additionalProperties": false
"type": "object"
},
"releaseId": {
"minLength": 1,
"type": "string"
},
"schemaVersion": {
"const": 1,
"type": "number"
}
},
"required": [
@@ -112,5 +112,5 @@
"buildContext",
"outputs"
],
"additionalProperties": false
"type": "object"
}
@@ -1,31 +1,49 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://clean-architecture-frontend.local/schemas/dependency-inventory.schema.json",
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"packageManager",
"lockfileSha256",
"dependencyCount",
"directDependencyCount",
"dependencies"
],
"properties": {
"schemaVersion": { "const": 2 },
"packageManager": { "type": "string", "minLength": 1 },
"lockfileSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"dependencyCount": { "type": "integer", "minimum": 1 },
"directDependencyCount": { "type": "integer", "minimum": 1 },
"dependencies": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"dependencies": {
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"direct": {
"type": "boolean"
},
"integrity": {
"pattern": "^sha512-",
"type": "string"
},
"license": {
"minLength": 1,
"type": "string"
},
"name": {
"minLength": 1,
"type": "string"
},
"optional": {
"type": "boolean"
},
"scope": {
"enum": [
"production",
"development"
],
"type": "string"
},
"version": {
"minLength": 1,
"type": "string"
}
},
"required": [
"name",
"version",
@@ -36,25 +54,40 @@
"integrity",
"dependencies"
],
"properties": {
"name": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 },
"direct": { "type": "boolean" },
"scope": {
"enum": ["production", "development"]
},
"optional": { "type": "boolean" },
"license": { "type": "string", "minLength": 1 },
"integrity": {
"type": "string",
"pattern": "^sha512-"
},
"dependencies": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
}
}
}
"type": "object"
},
"type": "array"
},
"dependencyCount": {
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
"directDependencyCount": {
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
"lockfileSha256": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"packageManager": {
"minLength": 1,
"type": "string"
},
"schemaVersion": {
"const": 2,
"type": "number"
}
}
},
"required": [
"schemaVersion",
"packageManager",
"lockfileSha256",
"dependencyCount",
"directDependencyCount",
"dependencies"
],
"type": "object"
}
+253 -42
View File
@@ -1,6 +1,257 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"$defs": {
"__schema0": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"items": {
"$ref": "#/$defs/__schema0"
},
"type": "array"
},
{
"additionalProperties": {
"$ref": "#/$defs/__schema0"
},
"propertyNames": {
"type": "string"
},
"type": "object"
}
]
},
"__schema1": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"items": {
"$ref": "#/$defs/__schema1"
},
"type": "array"
},
{
"additionalProperties": {
"$ref": "#/$defs/__schema1"
},
"propertyNames": {
"type": "string"
},
"type": "object"
}
]
},
"__schema2": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"items": {
"$ref": "#/$defs/__schema2"
},
"type": "array"
},
{
"additionalProperties": {
"$ref": "#/$defs/__schema2"
},
"propertyNames": {
"type": "string"
},
"type": "object"
}
]
}
},
"additionalProperties": false,
"properties": {
"baselineDigest": {
"anyOf": [
{
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
{
"type": "null"
}
]
},
"compatibility": {
"additionalProperties": false,
"properties": {
"changes": {
"items": {
"additionalProperties": false,
"properties": {
"after": {
"$ref": "#/$defs/__schema1"
},
"before": {
"$ref": "#/$defs/__schema0"
},
"changeId": {
"minLength": 1,
"type": "string"
},
"field": {
"minLength": 1,
"type": "string"
},
"impact": {
"enum": [
"none",
"additive",
"behavior-change",
"breaking"
],
"type": "string"
},
"kind": {
"minLength": 1,
"type": "string"
},
"registryId": {
"minLength": 1,
"type": "string"
},
"rowName": {
"minLength": 1,
"type": "string"
}
},
"required": [
"changeId",
"registryId",
"rowName",
"field",
"kind",
"impact"
],
"type": "object"
},
"type": "array"
},
"impact": {
"enum": [
"not-evaluated",
"none",
"additive",
"behavior-change",
"breaking"
],
"type": "string"
}
},
"required": [
"impact",
"changes"
],
"type": "object"
},
"currentDigest": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"failures": {
"items": {
"type": "string"
},
"type": "array"
},
"generatedAt": {
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"type": "string"
},
"registries": {
"items": {
"additionalProperties": false,
"properties": {
"contract": {
"additionalProperties": {
"$ref": "#/$defs/__schema2"
},
"propertyNames": {
"type": "string"
},
"type": "object"
},
"owner": {
"minLength": 1,
"type": "string"
},
"registryId": {
"minLength": 1,
"type": "string"
},
"rowCount": {
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
"rows": {
"additionalProperties": {
"$ref": "#/$defs/__schema2"
},
"propertyNames": {
"type": "string"
},
"type": "object"
},
"source": {
"minLength": 1,
"type": "string"
}
},
"required": [
"registryId",
"owner",
"source",
"rowCount",
"contract",
"rows"
],
"type": "object"
},
"type": "array"
},
"schemaVersion": {
"const": 2,
"type": "number"
}
},
"required": [
"schemaVersion",
"generatedAt",
@@ -10,45 +261,5 @@
"failures",
"registries"
],
"properties": {
"schemaVersion": { "const": 2 },
"generatedAt": { "type": "string", "format": "date-time" },
"baselineDigest": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"currentDigest": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"compatibility": {
"type": "object",
"required": ["impact", "changes"],
"properties": {
"impact": {
"enum": ["none", "additive", "behavior-change", "breaking"]
},
"changes": { "type": "array" }
},
"additionalProperties": false
},
"failures": { "type": "array", "maxItems": 0 },
"registries": {
"type": "array",
"minItems": 11,
"maxItems": 11,
"items": {
"type": "object",
"required": [
"registryId",
"owner",
"source",
"rowCount",
"contract",
"rows"
]
}
}
},
"additionalProperties": false
"type": "object"
}
@@ -1,8 +1,130 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://clean-architecture-frontend.local/schemas/supply-chain-verification.schema.json",
"type": "object",
"additionalProperties": false,
"properties": {
"dependencyDiff": {
"additionalProperties": false,
"properties": {
"added": {
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"changed": {
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"removed": {
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"upgrades": {
"items": {
"additionalProperties": false,
"properties": {
"from": {
"minLength": 1,
"type": "string"
},
"name": {
"minLength": 1,
"type": "string"
},
"to": {
"minLength": 1,
"type": "string"
}
},
"required": [
"name",
"from",
"to"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"added",
"removed",
"changed",
"upgrades"
],
"type": "object"
},
"distSha256": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"failures": {
"items": {
"type": "string"
},
"type": "array"
},
"highRiskReview": {
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"localStatus": {
"enum": [
"PASS",
"FAIL"
],
"type": "string"
},
"lockfileSha256": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"promotionStatus": {
"enum": [
"PASS",
"FAIL_UNVERIFIED"
],
"type": "string"
},
"provenanceAttestationStatus": {
"enum": [
"PASS",
"FAIL_UNVERIFIED"
],
"type": "string"
},
"sbomSha256": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"schemaVersion": {
"const": 1,
"type": "number"
},
"sourceSetSha256": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"vulnerabilityStatus": {
"enum": [
"PASS",
"FAIL",
"FAIL_UNVERIFIED"
],
"type": "string"
}
},
"required": [
"schemaVersion",
"localStatus",
@@ -17,39 +139,5 @@
"provenanceAttestationStatus",
"failures"
],
"properties": {
"schemaVersion": { "const": 1 },
"localStatus": { "enum": ["PASS", "FAIL"] },
"promotionStatus": {
"enum": ["PASS", "FAIL_UNVERIFIED"]
},
"lockfileSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"sourceSetSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"distSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"sbomSha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"dependencyDiff": { "type": "object" },
"highRiskReview": { "type": "array" },
"vulnerabilityStatus": {
"enum": ["PASS", "FAIL", "FAIL_UNVERIFIED"]
},
"provenanceAttestationStatus": {
"enum": ["PASS", "FAIL_UNVERIFIED"]
},
"failures": {
"type": "array",
"items": { "type": "string" }
}
}
"type": "object"
}
+7 -2
View File
@@ -3,7 +3,6 @@ import {
mkdir,
readFile,
readdir,
writeFile,
} from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
@@ -16,6 +15,8 @@ import {
verifyRegistryBaselineApproval,
} from "./lib/registry-compatibility.ts";
import { assertMatchesJsonSchema } from "./lib/json-schema.ts";
import { registrySnapshotArtifactSchema } from "./contracts/release-artifacts.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
type RegistryRow = Record<string, unknown>;
type RegistryRows = Record<string, RegistryRow>;
@@ -675,7 +676,11 @@ if (usesRepositoryBaseline && failures.length === 0) {
}
}
await mkdir(path.dirname(artifactPath), { recursive: true });
await writeFile(artifactPath, `${JSON.stringify(report, null, 2)}\n`);
await writeValidatedJsonArtifact({
path: artifactPath,
schema: registrySnapshotArtifactSchema,
value: report,
});
if (failures.length > 0) {
process.stderr.write(`Registry governance failed:\n${failures.join("\n")}\n`);
+8 -5
View File
@@ -1,10 +1,12 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { mkdir, readFile } from "node:fs/promises";
import {
evaluateFieldBudget,
percentile75,
} from "../src/application/policies/performance-budgets.ts";
import { fieldWebVitalsArtifactSchema } from "./contracts/release-artifacts.ts";
import { validateFieldEvidenceInput } from "./lib/field-vitals-evidence.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
const inputPath =
process.env.FIELD_WEB_VITALS_INPUT ||
@@ -94,10 +96,11 @@ const report = {
};
await mkdir("artifacts/performance", { recursive: true });
await writeFile(
"artifacts/performance/field-web-vitals.json",
`${JSON.stringify(report, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: "artifacts/performance/field-web-vitals.json",
schema: fieldWebVitalsArtifactSchema,
value: report,
});
if (!passed) {
process.stderr.write(
`Field Web Vitals: ${status} (approved threshold decision and valid 28-day production evidence are required)\n`,
+424
View File
@@ -1 +1,425 @@
import { z } from "zod";
export * from "../../src/contracts/release-artifacts.ts";
const nonEmptyString = z.string().min(1);
const timestamp = z.iso.datetime();
const sha256 = z.string().regex(/^[a-f0-9]{64}$/u);
const jsonObject = z.record(z.string(), z.json());
export const moduleInventoryArtifactSchema = z
.object({
schemaVersion: z.literal(1),
chunks: z.array(
z
.object({
fileName: nonEmptyString,
modules: z.array(nonEmptyString),
})
.strict(),
),
})
.strict();
export const jsonSchemaDocumentArtifactSchema = z
.object({
$schema: z.literal("https://json-schema.org/draft/2020-12/schema"),
})
.catchall(z.json());
const dependencyInventoryRowSchema = z
.object({
name: nonEmptyString,
version: nonEmptyString,
direct: z.boolean(),
scope: z.enum(["production", "development"]),
optional: z.boolean(),
license: nonEmptyString,
integrity: z.string().regex(/^sha512-/u),
dependencies: z.array(nonEmptyString),
})
.strict();
export const dependencyInventoryArtifactSchema = z
.object({
schemaVersion: z.literal(2),
packageManager: nonEmptyString,
lockfileSha256: sha256,
dependencyCount: z.int().nonnegative(),
directDependencyCount: z.int().nonnegative(),
dependencies: z.array(dependencyInventoryRowSchema),
})
.strict()
.superRefine((inventory, context) => {
if (inventory.dependencyCount !== inventory.dependencies.length) {
context.addIssue({
code: "custom",
path: ["dependencyCount"],
message: "must equal dependencies.length",
});
}
const actualDirect = inventory.dependencies.filter(
(dependency) => dependency.direct,
).length;
if (inventory.directDependencyCount !== actualDirect) {
context.addIssue({
code: "custom",
path: ["directDependencyCount"],
message: "must equal the number of direct dependencies",
});
}
});
const dependencyUpgradeSchema = z
.object({
name: nonEmptyString,
from: nonEmptyString,
to: nonEmptyString,
})
.strict();
export const dependencyDiffSchema = z
.object({
added: z.array(nonEmptyString),
removed: z.array(nonEmptyString),
changed: z.array(nonEmptyString),
upgrades: z.array(dependencyUpgradeSchema),
})
.strict();
export const supplyChainVerificationArtifactSchema = z
.object({
schemaVersion: z.literal(1),
localStatus: z.enum(["PASS", "FAIL"]),
promotionStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]),
lockfileSha256: sha256,
sourceSetSha256: sha256,
distSha256: sha256,
sbomSha256: sha256,
dependencyDiff: dependencyDiffSchema,
highRiskReview: z.array(nonEmptyString),
vulnerabilityStatus: z.enum(["PASS", "FAIL", "FAIL_UNVERIFIED"]),
provenanceAttestationStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]),
failures: z.array(z.string()),
})
.strict();
const registryChangeSchema = z
.object({
changeId: nonEmptyString,
registryId: nonEmptyString,
rowName: nonEmptyString,
field: nonEmptyString,
kind: nonEmptyString,
impact: z.enum(["none", "additive", "behavior-change", "breaking"]),
before: z.json().optional(),
after: z.json().optional(),
})
.strict();
const registryArtifactRowSchema = z
.object({
registryId: nonEmptyString,
owner: nonEmptyString,
source: nonEmptyString,
rowCount: z.int().nonnegative(),
contract: jsonObject,
rows: jsonObject,
})
.strict();
export const registrySnapshotArtifactSchema = z
.object({
schemaVersion: z.literal(2),
generatedAt: timestamp,
baselineDigest: sha256.nullable(),
currentDigest: sha256,
compatibility: z
.object({
impact: z.enum([
"not-evaluated",
"none",
"additive",
"behavior-change",
"breaking",
]),
changes: z.array(registryChangeSchema),
})
.strict(),
failures: z.array(z.string()),
registries: z.array(registryArtifactRowSchema),
})
.strict();
const outputDigestSchema = z
.object({
path: nonEmptyString,
bytes: z.int().nonnegative(),
gzipBytes: z.int().nonnegative(),
sha256,
})
.strict();
export const bundlePerformanceArtifactSchema = z
.object({
schemaVersion: z.literal(1),
generatedAt: timestamp,
context: z
.object({
nodeVersion: nonEmptyString,
packageManager: nonEmptyString,
runnerImage: nonEmptyString,
})
.strict(),
outputs: z.array(outputDigestSchema).min(1),
})
.strict();
const cyclonedxComponentSchema = z
.object({
type: z.literal("library"),
"bom-ref": nonEmptyString,
name: nonEmptyString,
version: nonEmptyString,
scope: z.enum(["optional", "required"]),
hashes: z.array(
z.object({ alg: z.literal("SHA-512"), content: nonEmptyString }).strict(),
),
licenses: z.array(
z.object({ expression: nonEmptyString }).strict(),
),
properties: z.array(
z.object({ name: nonEmptyString, value: nonEmptyString }).strict(),
),
})
.strict();
export const sbomArtifactSchema = z
.object({
bomFormat: z.literal("CycloneDX"),
specVersion: z.literal("1.6"),
serialNumber: nonEmptyString,
version: z.literal(1),
metadata: z
.object({
component: z
.object({
type: z.literal("application"),
name: nonEmptyString,
version: nonEmptyString,
})
.strict(),
properties: z.array(
z.object({ name: nonEmptyString, value: nonEmptyString }).strict(),
),
})
.strict(),
components: z.array(cyclonedxComponentSchema),
dependencies: z.array(
z
.object({ ref: nonEmptyString, dependsOn: z.array(nonEmptyString) })
.strict(),
),
})
.strict();
export const provenanceArtifactSchema = z
.object({
_type: z.literal("https://in-toto.io/Statement/v1"),
subject: z
.array(
z
.object({
name: z.literal("dist"),
digest: z.object({ sha256 }).strict(),
})
.strict(),
)
.length(1),
predicateType: z.literal("https://slsa.dev/provenance/v1"),
predicate: z
.object({
buildDefinition: z
.object({
buildType: nonEmptyString,
externalParameters: jsonObject,
internalParameters: jsonObject,
resolvedDependencies: z.array(
z
.object({ uri: nonEmptyString, digest: z.object({ sha256 }).strict() })
.strict(),
),
})
.strict(),
runDetails: z
.object({
builder: z.object({ id: nonEmptyString }).strict(),
metadata: z.object({ invocationId: nonEmptyString }).strict(),
})
.strict(),
materials: z
.object({ lockfileSha256: sha256, sourceSetSha256: sha256, sbomSha256: sha256 })
.strict(),
})
.strict(),
})
.strict();
export const dependencyDiffArtifactSchema = z
.object({
schemaVersion: z.literal(2),
baselineDigest: sha256.nullable(),
currentDigest: sha256,
...dependencyDiffSchema.shape,
highRisk: z.array(nonEmptyString),
reviewFailures: z.array(z.string()),
})
.strict();
export const licenseReportArtifactSchema = z
.object({
schemaVersion: z.literal(1),
status: z.enum(["PASS", "FAIL"]),
dependencyCount: z.int().nonnegative(),
results: z.array(
z
.object({
package: nonEmptyString,
license: nonEmptyString,
passed: z.boolean(),
reason: z.string().nullable(),
})
.strict(),
),
failures: z.array(z.string()),
})
.strict();
export const vulnerabilityReportArtifactSchema = z
.object({
schemaVersion: z.literal(1),
provider: nonEmptyString,
scannedLockfileSha256: sha256,
status: z.enum(["PASS", "FAIL", "FAIL_UNVERIFIED"]),
findings: z.array(jsonObject),
exceptionsApplied: z.array(jsonObject),
failures: z.array(z.string()),
blocking: z.array(z.string()),
})
.strict();
export const fieldWebVitalsArtifactSchema = z
.object({
schemaVersion: z.literal(1),
generatedAt: timestamp,
window: z
.object({ days: z.literal(28), start: timestamp, end: timestamp })
.strict(),
context: z
.object({
source: nonEmptyString,
sourceSystem: z.string().nullable(),
exportId: z.string().nullable(),
network: z.literal("production-real-user"),
routeAggregation: z.literal("route-id-only"),
releaseId: z.string().nullable(),
privacyApprovalRef: z.string().nullable(),
thresholdDecisionRef: z.string().nullable(),
validationFailures: z.array(z.string()),
})
.strict(),
metrics: z
.object({
p75LcpMs: z.number().finite().nonnegative().nullable(),
p75Cls: z.number().finite().nonnegative().nullable(),
p75InpMs: z.number().finite().nonnegative().nullable(),
})
.strict(),
thresholds: z
.object({
p75LcpMs: z.number().finite().nonnegative(),
p75Cls: z.number().finite().nonnegative(),
p75InpMs: z.number().finite().nonnegative(),
minimumEligibleSamples: z.int().positive().nullable(),
})
.strict(),
eligibility: z
.object({
consentRequired: z.literal(true),
totalSamples: z.int().nonnegative(),
eligibleSamples: z.int().nonnegative(),
minimumEligibleSamples: z.int().positive().nullable(),
routeSamples: z.record(z.string(), z.int().nonnegative()),
})
.strict(),
status: z.enum(["PASS", "FAIL_THRESHOLD", "FAIL_UNVERIFIED"]),
passed: z.boolean(),
})
.strict();
export const labPerformanceArtifactSchema = z
.object({
schemaVersion: z.literal(1),
generatedAt: timestamp,
context: jsonObject,
metrics: jsonObject,
thresholds: jsonObject,
fixtures: z.array(
z.object({ name: nonEmptyString, passed: z.boolean() }).strict(),
),
passed: z.boolean(),
})
.strict();
export const releaseVerificationArtifactSchema = z
.object({
schemaVersion: z.literal(1),
generatedAt: timestamp,
artifact: z
.object({
checked: z.boolean(),
compatible: z.boolean(),
mismatches: z.array(z.string()),
releaseId: nonEmptyString,
})
.strict(),
fixtures: z.array(
z
.object({
name: nonEmptyString,
expectedCompatible: z.boolean(),
actualCompatible: z.boolean(),
mismatches: z.array(z.string()),
passed: z.boolean(),
})
.strict(),
),
passed: z.boolean(),
})
.strict();
export const runbookRecordArtifactSchema = z
.object({
schemaVersion: z.literal(1),
runbookId: z.string().regex(/^FE-RB-00[1-5]$/u),
releaseId: nonEmptyString,
drillTimestamp: timestamp,
triggerInjected: nonEmptyString,
triggerAsserted: z.boolean(),
containmentAsserted: z.boolean(),
escalationPathAsserted: z.boolean(),
recoveryAssertions: z.array(
z
.object({
assertion: nonEmptyString,
evidence: nonEmptyString,
passed: z.boolean(),
})
.strict(),
),
negativeFixtureFailedAsExpected: z.boolean(),
windowObservedBucket: nonEmptyString,
providerVerificationRequired: z.boolean(),
passed: z.boolean(),
})
.strict();
+8 -5
View File
@@ -1,4 +1,4 @@
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { access, mkdir, readFile } from "node:fs/promises";
import { pathToFileURL } from "node:url";
import { shouldRetry } from "../src/adapters/http/retry-policy.ts";
@@ -15,7 +15,9 @@ import {
} from "../src/contracts/release-artifacts.ts";
import { projectTelemetryEvent } from "../src/contracts/telemetry.ts";
import { EXPECTED_CONTRACT_SET_PACKAGES } from "../src/features/installed-contract-contributions.ts";
import { runbookRecordArtifactSchema } from "./contracts/release-artifacts.ts";
import { verifyReleaseRuntimeCoherence } from "./lib/release-runtime-coherence.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
type RecoveryAssertion = Readonly<{
assertion: string;
@@ -354,10 +356,11 @@ async function main(): Promise<void> {
};
const artifactDirectory = `artifacts/runbooks/${runbookId}`;
await mkdir(artifactDirectory, { recursive: true });
await writeFile(
`${artifactDirectory}/record.json`,
`${JSON.stringify(record, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: `${artifactDirectory}/record.json`,
schema: runbookRecordArtifactSchema,
value: record,
});
if (!passed) {
process.stderr.write(`${runbookId} drill failed.\n`);
process.exit(1);
+131
View File
@@ -0,0 +1,131 @@
import { mkdir, readFile } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { z } from "zod";
import {
buildManifestArtifactSchema,
dependencyInventoryArtifactSchema,
jsonSchemaDocumentArtifactSchema,
registrySnapshotArtifactSchema,
supplyChainVerificationArtifactSchema,
} from "./contracts/release-artifacts.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
const ARTIFACT_SCHEMA_DEFINITIONS = Object.freeze([
{
relativePath: "schemas/artifacts/build-manifest.schema.json",
id: undefined,
schema: buildManifestArtifactSchema,
},
{
relativePath: "schemas/artifacts/dependency-inventory.schema.json",
id: "https://clean-architecture-frontend.local/schemas/dependency-inventory.schema.json",
schema: dependencyInventoryArtifactSchema,
},
{
relativePath: "schemas/artifacts/registry-snapshot.schema.json",
id: undefined,
schema: registrySnapshotArtifactSchema,
},
{
relativePath: "schemas/artifacts/supply-chain-verification.schema.json",
id: "https://clean-architecture-frontend.local/schemas/supply-chain-verification.schema.json",
schema: supplyChainVerificationArtifactSchema,
},
] as const);
type GenerateArtifactSchemasOptions = Readonly<{
root?: string;
check?: boolean;
}>;
function canonicalize(value: unknown): unknown {
if (Array.isArray(value)) return value.map(canonicalize);
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, item]) => [key, canonicalize(item)]),
);
}
return value;
}
function generatedSchema(
definition: (typeof ARTIFACT_SCHEMA_DEFINITIONS)[number],
): Readonly<Record<string, unknown>> {
const schema = z.toJSONSchema(definition.schema, {
target: "draft-2020-12",
});
return canonicalize({
...schema,
...(definition.id === undefined ? {} : { $id: definition.id }),
}) as Readonly<Record<string, unknown>>;
}
export async function generateArtifactSchemas(
options: GenerateArtifactSchemasOptions = {},
): Promise<void> {
const root = path.resolve(options.root ?? process.cwd());
const documents = ARTIFACT_SCHEMA_DEFINITIONS.map((definition) => ({
definition,
document: generatedSchema(definition),
}));
const drift: string[] = [];
for (const { definition, document } of documents) {
const destination = path.join(root, definition.relativePath);
const validatedDocument = jsonSchemaDocumentArtifactSchema.parse(document);
const expected = `${JSON.stringify(validatedDocument, null, 2)}\n`;
if (options.check) {
let actual: string | null = null;
try {
actual = await readFile(destination, "utf8");
} catch {
// A missing or unreadable checked-in schema is drift.
}
if (actual !== expected) drift.push(definition.relativePath);
continue;
}
await mkdir(path.dirname(destination), { recursive: true });
await writeValidatedJsonArtifact({
path: destination,
schema: jsonSchemaDocumentArtifactSchema,
value: document,
});
}
if (drift.length > 0) {
throw new Error(`Artifact JSON Schema drift:\n- ${drift.join("\n- ")}`);
}
}
function argumentValue(name: string): string | undefined {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] : undefined;
}
const invokedPath = process.argv[1];
if (
invokedPath !== undefined &&
import.meta.url === pathToFileURL(invokedPath).href
) {
try {
await generateArtifactSchemas({
root: argumentValue("--root"),
check: process.argv.includes("--check"),
});
process.stdout.write(
process.argv.includes("--check")
? "Artifact JSON Schemas: PASS\n"
: "Artifact JSON Schemas: GENERATED\n",
);
} catch (error) {
process.stderr.write(
`${error instanceof Error ? error.message : String(error)}\n`,
);
process.exitCode = 1;
}
}
+40 -42
View File
@@ -1,5 +1,5 @@
import { createHash } from "node:crypto";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { mkdir, readFile, rm } from "node:fs/promises";
import process from "node:process";
import { z } from "zod";
@@ -9,14 +9,17 @@ import {
} from "../src/features/installed-feature-contracts.ts";
import {
buildManifestArtifactSchema,
jsonSchemaDocumentArtifactSchema,
moduleInventoryArtifactSchema,
releaseManifestV2ArtifactSchema,
runtimeConfigV2ArtifactSchema,
} from "../src/contracts/release-artifacts.ts";
} from "./contracts/release-artifacts.ts";
import { buildContractSet } from "./generate-contract-set.ts";
import {
assertCiBuildEnvironment,
buildDate,
} from "./lib/build-environment.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
assertCiBuildEnvironment(process.env);
type ViteManifestEntry = Readonly<{
@@ -41,12 +44,15 @@ const moduleInventory = await readFile(
"dist/.vite/module-inventory.json",
"utf8",
);
parseModuleInventory(JSON.parse(moduleInventory));
const moduleInventoryDocument = moduleInventoryArtifactSchema.parse(
JSON.parse(moduleInventory),
);
const moduleInventoryBytes = `${JSON.stringify(moduleInventoryDocument, null, 2)}\n`;
const assetManifestHash = createHash("sha256")
.update(viteManifest)
.digest("hex");
const moduleInventoryHash = createHash("sha256")
.update(moduleInventory)
.update(moduleInventoryBytes)
.digest("hex");
const runtimeConfig = runtimeConfigV2ArtifactSchema.parse(
JSON.parse(await readFile("dist/config.json", "utf8")),
@@ -108,28 +114,37 @@ const releaseManifest = releaseManifestV2ArtifactSchema.parse({
await mkdir("artifacts/release", { recursive: true });
await mkdir("artifacts/quality", { recursive: true });
await writeFile(
"artifacts/quality/vite-module-inventory.json",
moduleInventory,
);
await writeValidatedJsonArtifact({
path: "artifacts/quality/vite-module-inventory.json",
schema: moduleInventoryArtifactSchema,
value: moduleInventoryDocument,
});
await rm("dist/.vite/module-inventory.json");
await writeFile("dist/config.json", `${JSON.stringify(runtimeConfig, null, 2)}\n`);
await writeFile(
"dist/release-manifest.json",
`${JSON.stringify(releaseManifest, null, 2)}\n`,
);
await writeFile(
"dist/runtime-config.schema.json",
`${JSON.stringify(runtimeConfigJsonSchema, null, 2)}\n`,
);
await writeFile(
"artifacts/release/runtime-config.schema.json",
`${JSON.stringify(runtimeConfigJsonSchema, null, 2)}\n`,
);
await writeFile(
"artifacts/release/build-manifest.json",
`${JSON.stringify(manifest, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: "dist/config.json",
schema: runtimeConfigV2ArtifactSchema,
value: runtimeConfig,
});
await writeValidatedJsonArtifact({
path: "dist/release-manifest.json",
schema: releaseManifestV2ArtifactSchema,
value: releaseManifest,
});
await writeValidatedJsonArtifact({
path: "dist/runtime-config.schema.json",
schema: jsonSchemaDocumentArtifactSchema,
value: runtimeConfigJsonSchema,
});
await writeValidatedJsonArtifact({
path: "artifacts/release/runtime-config.schema.json",
schema: jsonSchemaDocumentArtifactSchema,
value: runtimeConfigJsonSchema,
});
await writeValidatedJsonArtifact({
path: "artifacts/release/build-manifest.json",
schema: buildManifestArtifactSchema,
value: manifest,
});
function parsePackageMetadata(value: unknown): Readonly<{
version: string;
@@ -165,23 +180,6 @@ function parseViteManifest(
return entries;
}
function parseModuleInventory(value: unknown): void {
if (
!isRecord(value) ||
value.schemaVersion !== 1 ||
!Array.isArray(value.chunks) ||
value.chunks.some(
(chunk) =>
!isRecord(chunk) ||
typeof chunk.fileName !== "string" ||
!Array.isArray(chunk.modules) ||
chunk.modules.some((moduleId) => typeof moduleId !== "string"),
)
) {
throw new TypeError("Vite module inventory is invalid");
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
+77 -67
View File
@@ -10,6 +10,16 @@ import {
} from "node:fs/promises";
import path from "node:path";
import {
bundlePerformanceArtifactSchema,
dependencyDiffArtifactSchema,
dependencyInventoryArtifactSchema,
licenseReportArtifactSchema,
provenanceArtifactSchema,
sbomArtifactSchema,
supplyChainVerificationArtifactSchema,
vulnerabilityReportArtifactSchema,
} from "./contracts/release-artifacts.ts";
import {
diffDependencyInventories,
flattenPnpmDependencyTree,
@@ -22,6 +32,7 @@ import {
verifySupplyChainCoherence,
type DependencyInventoryDiff,
} from "./lib/supply-chain.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
type Document = Record<string, unknown>;
@@ -428,81 +439,80 @@ const verification = {
: "FAIL_UNVERIFIED",
failures: localFailures,
};
const bundleReport = {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
context: {
nodeVersion: process.version,
packageManager: String(packageJson.packageManager ?? ""),
runnerImage:
process.env.CI_RUNNER_IMAGE ?? `${process.platform}-${process.arch}`,
},
outputs,
};
const dependencyDiffReport = {
schemaVersion: 2,
baselineDigest: baseline ? supplyChainDigest(baseline) : null,
currentDigest: supplyChainDigest(inventory),
...dependencyDiff,
highRisk: reviewResult.highRisk,
reviewFailures: reviewResult.failures,
};
const licenseReport = {
schemaVersion: 1,
status: licenseResult.passed ? "PASS" : "FAIL",
dependencyCount: inventory.dependencyCount,
results: licenseResult.results,
failures: licenseResult.failures,
};
await mkdir("artifacts/performance", { recursive: true });
await mkdir("artifacts/release", { recursive: true });
await mkdir("artifacts/security", { recursive: true });
await writeFile(
"artifacts/performance/bundle.json",
`${JSON.stringify(
{
schemaVersion: 1,
generatedAt: new Date().toISOString(),
context: {
nodeVersion: process.version,
packageManager: String(packageJson.packageManager ?? ""),
runnerImage:
process.env.CI_RUNNER_IMAGE ?? `${process.platform}-${process.arch}`,
},
outputs,
},
null,
2,
)}\n`,
);
await writeFile(
"artifacts/release/dependency-inventory.json",
`${JSON.stringify(inventory, null, 2)}\n`,
);
await writeFile(
"artifacts/release/sbom.cdx.json",
`${JSON.stringify(sbom, null, 2)}\n`,
);
await writeFile(
"artifacts/release/provenance.json",
`${JSON.stringify(provenance, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: "artifacts/performance/bundle.json",
schema: bundlePerformanceArtifactSchema,
value: bundleReport,
});
await writeValidatedJsonArtifact({
path: "artifacts/release/dependency-inventory.json",
schema: dependencyInventoryArtifactSchema,
value: inventory,
});
await writeValidatedJsonArtifact({
path: "artifacts/release/sbom.cdx.json",
schema: sbomArtifactSchema,
value: sbom,
});
await writeValidatedJsonArtifact({
path: "artifacts/release/provenance.json",
schema: provenanceArtifactSchema,
value: provenance,
});
await writeFile(
"artifacts/release/checksums.txt",
`${outputs.map((output) => `${output.sha256} ${output.path}`).join("\n")}\n`,
);
await writeFile(
"artifacts/security/dependency-diff.json",
`${JSON.stringify(
{
schemaVersion: 2,
baselineDigest: baseline ? supplyChainDigest(baseline) : null,
currentDigest: supplyChainDigest(inventory),
...dependencyDiff,
highRisk: reviewResult.highRisk,
reviewFailures: reviewResult.failures,
},
null,
2,
)}\n`,
);
await writeFile(
"artifacts/security/license-report.json",
`${JSON.stringify(
{
schemaVersion: 1,
status: licenseResult.passed ? "PASS" : "FAIL",
dependencyCount: inventory.dependencyCount,
results: licenseResult.results,
failures: licenseResult.failures,
},
null,
2,
)}\n`,
);
await writeFile(
"artifacts/security/vulnerability-report.json",
`${JSON.stringify(vulnerabilityReport, null, 2)}\n`,
);
await writeFile(
"artifacts/security/supply-chain-verification.json",
`${JSON.stringify(verification, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: "artifacts/security/dependency-diff.json",
schema: dependencyDiffArtifactSchema,
value: dependencyDiffReport,
});
await writeValidatedJsonArtifact({
path: "artifacts/security/license-report.json",
schema: licenseReportArtifactSchema,
value: licenseReport,
});
await writeValidatedJsonArtifact({
path: "artifacts/security/vulnerability-report.json",
schema: vulnerabilityReportArtifactSchema,
value: vulnerabilityReport,
});
await writeValidatedJsonArtifact({
path: "artifacts/security/supply-chain-verification.json",
schema: supplyChainVerificationArtifactSchema,
value: verification,
});
if (!localPassed) {
process.stderr.write(
+85
View File
@@ -0,0 +1,85 @@
import { randomUUID } from "node:crypto";
import {
open as openFile,
rename as renameFile,
rm as removeFile,
} from "node:fs/promises";
import path from "node:path";
import type { z } from "zod";
export type ValidatedJsonArtifactInput = Readonly<{
path: string;
schema: z.ZodType;
value: unknown;
}>;
export type ValidatedJsonArtifactFileSystem = Readonly<{
open: (path: string, flags: "wx") => Promise<{
writeFile(data: string, encoding: "utf8"): Promise<unknown>;
close(): Promise<unknown>;
}>;
rename: (source: string, destination: string) => Promise<unknown>;
rm: (path: string, options: Readonly<{ force: true }>) => Promise<unknown>;
}>;
type ValidatedJsonArtifactWriterDependencies = Readonly<{
createNonce?: () => string;
fileSystem?: ValidatedJsonArtifactFileSystem;
}>;
const defaultFileSystem: ValidatedJsonArtifactFileSystem = Object.freeze({
open: async (target, flags) => openFile(target, flags),
rename: async (source, destination) => renameFile(source, destination),
rm: async (target, options) => removeFile(target, options),
});
/**
* Builds a writer whose only publish operation is an atomic sibling rename.
* Dependency injection is limited to the file-system boundary so failure
* ownership can be verified without exposing a caller-selected cleanup path.
*/
export function createValidatedJsonArtifactWriter(
dependencies: ValidatedJsonArtifactWriterDependencies = {},
) {
const createNonce = dependencies.createNonce ?? randomUUID;
const fileSystem = dependencies.fileSystem ?? defaultFileSystem;
return async function writeArtifact(
input: ValidatedJsonArtifactInput,
): Promise<void> {
const parsed = input.schema.parse(input.value);
const serialized = JSON.stringify(parsed, null, 2);
if (serialized === undefined) {
throw new TypeError("Validated JSON artifact is not serializable");
}
const temporaryPath = path.join(
path.dirname(input.path),
`.${path.basename(input.path)}.${createNonce()}.tmp`,
);
let ownsTemporaryFile = false;
try {
const handle = await fileSystem.open(temporaryPath, "wx");
ownsTemporaryFile = true;
try {
await handle.writeFile(`${serialized}\n`, "utf8");
} finally {
await handle.close();
}
await fileSystem.rename(temporaryPath, input.path);
} catch (error) {
if (ownsTemporaryFile) {
try {
await fileSystem.rm(temporaryPath, { force: true });
} catch {
// Preserve the publishing failure; cleanup is confined to our nonce.
}
}
throw error;
}
};
}
export const writeValidatedJsonArtifact =
createValidatedJsonArtifactWriter();
+16 -17
View File
@@ -1,5 +1,5 @@
import { spawn } from "node:child_process";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { mkdir, readFile } from "node:fs/promises";
import { performance } from "node:perf_hooks";
import process from "node:process";
@@ -7,6 +7,8 @@ import { chromium } from "@playwright/test";
import { evaluateLabBudget } from "../src/application/policies/performance-budgets.ts";
import { ROUTE_REGISTRY } from "../src/features/installed-feature-contracts.ts";
import { labPerformanceArtifactSchema } from "./contracts/release-artifacts.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
type ContractPerformanceEvidence = {
lcpMs: number;
@@ -142,22 +144,19 @@ try {
];
const passed = result.passed && fixtures.every((fixture) => fixture.passed);
await mkdir("artifacts/performance", { recursive: true });
await writeFile(
"artifacts/performance/lab.json",
`${JSON.stringify(
{
schemaVersion: 1,
generatedAt: new Date().toISOString(),
context: contextMetadata,
metrics,
thresholds,
fixtures,
passed,
},
null,
2,
)}\n`,
);
await writeValidatedJsonArtifact({
path: "artifacts/performance/lab.json",
schema: labPerformanceArtifactSchema,
value: {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
context: contextMetadata,
metrics,
thresholds,
fixtures,
passed,
},
});
if (!passed) {
throw new Error(`Lab performance failed: ${JSON.stringify(metrics)}`);
}
+8 -5
View File
@@ -1,5 +1,5 @@
import { createHash } from "node:crypto";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { mkdir, readFile } from "node:fs/promises";
import { pathToFileURL } from "node:url";
import {
@@ -23,6 +23,8 @@ import {
} from "../src/features/installed-feature-contracts.ts";
import { assertMatchesJsonSchema } from "./lib/json-schema.ts";
import { verifyReleaseRuntimeCoherence } from "./lib/release-runtime-coherence.ts";
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
import { releaseVerificationArtifactSchema } from "./contracts/release-artifacts.ts";
type CoherenceFixture = Readonly<{
name: string;
@@ -212,10 +214,11 @@ const report = {
};
await mkdir("artifacts/release", { recursive: true });
await writeFile(
"artifacts/release/verification.json",
`${JSON.stringify(report, null, 2)}\n`,
);
await writeValidatedJsonArtifact({
path: "artifacts/release/verification.json",
schema: releaseVerificationArtifactSchema,
value: report,
});
if (!passed) {
process.stderr.write(
+147 -2
View File
@@ -1,9 +1,28 @@
import { readFile } from "node:fs/promises";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { generateArtifactSchemas } from "../../scripts/generate-artifact-schemas.ts";
import { assertMatchesJsonSchema } from "../../scripts/lib/json-schema.ts";
const temporaryDirectories: string[] = [];
async function temporaryDirectory(): Promise<string> {
const directory = await mkdtemp(path.join(tmpdir(), "artifact-schemas-"));
temporaryDirectories.push(directory);
return directory;
}
afterEach(async () => {
await Promise.all(
temporaryDirectories.splice(0).map((directory) =>
rm(directory, { force: true, recursive: true }),
),
);
});
async function json(path: string): Promise<unknown> {
return JSON.parse(await readFile(path, "utf8")) as unknown;
}
@@ -55,4 +74,130 @@ describe("checked-in JSON Schema execution", () => {
assertMatchesJsonSchema(schema, catalog, "optional recipe catalog"),
).not.toThrow();
});
it("generates deterministic draft 2020-12 artifact schemas and detects drift", async () => {
const root = await temporaryDirectory();
await generateArtifactSchemas({ root });
const generatedPath = path.join(
root,
"schemas/artifacts/dependency-inventory.schema.json",
);
const generated = await readFile(generatedPath, "utf8");
expect(generated.endsWith("\n")).toBe(true);
expect(JSON.parse(generated)).toMatchObject({
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
additionalProperties: false,
});
await generateArtifactSchemas({ root, check: true });
await writeFile(generatedPath, `${generated.trimEnd()} \n`, "utf8");
await expect(
generateArtifactSchemas({ root, check: true }),
).rejects.toThrow(/dependency-inventory\.schema\.json/u);
});
it.each([
{
name: "dependency inventory",
path: "schemas/artifacts/dependency-inventory.schema.json",
value: {
schemaVersion: 2,
packageManager: "pnpm@11.17.0",
lockfileSha256: "a".repeat(64),
dependencyCount: 1,
directDependencyCount: 1,
dependencies: [
{
name: "zod",
version: "4.4.3",
direct: true,
scope: "production",
optional: false,
license: "MIT",
integrity: `sha512-${"a".repeat(86)}`,
dependencies: [],
},
],
},
},
{
name: "registry snapshot",
path: "schemas/artifacts/registry-snapshot.schema.json",
value: {
schemaVersion: 2,
generatedAt: "2026-08-01T00:00:00.000Z",
baselineDigest: "b".repeat(64),
currentDigest: "c".repeat(64),
compatibility: { impact: "none", changes: [] },
failures: [],
registries: Array.from({ length: 11 }, (_, index) => ({
registryId: `registry-${index}`,
owner: "platform",
source: `src/registry-${index}.ts`,
rowCount: 0,
contract: {},
rows: {},
})),
},
},
{
name: "supply-chain verification",
path: "schemas/artifacts/supply-chain-verification.schema.json",
value: {
schemaVersion: 1,
localStatus: "PASS",
promotionStatus: "FAIL_UNVERIFIED",
lockfileSha256: "d".repeat(64),
sourceSetSha256: "e".repeat(64),
distSha256: "f".repeat(64),
sbomSha256: "0".repeat(64),
dependencyDiff: {
added: [],
removed: [],
changed: [],
upgrades: [],
},
highRiskReview: [],
vulnerabilityStatus: "FAIL_UNVERIFIED",
provenanceAttestationStatus: "FAIL_UNVERIFIED",
failures: [],
},
},
])("accepts $name and rejects undeclared top-level fields", async (fixture) => {
const schema = await json(fixture.path);
expect(() =>
assertMatchesJsonSchema(schema, fixture.value, fixture.name),
).not.toThrow();
expect(() =>
assertMatchesJsonSchema(
schema,
{ ...fixture.value, undocumented: true },
fixture.name,
),
).toThrow(/checked-in JSON Schema/u);
if (fixture.name === "dependency inventory") {
const dependency = fixture.value.dependencies?.[0];
if (dependency === undefined) {
throw new TypeError("dependency inventory fixture is incomplete");
}
expect(() =>
assertMatchesJsonSchema(
schema,
{
...fixture.value,
dependencies: [
{
...dependency,
undocumented: true,
},
],
},
fixture.name,
),
).toThrow(/checked-in JSON Schema/u);
}
});
});
+245
View File
@@ -1,10 +1,18 @@
import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import {
dependencyInventoryArtifactSchema,
fieldWebVitalsArtifactSchema,
jsonSchemaDocumentArtifactSchema,
labPerformanceArtifactSchema,
parseBuildManifestArtifact,
parseReleaseArtifact,
parseRuntimeConfigArtifact,
projectReleaseTokens,
registrySnapshotArtifactSchema,
supplyChainVerificationArtifactSchema,
} from "../../scripts/contracts/release-artifacts.ts";
const EMPTY_CONTRACT_SET_DIGEST =
@@ -126,4 +134,241 @@ describe("release artifact contracts", () => {
}),
).not.toHaveProperty("API_CONTRACT_VERSION");
});
it("enforces dependency inventory counts at the executable writer boundary", () => {
const inventory = {
schemaVersion: 2,
packageManager: "pnpm@11.17.0",
lockfileSha256: "a".repeat(64),
dependencyCount: 1,
directDependencyCount: 1,
dependencies: [
{
name: "zod",
version: "4.4.3",
direct: true,
scope: "production",
optional: false,
license: "MIT",
integrity: `sha512-${"a".repeat(86)}`,
dependencies: [],
},
],
} as const;
expect(dependencyInventoryArtifactSchema.parse(inventory)).toEqual(
inventory,
);
expect(() =>
dependencyInventoryArtifactSchema.parse({
...inventory,
dependencyCount: 2,
}),
).toThrow();
});
it("rejects undeclared supply-chain verification evidence", () => {
const verification = {
schemaVersion: 1,
localStatus: "PASS",
promotionStatus: "FAIL_UNVERIFIED",
lockfileSha256: "a".repeat(64),
sourceSetSha256: "b".repeat(64),
distSha256: "c".repeat(64),
sbomSha256: "d".repeat(64),
dependencyDiff: {
added: [],
removed: [],
changed: [],
upgrades: [],
},
highRiskReview: [],
vulnerabilityStatus: "FAIL_UNVERIFIED",
provenanceAttestationStatus: "FAIL_UNVERIFIED",
failures: [],
} as const;
expect(supplyChainVerificationArtifactSchema.parse(verification)).toEqual(
verification,
);
expect(() =>
supplyChainVerificationArtifactSchema.parse({
...verification,
undocumented: true,
}),
).toThrow();
});
it("preserves no-baseline and failure registry evidence", () => {
const failureEvidence = {
schemaVersion: 2,
generatedAt: "2026-08-01T00:00:00.000Z",
baselineDigest: null,
currentDigest: "a".repeat(64),
compatibility: { impact: "not-evaluated", changes: [] },
failures: ["missing registry source"],
registries: [],
} as const;
expect(registrySnapshotArtifactSchema.parse(failureEvidence)).toEqual(
failureEvidence,
);
});
it("preserves unverified field evidence when no samples are eligible", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
window: {
days: 28,
start: "2026-07-04T00:00:00.000Z",
end: "2026-08-01T00:00:00.000Z",
},
context: {
source: "config/performance/field-input.example.json",
sourceSystem: null,
exportId: null,
network: "production-real-user",
routeAggregation: "route-id-only",
releaseId: null,
privacyApprovalRef: null,
thresholdDecisionRef: null,
validationFailures: ["input: invalid evidence"],
},
metrics: { p75LcpMs: null, p75Cls: null, p75InpMs: null },
thresholds: {
p75LcpMs: 2_500,
p75Cls: 0.1,
p75InpMs: 200,
minimumEligibleSamples: null,
},
eligibility: {
consentRequired: true,
totalSamples: 0,
eligibleSamples: 0,
minimumEligibleSamples: null,
routeSamples: {},
},
status: "FAIL_UNVERIFIED",
passed: false,
} as const;
expect(fieldWebVitalsArtifactSchema.parse(report)).toEqual(report);
});
it("preserves verified field evidence that fails an approved threshold", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
window: {
days: 28,
start: "2026-07-04T00:00:00.000Z",
end: "2026-08-01T00:00:00.000Z",
},
context: {
source: "provider.json",
sourceSystem: "provider",
exportId: "export-1",
network: "production-real-user",
routeAggregation: "route-id-only",
releaseId: "release-1",
privacyApprovalRef: "privacy-1",
thresholdDecisionRef: "decision-1",
validationFailures: [],
},
metrics: { p75LcpMs: 2_501, p75Cls: 0.1, p75InpMs: 200 },
thresholds: {
p75LcpMs: 2_500,
p75Cls: 0.1,
p75InpMs: 200,
minimumEligibleSamples: 1,
},
eligibility: {
consentRequired: true,
totalSamples: 1,
eligibleSamples: 1,
minimumEligibleSamples: 1,
routeSamples: { APP_HOME: 1 },
},
status: "FAIL_THRESHOLD",
passed: false,
} as const;
expect(fieldWebVitalsArtifactSchema.parse(report)).toEqual(report);
});
it("accepts the lab writer's nested browser context as JSON evidence", () => {
const report = {
schemaVersion: 1,
generatedAt: "2026-08-01T00:00:00.000Z",
context: {
runner: { platform: "linux", architecture: "x64", nodeVersion: "v24" },
browser: { name: "chromium", version: "140" },
viewport: { width: 1280, height: 720 },
network: {
profile: "contract-fast-4g",
latencyMs: 40,
downloadBytesPerSecond: 200_000,
uploadBytesPerSecond: 93_750,
},
cpu: { throttlingRate: 4 },
cache: { state: "cold", isolation: "new-browser-context" },
build: { buildId: "build-1", releaseId: "release-1" },
},
metrics: { lcpMs: 1_000, cls: 0.01, namedInteractionMs: 100 },
thresholds: { lcpMs: 2_500, cls: 0.1, namedInteractionMs: 200 },
fixtures: [
{ name: "missing-context", passed: true },
{ name: "lcp-over-threshold", passed: true },
],
passed: true,
} as const;
expect(labPerformanceArtifactSchema.parse(report)).toEqual(report);
});
it("rejects non-JSON values in generated schema documents", () => {
expect(() =>
jsonSchemaDocumentArtifactSchema.parse({
$schema: "https://json-schema.org/draft/2020-12/schema",
invalid: () => undefined,
}),
).toThrow();
});
it("keeps machine-readable evidence publishers on the validated writer", async () => {
const writerFiles = [
"scripts/generate-build-manifest.ts",
"scripts/generate-supply-chain.ts",
"scripts/collect-web-vitals-evidence.ts",
"scripts/test-performance.ts",
"scripts/verify-release.ts",
"scripts/drill-runbook.ts",
"scripts/check-registries.ts",
] as const;
const sources = await Promise.all(
writerFiles.map(async (file) => ({
file,
source: await readFile(file, "utf8"),
})),
);
for (const { file, source } of sources) {
const directWrites = source.match(/\bwriteFile\s*\(/gu) ?? [];
if (file === "scripts/generate-supply-chain.ts") {
expect(directWrites, file).toHaveLength(1);
expect(source, file).toMatch(
/writeFile\(\s*["']artifacts\/release\/checksums\.txt["']/u,
);
} else {
expect(directWrites, file).toHaveLength(0);
}
}
expect(
sources.flatMap(({ source }) =>
source.match(/\bwriteValidatedJsonArtifact\s*\(/gu) ?? [],
),
).toHaveLength(19);
});
});
+169
View File
@@ -0,0 +1,169 @@
import {
mkdtemp,
open,
readFile,
readdir,
rename,
rm,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { z } from "zod";
import {
createValidatedJsonArtifactWriter,
writeValidatedJsonArtifact,
} from "../../scripts/lib/validated-json-artifact.ts";
const artifactSchema = z
.object({ schemaVersion: z.literal(1), name: z.string().min(1) })
.strict();
const temporaryDirectories: string[] = [];
async function temporaryDirectory(): Promise<string> {
const directory = await mkdtemp(
path.join(tmpdir(), "validated-json-artifact-"),
);
temporaryDirectories.push(directory);
return directory;
}
afterEach(async () => {
await Promise.all(
temporaryDirectories.splice(0).map((directory) =>
rm(directory, { force: true, recursive: true }),
),
);
});
describe("validated JSON artifact writer", () => {
it.each(["existing", "missing"] as const)(
"rejects invalid %s artifacts before changing destination state",
async (destinationState) => {
const directory = await temporaryDirectory();
const destination = path.join(directory, "artifact.json");
if (destinationState === "existing") {
await writeFile(destination, "previous-bytes\n", "utf8");
}
const entriesBefore = await readdir(directory);
await expect(
writeValidatedJsonArtifact({
path: destination,
schema: artifactSchema,
value: { schemaVersion: 1, name: "" },
}),
).rejects.toThrow();
expect(await readdir(directory)).toEqual(entriesBefore);
if (destinationState === "existing") {
await expect(readFile(destination, "utf8")).resolves.toBe(
"previous-bytes\n",
);
} else {
await expect(readFile(destination, "utf8")).rejects.toMatchObject({
code: "ENOENT",
});
}
},
);
it("publishes complete formatted bytes through a sibling rename", async () => {
const directory = await temporaryDirectory();
const destination = path.join(directory, "artifact.json");
await writeValidatedJsonArtifact({
path: destination,
schema: artifactSchema,
value: { schemaVersion: 1, name: "valid" },
});
await expect(readFile(destination, "utf8")).resolves.toBe(
'{\n "schemaVersion": 1,\n "name": "valid"\n}\n',
);
expect(await readdir(directory)).toEqual(["artifact.json"]);
});
it.each(["write", "rename"] as const)(
"cleans only its owned sibling temp when %s fails",
async (failurePoint) => {
const directory = await temporaryDirectory();
const destination = path.join(directory, "artifact.json");
const unrelatedTemp = path.join(directory, ".artifact.json.unrelated.tmp");
const ownedTemp = path.join(directory, ".artifact.json.owned.tmp");
await writeFile(destination, "previous-bytes\n", "utf8");
await writeFile(unrelatedTemp, "unrelated\n", "utf8");
const writer = createValidatedJsonArtifactWriter({
createNonce: () => "owned",
fileSystem: {
open: async (target, flags) => {
const handle = await open(target, flags);
return {
writeFile: async (data, encoding) => {
await handle.writeFile(data, encoding);
if (failurePoint === "write") {
throw new Error("injected write failure");
}
},
close: async () => handle.close(),
};
},
rename: async (source, target) => {
if (failurePoint === "rename") {
throw new Error("injected rename failure");
}
await rename(source, target);
},
rm,
},
});
await expect(
writer({
path: destination,
schema: artifactSchema,
value: { schemaVersion: 1, name: "valid" },
}),
).rejects.toThrow(`injected ${failurePoint} failure`);
await expect(readFile(destination, "utf8")).resolves.toBe(
"previous-bytes\n",
);
await expect(readFile(unrelatedTemp, "utf8")).resolves.toBe(
"unrelated\n",
);
await expect(readFile(ownedTemp, "utf8")).rejects.toMatchObject({
code: "ENOENT",
});
},
);
it("does not delete a pre-existing colliding sibling temp", async () => {
const directory = await temporaryDirectory();
const destination = path.join(directory, "artifact.json");
const collidingTemp = path.join(directory, ".artifact.json.collision.tmp");
await writeFile(collidingTemp, "another-writer\n", "utf8");
const writer = createValidatedJsonArtifactWriter({
createNonce: () => "collision",
});
await expect(
writer({
path: destination,
schema: artifactSchema,
value: { schemaVersion: 1, name: "valid" },
}),
).rejects.toMatchObject({ code: "EEXIST" });
await expect(readFile(collidingTemp, "utf8")).resolves.toBe(
"another-writer\n",
);
await expect(readFile(destination, "utf8")).rejects.toMatchObject({
code: "ENOENT",
});
});
});