import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { generateReleaseNotes } from "./notes.mjs"; import { compareVersions, isExpectedNextVersion, parseVersion } from "./versioning.mjs"; function fail(message) { console.error(`release prepare failed: ${message}`); process.exit(1); } const releaseVersion = process.argv[2]; if (!releaseVersion) fail("usage: npm run release:prepare -- X.Y.Z"); const currentVersion = parseVersion(releaseVersion); if (!currentVersion) fail(`release version must be X.Y.Z, got ${releaseVersion}`); const packageJson = JSON.parse(await readFile("package.json", "utf8")); const packageLockJson = JSON.parse(await readFile("package-lock.json", "utf8")); const manifestJson = JSON.parse(await readFile("manifest.json", "utf8")); const versionsJson = JSON.parse(await readFile("versions.json", "utf8")); const notesDir = path.join(".github", "release-notes"); const notesPath = path.join(notesDir, `${releaseVersion}.md`); const versionKeys = Object.keys(versionsJson); const previousVersionKey = versionKeys.at(-1); const previousVersion = parseVersion(previousVersionKey); if (!previousVersion) fail(`versions.json contains invalid latest version ${previousVersionKey}`); if (compareVersions(previousVersion, currentVersion) >= 0) { fail(`release version ${releaseVersion} must be newer than ${previousVersionKey}`); } if (!isExpectedNextVersion(previousVersion, currentVersion)) { fail(`release version ${releaseVersion} is not the expected next version after ${previousVersionKey}`); } if (versionsJson[releaseVersion] !== undefined) fail(`versions.json already contains ${releaseVersion}`); try { await readFile(notesPath, "utf8"); fail(`${notesPath} already exists`); } catch (error) { if (error.code !== "ENOENT") throw error; } let releaseNotes; try { releaseNotes = generateReleaseNotes(previousVersionKey); } catch (error) { fail(error instanceof Error ? error.message : String(error)); } packageJson.version = releaseVersion; packageLockJson.version = releaseVersion; if (!packageLockJson.packages?.[""]) fail('package-lock.json is missing packages[""]'); packageLockJson.packages[""].version = releaseVersion; manifestJson.version = releaseVersion; versionsJson[releaseVersion] = manifestJson.minAppVersion; await writeFile("package.json", `${JSON.stringify(packageJson, null, 2)}\n`); await writeFile("package-lock.json", `${JSON.stringify(packageLockJson, null, 2)}\n`); await writeFile("manifest.json", `${JSON.stringify(manifestJson, null, 2)}\n`); await writeFile("versions.json", `${JSON.stringify(versionsJson, null, 2)}\n`); await mkdir(notesDir, { recursive: true }); await writeFile(notesPath, releaseNotes); console.log(`prepared release ${releaseVersion}`); console.log(`review and edit ${notesPath}, then run npm run release:preflight after committing`);