Complete V1 RC deployment capability gates
This commit is contained in:
107
tools/web/v1-acceptance-lib.mjs
Normal file
107
tools/web/v1-acceptance-lib.mjs
Normal file
@@ -0,0 +1,107 @@
|
||||
import assert from "node:assert/strict";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
export const ledgerPath = path.join(root, "docs/status/parity-ledger.json");
|
||||
export const packagePath = path.join(root, "web/package.json");
|
||||
export const evidencePath = path.join(root, "docs/status/v1-acceptance-evidence.json");
|
||||
export const toolRelativePaths = [
|
||||
"tools/web/v1-acceptance-lib.mjs",
|
||||
"tools/web/check-v1-acceptance-coverage.mjs",
|
||||
"tools/web/run-v1-acceptance.mjs",
|
||||
"tools/web/check-v1-acceptance-evidence.mjs",
|
||||
];
|
||||
|
||||
export function sha256(value) {
|
||||
return crypto.createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
export function readAcceptanceInputs() {
|
||||
const ledgerBytes = fs.readFileSync(ledgerPath);
|
||||
const packageBytes = fs.readFileSync(packagePath);
|
||||
return {
|
||||
ledgerBytes,
|
||||
packageBytes,
|
||||
ledger: JSON.parse(ledgerBytes),
|
||||
packageManifest: JSON.parse(packageBytes),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAcceptance(token, scripts) {
|
||||
assert.equal(typeof token, "string", "acceptance token must be a string");
|
||||
if (token.startsWith("web:e2e:")) {
|
||||
const pattern = token.slice("web:e2e:".length);
|
||||
assert.ok(pattern.trim().length > 0, `${token}: E2E pattern must not be empty`);
|
||||
assert.equal(typeof scripts["test:e2e"], "string", `${token}: web test:e2e script is missing`);
|
||||
return {
|
||||
token,
|
||||
kind: "e2e",
|
||||
script: "test:e2e",
|
||||
args: ["--", "--workers=1", "-g", pattern],
|
||||
command: `npm --prefix web run test:e2e -- --workers=1 -g ${JSON.stringify(pattern)}`,
|
||||
};
|
||||
}
|
||||
|
||||
const match = /^web:(test|release):(.+)$/.exec(token);
|
||||
assert.ok(match, `${token}: unsupported acceptance namespace`);
|
||||
const script = `${match[1]}:${match[2]}`;
|
||||
assert.equal(typeof scripts[script], "string", `${token}: web package script ${script} is missing`);
|
||||
return {
|
||||
token,
|
||||
kind: "script",
|
||||
script,
|
||||
args: [],
|
||||
command: `npm --prefix web run ${script}`,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAcceptancePlan(ledger, packageManifest) {
|
||||
assert.equal(ledger.schemaVersion, 2, "unsupported parity ledger schema");
|
||||
assert.ok(Array.isArray(ledger.families), "parity ledger families must be an array");
|
||||
assert.equal(ledger.families.length, 12, "V1 acceptance expects N-015 through N-026");
|
||||
assert.ok(packageManifest.scripts && typeof packageManifest.scripts === "object", "web package scripts are missing");
|
||||
|
||||
const byToken = new Map();
|
||||
let declarationCount = 0;
|
||||
for (const family of ledger.families) {
|
||||
assert.match(family.id, /^N-0(?:1[5-9]|2[0-6])$/, `${family.id}: unexpected family id`);
|
||||
assert.ok(Array.isArray(family.acceptance) && family.acceptance.length > 0, `${family.id}: acceptance is empty`);
|
||||
assert.equal(new Set(family.acceptance).size, family.acceptance.length, `${family.id}: duplicate acceptance token`);
|
||||
for (const token of family.acceptance) {
|
||||
declarationCount += 1;
|
||||
const existing = byToken.get(token);
|
||||
if (existing) {
|
||||
existing.familyIds.push(family.id);
|
||||
continue;
|
||||
}
|
||||
byToken.set(token, { ...resolveAcceptance(token, packageManifest.scripts), familyIds: [family.id] });
|
||||
}
|
||||
}
|
||||
|
||||
const entries = [...byToken.values()];
|
||||
return {
|
||||
familyCount: ledger.families.length,
|
||||
declarationCount,
|
||||
uniqueCount: entries.length,
|
||||
scriptCount: entries.filter((entry) => entry.kind === "script").length,
|
||||
e2eCount: entries.filter((entry) => entry.kind === "e2e").length,
|
||||
reusedCount: entries.filter((entry) => entry.familyIds.length > 1).length,
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
export function expectedEvidenceIdentity() {
|
||||
const inputs = readAcceptanceInputs();
|
||||
return {
|
||||
...inputs,
|
||||
plan: buildAcceptancePlan(inputs.ledger, inputs.packageManifest),
|
||||
ledgerSha256: sha256(inputs.ledgerBytes),
|
||||
packageSha256: sha256(inputs.packageBytes),
|
||||
toolSourceSha256: Object.fromEntries(
|
||||
toolRelativePaths.map((relativePath) => [relativePath, sha256(fs.readFileSync(path.join(root, relativePath)))]),
|
||||
),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user