源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Stable-version helpers for the tag-driven release flow.
|
||||
*
|
||||
* Git tags (vX.Y.Z) are the single source of truth for released versions —
|
||||
* package.json files hold a permanent "0.0.0-dev" placeholder and CI stamps
|
||||
* the real version into the workspace at build time (see stamp-version.mjs).
|
||||
*
|
||||
* CLI:
|
||||
* node scripts/release/versions.mjs latest # highest stable tag version
|
||||
* node scripts/release/versions.mjs next --bump patch # next version from tags
|
||||
*/
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
||||
|
||||
export const STABLE_VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
|
||||
|
||||
export function parseStableVersion(value) {
|
||||
const match = typeof value === "string" ? value.match(STABLE_VERSION_PATTERN) : null;
|
||||
return match ? match.slice(1).map(Number) : null;
|
||||
}
|
||||
|
||||
export function compareStableVersions(left, right) {
|
||||
const leftParts = parseStableVersion(left);
|
||||
const rightParts = parseStableVersion(right);
|
||||
if (!leftParts || !rightParts) {
|
||||
throw new Error(`Cannot compare non-stable versions: ${left} vs ${right}`);
|
||||
}
|
||||
for (let index = 0; index < 3; index += 1) {
|
||||
const difference = leftParts[index] - rightParts[index];
|
||||
if (difference !== 0) return difference;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function stableVersionFromTag(tag) {
|
||||
const trimmed = typeof tag === "string" ? tag.trim() : "";
|
||||
if (!trimmed.startsWith("v")) return null;
|
||||
const version = trimmed.slice(1);
|
||||
return parseStableVersion(version) ? version : null;
|
||||
}
|
||||
|
||||
export function highestStableVersion(versions) {
|
||||
let highest = null;
|
||||
for (const version of versions) {
|
||||
if (!parseStableVersion(version)) continue;
|
||||
if (highest === null || compareStableVersions(version, highest) > 0) {
|
||||
highest = version;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
export function bumpStableVersion(version, bumpType) {
|
||||
const parts = parseStableVersion(version);
|
||||
if (!parts) throw new Error(`Invalid stable version: ${version}`);
|
||||
const [major, minor, patch] = parts;
|
||||
if (bumpType === "major") return `${major + 1}.0.0`;
|
||||
if (bumpType === "minor") return `${major}.${minor + 1}.0`;
|
||||
if (bumpType === "patch") return `${major}.${minor}.${patch + 1}`;
|
||||
throw new Error(`Unknown bump type: ${bumpType}`);
|
||||
}
|
||||
|
||||
export function readStableTagVersions(cwd = root) {
|
||||
return execFileSync("git", ["tag", "--list", "v*"], { cwd, encoding: "utf8" })
|
||||
.split("\n")
|
||||
.map((tag) => stableVersionFromTag(tag))
|
||||
.filter((version) => version !== null);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
const latest = highestStableVersion(readStableTagVersions());
|
||||
|
||||
if (command === "latest") {
|
||||
if (!latest) throw new Error("No stable v* tags found (need a full clone: fetch-depth 0).");
|
||||
console.log(latest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "next") {
|
||||
if (!latest) throw new Error("No stable v* tags found (need a full clone: fetch-depth 0).");
|
||||
const bumpIndex = args.indexOf("--bump");
|
||||
const bumpType = bumpIndex >= 0 ? args[bumpIndex + 1] : "patch";
|
||||
console.log(bumpStableVersion(latest, bumpType));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error("Usage: versions.mjs latest | next --bump patch|minor|major");
|
||||
}
|
||||
|
||||
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user