import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const notesPath = path.join(repoRoot, "docs/web/RELEASE_NOTES.md"); const notes = fs.readFileSync(notesPath, "utf8"); const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "web/package.json"), "utf8")); const engine = JSON.parse(fs.readFileSync(path.join(repoRoot, "web/app/public/engine-manifest.json"), "utf8")); const ledger = JSON.parse(fs.readFileSync(path.join(repoRoot, "docs/status/parity-ledger.json"), "utf8")); assert.match(notes, new RegExp(`^# Web Blender Modeler V1 ${packageJson.version.replaceAll(".", "\\.")} Release Notes`, "m")); assert.match(notes, new RegExp(`engine\\s+\`${engine.releaseId.replaceAll(".", "\\.")}\``)); for (const capability of ["\.blend", "undo", "redo", "OffscreenCanvas", "single-thread", "pthread", "OPFS", "10M geometry", "8K textures", "SBOM"]) { assert.match(notes, new RegExp(capability, "i"), `release notes omit verified capability: ${capability}`); } for (const boundary of ["LOCAL_EXACT", "LOCAL_BOUNDED", "SERVER", "EXCLUDED", "Chromium-only", "not a browser port"]) { assert.ok(notes.includes(boundary), `release notes omit capability boundary: ${boundary}`); } const releaseReady = ledger.families.filter((family) => family.releaseStatus === "READY").length; const parityBlocked = ledger.families.filter((family) => family.parityStatus === "BLOCKED").length; assert.equal(releaseReady, 12); assert.equal(parityBlocked, 12); assert.ok(notes.includes(`All ${releaseReady} V1 family release slices are \`READY\``)); assert.match(notes, new RegExp(`all ${parityBlocked} complete Blender 5\\.2 family parity states are\\s+\`BLOCKED\``)); const links = [...notes.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)].map((match) => match[1]); assert.ok(links.length >= 5, "release notes do not link the capability and operations sources"); for (const link of links) { assert.doesNotMatch(link, /^(?:https?:|\/)/, `release notes contain an external or absolute link: ${link}`); const source = link === "V1_SCOPE.md" ? path.join(repoRoot, "docs/WEB_BLENDER_MODELER_V1_SCOPE.md") : link === "parity-ledger.json" ? path.join(repoRoot, "docs/status/parity-ledger.json") : path.join(repoRoot, "docs/web", link); assert.ok(fs.statSync(source).isFile(), `release note link is unresolved: ${link}`); } process.stdout.write(`rc-release-notes-ok version=${packageJson.version} ready=${releaseReady} parityBlocked=${parityBlocked} links=${links.length}\n`);