Skip to content

Commit 6a0e065

Browse files
Refine version update logic to allow arbitrary identifiers (e.g., "beta", not just "preview") (#1193)
1 parent 662f270 commit 6a0e065

3 files changed

Lines changed: 108 additions & 66 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as semver from "semver";
2+
3+
const validCommands = ["current", "current-prerelease", "latest", "prerelease", "unstable"];
4+
5+
export function calculateVersion(command, { latest, prerelease, unstable }) {
6+
if (!validCommands.includes(command)) {
7+
throw new Error(
8+
`Invalid argument, must be one of: ${validCommands.join(", ")}, got: "${command}"`
9+
);
10+
}
11+
12+
if (!latest) {
13+
throw new Error("No latest version found. Publish an initial version first.");
14+
}
15+
16+
// Output the current latest version to stdout
17+
if (command === "current") {
18+
return latest;
19+
}
20+
21+
// Use latest if no prerelease exists, or compare to find higher
22+
let higherVersion;
23+
if (!prerelease) {
24+
higherVersion = latest;
25+
} else {
26+
try {
27+
higherVersion = semver.gt(latest, prerelease) ? latest : prerelease;
28+
} catch (err) {
29+
throw new Error(
30+
`Failed to compare versions "${latest}" and "${prerelease}": ${err.message}`
31+
);
32+
}
33+
}
34+
35+
// Output the most recent version including prerelease versions to stdout
36+
if (command === "current-prerelease") {
37+
return higherVersion;
38+
}
39+
40+
if (command === "unstable") {
41+
if (unstable && semver.gt(unstable, higherVersion)) {
42+
higherVersion = unstable;
43+
}
44+
}
45+
46+
const increment = command === "latest" ? "patch" : "prerelease";
47+
const isIncrementingExistingPrerelease = semver.prerelease(higherVersion) !== null;
48+
const prereleaseIdentifier =
49+
command === "prerelease"
50+
? isIncrementingExistingPrerelease
51+
? undefined
52+
: "preview"
53+
: command === "unstable"
54+
? "unstable"
55+
: undefined;
56+
const nextVersion = semver.inc(higherVersion, increment, prereleaseIdentifier);
57+
if (!nextVersion) {
58+
throw new Error(`Failed to increment version "${higherVersion}" with "${increment}"`);
59+
}
60+
61+
return nextVersion;
62+
}

nodejs/scripts/get-version.js

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212
import { execSync } from "child_process";
1313
import * as semver from "semver";
14+
import { calculateVersion } from "./calculate-version.js";
1415

1516
async function getLatestVersion(tag) {
1617
try {
@@ -30,69 +31,8 @@ async function getLatestVersion(tag) {
3031
}
3132
}
3233

33-
async function main() {
34-
const command = process.argv[2];
35-
const validCommands = ["current", "current-prerelease", "latest", "prerelease", "unstable"];
36-
if (!validCommands.includes(command)) {
37-
console.error(
38-
`Invalid argument, must be one of: ${validCommands.join(", ")}, got: "${command}"`
39-
);
40-
process.exit(1);
41-
}
42-
43-
const latest = await getLatestVersion("latest");
44-
if (!latest) {
45-
console.error("No latest version found. Publish an initial version first.");
46-
process.exit(1);
47-
}
48-
49-
// Output the current latest version to stdout
50-
if (command === "current") {
51-
console.log(latest);
52-
return;
53-
}
54-
55-
const prerelease = await getLatestVersion("prerelease");
56-
57-
// Use latest if no prerelease exists, or compare to find higher
58-
let higherVersion;
59-
if (!prerelease) {
60-
higherVersion = latest;
61-
} else {
62-
try {
63-
higherVersion = semver.gt(latest, prerelease) ? latest : prerelease;
64-
} catch (err) {
65-
console.error(
66-
`Failed to compare versions "${latest}" and "${prerelease}": ${err.message}`
67-
);
68-
process.exit(1);
69-
}
70-
}
71-
72-
// Output the most recent version including prerelease versions to stdout
73-
if (command === "current-prerelease") {
74-
console.log(higherVersion);
75-
return;
76-
}
77-
78-
if (command === "unstable") {
79-
const unstable = await getLatestVersion("unstable");
80-
if (unstable && semver.gt(unstable, higherVersion)) {
81-
higherVersion = unstable;
82-
}
83-
}
84-
85-
const increment = command === "latest" ? "patch" : "prerelease";
86-
const prereleaseIdentifier =
87-
command === "prerelease" ? "preview" : command === "unstable" ? "unstable" : undefined;
88-
const nextVersion = semver.inc(higherVersion, increment, prereleaseIdentifier);
89-
if (!nextVersion) {
90-
console.error(`Failed to increment version "${higherVersion}" with "${increment}"`);
91-
process.exit(1);
92-
}
93-
94-
// Output the next version to stdout
95-
console.log(nextVersion);
96-
}
97-
98-
void main();
34+
const command = process.argv[2];
35+
const latest = await getLatestVersion("latest");
36+
const prerelease = await getLatestVersion("prerelease");
37+
const unstable = command === "unstable" ? await getLatestVersion("unstable") : undefined;
38+
console.log(calculateVersion(command, { latest, prerelease, unstable }));

nodejs/test/get-version.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { describe, expect, it } from "vitest";
2+
import { calculateVersion } from "../scripts/calculate-version.js";
3+
4+
describe("get-version", () => {
5+
it("increments stable latest versions by patch", () => {
6+
expect(calculateVersion("latest", { latest: "1.0.1" })).toBe("1.0.2");
7+
});
8+
9+
it("promotes a higher prerelease to stable for latest releases", () => {
10+
expect(calculateVersion("latest", { latest: "0.3.0", prerelease: "1.0.0-beta.1" })).toBe(
11+
"1.0.0"
12+
);
13+
});
14+
15+
it("starts preview prereleases when incrementing from a stable release", () => {
16+
expect(calculateVersion("prerelease", { latest: "0.3.0" })).toBe("0.3.1-preview.0");
17+
});
18+
19+
it("preserves custom prerelease identifiers when incrementing prereleases", () => {
20+
expect(
21+
calculateVersion("prerelease", { latest: "0.3.0", prerelease: "0.4.0-chicken.2" })
22+
).toBe("0.4.0-chicken.3");
23+
});
24+
25+
it("preserves beta prerelease identifiers when incrementing prereleases", () => {
26+
expect(
27+
calculateVersion("prerelease", { latest: "0.3.0", prerelease: "1.0.0-beta.1" })
28+
).toBe("1.0.0-beta.2");
29+
});
30+
31+
it("increments unstable releases with the unstable identifier", () => {
32+
expect(
33+
calculateVersion("unstable", {
34+
latest: "0.3.0",
35+
prerelease: "0.4.0-chicken.2",
36+
unstable: "0.5.0-unstable.2",
37+
})
38+
).toBe("0.5.0-unstable.3");
39+
});
40+
});

0 commit comments

Comments
 (0)