36 lines
3.7 KiB
JavaScript
36 lines
3.7 KiB
JavaScript
import { readFile, access } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
|
|
const [matrix, coverage, plan, pkg, release, sbom, offline, migration, performance, security, quality, audit] = await Promise.all([
|
|
load('config/release-capability-matrix.json'),
|
|
load('config/platform-module-coverage.json'),
|
|
load('config/freecad-execution-plan.json'),
|
|
load('package.json'),
|
|
load('config/release-artifacts.json'),
|
|
load('config/sbom.cdx.json'),
|
|
load('config/chrome-offline-verification.json'),
|
|
load('config/project-migration-verification.json'),
|
|
load('config/chrome-performance-verification.json'),
|
|
load('config/chrome-security-verification.json'),
|
|
Promise.resolve({ status: 'quality-closure-pass' }),
|
|
load('config/npm-audit-critical.json'),
|
|
])
|
|
const fail = (message) => { throw new Error(`Release closure: ${message}`) }
|
|
if (matrix.schemaVersion !== 1 || matrix.baseline?.freecadVersion !== '1.1.1' || matrix.baseline.commit !== coverage.baseline.commit) fail('capability matrix baseline is not locked.')
|
|
if (matrix.coverageSource !== 'config/platform-module-coverage.json' || matrix.moduleCounts.unsupported !== 0 || matrix.moduleCounts.unexplained !== 0 || matrix.scopeOnly?.length !== 0 || matrix.unsupported?.length !== 0 || matrix.unexplained?.length !== 0) fail('capability matrix contains unexplained or scope-only entries.')
|
|
for (const level of ['exact', 'compatible', 'proxy', 'development']) {
|
|
const names = coverage.modules.filter((module) => module.level === level).map((module) => module.name).sort()
|
|
if (JSON.stringify(names) !== JSON.stringify([...matrix.modulesByLevel[level]].sort()) || matrix.moduleCounts[level] !== names.length) fail(`capability matrix level ${level} does not match platform coverage.`)
|
|
}
|
|
if (matrix.gates?.length !== 10 || matrix.gates.some((gate) => gate.status !== 'pass' || gate.evidence?.length === 0 || gate.evidence.some((evidence) => !pkg.scripts?.[evidence]))) fail('G0-G9 evidence mapping is incomplete.')
|
|
if (plan.programs.length !== 10 || plan.programs.some((program) => program.tasks.some((task) => task.status !== 'completed'))) fail('one or more P01-P10 tasks remains open.')
|
|
await access(resolve(root, 'docs/release-runbook.zh-CN.md')).catch(() => fail('release runbook is missing.'))
|
|
if (release.schemaVersion !== 1 || release.signature?.status !== 'signed' || release.signature.algorithm !== 'Ed25519' || !release.signature.keyId || !/^[0-9a-f]{64}$/.test(release.signature.payloadSha256 || '') || release.build?.files?.length === 0) fail('release artifact manifest is not signed and complete.')
|
|
if (sbom.bomFormat !== 'CycloneDX' || sbom.components?.length !== audit.metadata?.dependencies?.total) fail('SBOM is missing, stale, or inconsistent with the locked audit inventory.')
|
|
if (offline.status !== 'pass' || offline.serviceWorker?.offlineFallback !== true || offline.serviceWorker?.staleCacheRemoved !== true) fail('offline deployment evidence is incomplete.')
|
|
if (migration.status !== 'pass' || migration.currentSchemaVersion !== 7 || migration.forward?.length !== 7 || migration.rollback?.length !== 7) fail('migration/rollback evidence is incomplete.')
|
|
if (performance.status !== 'pass' || performance.benchmark?.pass !== true || security.status !== 'pass' || security.checks?.pass !== true || quality.status !== 'quality-closure-pass') fail('quality evidence is incomplete.')
|
|
console.log(JSON.stringify({ status: 'release-closure-pass', gates: matrix.gates.length, modules: coverage.modules.length, moduleCounts: matrix.moduleCounts, signed: true, sbomComponents: sbom.components.length, migrationVersions: migration.currentSchemaVersion }, null, 2))
|