From 1573d46ac2745e17c22a82b5b283502304a94937 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 17 Apr 2026 23:03:31 +0200 Subject: [PATCH] chore: allow explicit release versions --- scripts/release.mjs | 73 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/scripts/release.mjs b/scripts/release.mjs index 12b33d04..490938e8 100755 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -2,11 +2,13 @@ /** * Release script for pi-mono * - * Usage: node scripts/release.mjs + * Usage: + * node scripts/release.mjs + * node scripts/release.mjs * * Steps: * 1. Check for uncommitted changes - * 2. Bump version via npm run version:xxx + * 2. Bump version via npm run version:xxx or set an explicit version * 3. Update CHANGELOG.md files: [Unreleased] -> [version] - date * 4. Commit and tag * 5. Publish to npm @@ -18,10 +20,12 @@ import { execSync } from "child_process"; import { readFileSync, writeFileSync, readdirSync, existsSync } from "fs"; import { join } from "path"; -const BUMP_TYPE = process.argv[2]; +const RELEASE_TARGET = process.argv[2]; +const BUMP_TYPES = new Set(["major", "minor", "patch"]); +const SEMVER_RE = /^\d+\.\d+\.\d+$/; -if (!["major", "minor", "patch"].includes(BUMP_TYPE)) { - console.error("Usage: node scripts/release.mjs "); +if (!RELEASE_TARGET || (!BUMP_TYPES.has(RELEASE_TARGET) && !SEMVER_RE.test(RELEASE_TARGET))) { + console.error("Usage: node scripts/release.mjs "); process.exit(1); } @@ -43,6 +47,55 @@ function getVersion() { return pkg.version; } +function compareVersions(a, b) { + const aParts = a.split(".").map(Number); + const bParts = b.split(".").map(Number); + + for (let i = 0; i < 3; i++) { + const diff = (aParts[i] || 0) - (bParts[i] || 0); + if (diff !== 0) { + return diff; + } + } + + return 0; +} + +function shellQuote(value) { + return `'${value.replace(/'/g, `'\\''`)}'`; +} + +function stageChangedFiles() { + const output = run("git ls-files -m -o -d --exclude-standard", { silent: true }); + const paths = [...new Set((output || "").split("\n").map((line) => line.trim()).filter(Boolean))]; + if (paths.length === 0) { + return; + } + + run(`git add -- ${paths.map(shellQuote).join(" ")}`); +} + +function bumpOrSetVersion(target) { + const currentVersion = getVersion(); + + if (BUMP_TYPES.has(target)) { + console.log(`Bumping version (${target})...`); + run(`npm run version:${target}`); + return getVersion(); + } + + if (compareVersions(target, currentVersion) <= 0) { + console.error(`Error: explicit version ${target} must be greater than current version ${currentVersion}.`); + process.exit(1); + } + + console.log(`Setting explicit version (${target})...`); + run( + `npm version ${target} -ws --no-git-tag-version && node scripts/sync-versions.js && shx rm -rf node_modules packages/*/node_modules package-lock.json && npm install`, + ); + return getVersion(); +} + function getChangelogs() { const packagesDir = "packages"; const packages = readdirSync(packagesDir); @@ -102,10 +155,8 @@ if (status && status.trim()) { } console.log(" Working directory clean\n"); -// 2. Bump version -console.log(`Bumping version (${BUMP_TYPE})...`); -run(`npm run version:${BUMP_TYPE}`); -const version = getVersion(); +// 2. Bump or set version +const version = bumpOrSetVersion(RELEASE_TARGET); console.log(` New version: ${version}\n`); // 3. Update changelogs @@ -115,7 +166,7 @@ console.log(); // 4. Commit and tag console.log("Committing and tagging..."); -run("git add ."); +stageChangedFiles(); run(`git commit -m "Release v${version}"`); run(`git tag v${version}`); console.log(); @@ -132,7 +183,7 @@ console.log(); // 7. Commit console.log("Committing changelog updates..."); -run("git add ."); +stageChangedFiles(); run(`git commit -m "Add [Unreleased] section for next cycle"`); console.log();