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))