From 9b17fd5bb3fe9be26e1e6099cbce3c6b5e1a6f5b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 6 Aug 2026 18:16:01 +0200 Subject: [PATCH] fix: verify npm package registration before release --- scripts/release.mjs | 73 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/scripts/release.mjs b/scripts/release.mjs index 217561cf1..422c08a8a 100755 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -8,17 +8,18 @@ * * Steps: * 1. Check for uncommitted changes - * 2. Bump version via npm run version:xxx or set an explicit version - * 3. Update CHANGELOG.md files: [Unreleased] -> [version] - date - * 4. Regenerate release artifacts - * 5. Run checks and tests - * 6. Commit and tag the release - * 7. Add new [Unreleased] section to changelogs - * 8. Commit next-cycle changelog updates - * 9. Push main and the tag to trigger CI publishing + * 2. Verify every public workspace package is registered on npm + * 3. Bump version via npm run version:xxx or set an explicit version + * 4. Update CHANGELOG.md files: [Unreleased] -> [version] - date + * 5. Regenerate release artifacts + * 6. Run checks and tests + * 7. Commit and tag the release + * 8. Add new [Unreleased] section to changelogs + * 9. Commit next-cycle changelog updates + * 10. Push main and the tag to trigger CI publishing */ -import { execSync } from "node:child_process"; +import { execSync, spawnSync } from "node:child_process"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { findPackageDirectories } from "./package-workspaces.mjs"; @@ -50,6 +51,41 @@ function getVersion() { return pkg.version; } +function assertPackagesAreRegisteredWithNpm() { + const packageNames = findPackageDirectories() + .map((directory) => JSON.parse(readFileSync(join(directory, "package.json"), "utf8"))) + .filter((pkg) => pkg.private !== true) + .map((pkg) => pkg.name); + const unregisteredPackages = []; + + console.log("Checking npm package registration..."); + for (const packageName of packageNames) { + const result = spawnSync(process.platform === "win32" ? "npm.cmd" : "npm", ["view", packageName, "version", "--json"], { + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + + if (result.status === 0 && result.stdout.trim()) { + console.log(` ${packageName}`); + continue; + } + + const output = [result.stdout, result.stderr, result.error?.message].filter(Boolean).join("\n"); + if (output.includes("E404") || output.includes("404 Not Found")) { + unregisteredPackages.push(packageName); + continue; + } + + throw new Error(output ? `Failed to query npm registration for ${packageName}\n${output}` : `Failed to query npm registration for ${packageName}`); + } + + if (unregisteredPackages.length > 0) { + throw new Error(`The following public workspace packages are not registered on npm:\n${unregisteredPackages.map((packageName) => ` ${packageName}`).join("\n")}\nRegister them before running a release.`); + } + + console.log(" All public workspace packages are registered on npm\n"); +} + function compareVersions(a, b) { const aParts = a.split(".").map(Number); const bParts = b.split(".").map(Number); @@ -190,16 +226,19 @@ if (status && status.trim()) { } console.log(" Working directory clean\n"); -// 2. Bump or set version +// 2. Verify npm package registration before modifying the worktree. +assertPackagesAreRegisteredWithNpm(); + +// 3. Bump or set version const version = bumpOrSetVersion(RELEASE_TARGET); console.log(` New version: ${version}\n`); -// 3. Update changelogs +// 4. Update changelogs console.log("Updating CHANGELOG.md files..."); updateChangelogsForRelease(version); console.log(); -// 4. Regenerate release artifacts +// 5. Regenerate release artifacts console.log("Regenerating release artifacts..."); run("npm run generate:models"); run("npm run check:model-data"); @@ -207,7 +246,7 @@ run("npm run shrinkwrap:coding-agent"); run("npm run install-lock:coding-agent"); console.log(); -// 5. Run checks and tests +// 6. Run checks and tests console.log("Running checks..."); run("npm run check"); console.log(); @@ -220,25 +259,25 @@ console.log("Running tests..."); run("./test.sh"); console.log(); -// 6. Commit and tag +// 7. Commit and tag console.log("Committing and tagging..."); stageChangedFiles(); run(`git commit -m "Release v${version}"`); run(`git tag v${version}`); console.log(); -// 7. Add new [Unreleased] sections +// 8. Add new [Unreleased] sections console.log("Adding [Unreleased] sections for next cycle..."); addUnreleasedSection(); console.log(); -// 8. Commit +// 9. Commit console.log("Committing changelog updates..."); stageChangedFiles(); run(`git commit -m "Add [Unreleased] section for next cycle"`); console.log(); -// 9. Push +// 10. Push console.log("Pushing to remote..."); run("git push origin main"); run(`git push origin v${version}`);