ci: fix Node 20 compat, rename workflows, add unit tests

- Replace import.meta.dirname (Node 21.2+) with fileURLToPath
  workaround for Node 20 compatibility
- Rename workflows to release / pre, release / publish, release /
  create-pr so they group together in the Actions UI
- Fix semver regex to allow hyphens in prerelease identifiers
- Add unit tests for parseSemver, computeNextStableVersion,
  computePrereleaseVersion
This commit is contained in:
Tyler Slaton
2026-04-10 22:43:53 -07:00
parent 1ddccd4d95
commit 5b84c16b55
6 changed files with 118 additions and 5 deletions
+5 -1
View File
@@ -1,7 +1,11 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
export const ROOT = path.resolve(import.meta.dirname, "../../..");
export const ROOT = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../..",
);
export interface ReleaseConfig {
prereleaseTag: string;
+109
View File
@@ -0,0 +1,109 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import {
parseSemver,
computeNextStableVersion,
computePrereleaseVersion,
} from "./versions.js";
// Mock loadConfig for computePrereleaseVersion
vi.mock("./config.js", () => ({
ROOT: "/mock",
loadConfig: () => ({
prereleaseTag: "canary",
versionedTogether: [],
versionedIndependently: [],
}),
}));
describe("parseSemver", () => {
it("parses a stable version", () => {
expect(parseSemver("1.55.2")).toEqual({
major: 1,
minor: 55,
patch: 2,
prerelease: null,
});
});
it("parses a prerelease version", () => {
expect(parseSemver("1.55.2-canary.1744382400")).toEqual({
major: 1,
minor: 55,
patch: 2,
prerelease: "canary.1744382400",
});
});
it("parses a version with text prerelease", () => {
expect(parseSemver("2.0.0-canary.fix-user-issue")).toEqual({
major: 2,
minor: 0,
patch: 0,
prerelease: "canary.fix-user-issue",
});
});
it("throws on invalid version", () => {
expect(() => parseSemver("not-a-version")).toThrow("Invalid semver");
});
it("throws on empty string", () => {
expect(() => parseSemver("")).toThrow("Invalid semver");
});
});
describe("computeNextStableVersion", () => {
it("bumps patch", () => {
expect(computeNextStableVersion("1.55.2", "patch")).toBe("1.55.3");
});
it("bumps minor", () => {
expect(computeNextStableVersion("1.55.2", "minor")).toBe("1.56.0");
});
it("bumps major", () => {
expect(computeNextStableVersion("1.55.2", "major")).toBe("2.0.0");
});
it("strips prerelease suffix on any bump", () => {
expect(computeNextStableVersion("1.56.0-canary.123", "patch")).toBe(
"1.56.0",
);
});
it("strips prerelease suffix regardless of bump level", () => {
expect(computeNextStableVersion("2.0.0-canary.123", "major")).toBe("2.0.0");
});
it("handles 0.x versions", () => {
expect(computeNextStableVersion("0.1.0", "patch")).toBe("0.1.1");
expect(computeNextStableVersion("0.1.0", "minor")).toBe("0.2.0");
expect(computeNextStableVersion("0.1.0", "major")).toBe("1.0.0");
});
});
describe("computePrereleaseVersion", () => {
it("appends canary tag with timestamp when no suffix given", () => {
const result = computePrereleaseVersion("1.55.2");
expect(result).toMatch(/^1\.55\.2-canary\.\d+$/);
});
it("appends canary tag with custom suffix", () => {
expect(computePrereleaseVersion("1.55.2", "fix-user-issue")).toBe(
"1.55.2-canary.fix-user-issue",
);
});
it("uses the base version as-is (no bump)", () => {
expect(computePrereleaseVersion("1.55.2", "test")).toBe(
"1.55.2-canary.test",
);
expect(computePrereleaseVersion("2.0.0", "test")).toBe("2.0.0-canary.test");
});
it("strips existing prerelease before appending", () => {
expect(computePrereleaseVersion("1.55.2-canary.old", "new")).toBe(
"1.55.2-canary.new",
);
});
});
+1 -1
View File
@@ -28,7 +28,7 @@ export function getCurrentVersion(): string {
export function parseSemver(version: string): SemVer {
const match = version.match(
/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.]+))?(?:\+(.+))?$/,
/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.\-]+))?(?:\+(.+))?$/,
);
if (!match) {
throw new Error(`Invalid semver: ${version}`);