Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions dist/azure/index.mjs

Large diffs are not rendered by default.

13 changes: 5 additions & 8 deletions dist/gitlab/index.mjs

Large diffs are not rendered by default.

97 changes: 49 additions & 48 deletions dist/index.mjs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
"@actions/core": "^3.0.0",
"@actions/exec": "^3.0.0",
"@actions/glob": "^0.7.0",
"semver": "^7.8.5",
"verkit": "^0.5.0",
"yaml": "^2.8.2",
"zod": "^4.0.0"
},
"devDependencies": {
"@e18e/eslint-plugin": "^0.8.1",
"@types/node": "^24.0.0",
"@types/semver": "^7.8.0",
"fallow": "3.33.1",
"typescript": "^7.0.0",
"vite": "catalog:",
Expand Down
28 changes: 13 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 26 additions & 11 deletions src/ci/package-manager-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ function write(path: string, text = "fixture"): void {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, text);
}
function fixture(name = "pnpm", platform: NodeJS.Platform = process.platform) {
function fixture(name = "pnpm", platform: NodeJS.Platform = process.platform, version = "12.14.0") {
const toolchain = createToolchainCachePlan(
{ version: "1.3.0", nodeVersion: "24.22.0", projectDir: root },
env,
platform,
)!;
const manager = {
name,
version: "12.14.0",
version,
mode: "managed",
bin_paths: {
[name]: join(
toolchain.data,
"package_manager",
name,
"12.14.0",
version,
name,
"bin",
`${name}${platform === "win32" ? ".cmd" : ""}`,
Expand Down Expand Up @@ -83,14 +83,29 @@ describe("package manager cache selection", () => {
expect(create()?.path).toBe(join(toolchain.data, "package_manager", name, "12.14.0"));
},
);
it.each(["latest", "default", "^12.0.0", "../outside", "unknown"])(
"skips unresolved version %s",
(version) => {
const { manager, create } = fixture();
manager.version = version;
expect(create()).toBeUndefined();
},
);
it.each([
"latest",
"default",
"^12.0.0",
"../outside",
"unknown",
"v12.14.0",
"12.14.0+build.1",
"012.14.0",
"12.14.0-rc.01",
"9007199254740992.0.0",
])("skips unresolved or noncanonical version %s", (version) => {
const { create } = fixture("pnpm", process.platform, version);
expect(create()).toBeUndefined();
});
it("accepts an exact prerelease package manager version", () => {
const version = "12.14.0-rc.1";
const { create, toolchain } = fixture("pnpm", process.platform, version);
expect(create()).toMatchObject({
version,
path: join(toolchain.data, "package_manager", "pnpm", version),
});
});
it("skips system managers, bundled npm, and unsupported layouts", () => {
const { manager, create } = fixture();
manager.mode = "system_first";
Expand Down
4 changes: 2 additions & 2 deletions src/ci/package-manager-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
import { homedir, tmpdir } from "node:os";
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
import { valid } from "semver";
import { normalize } from "verkit";
import type { ToolchainCachePlan } from "./toolchain-cache.js";

const PACKAGE_MANAGERS = {
Expand Down Expand Up @@ -58,7 +58,7 @@ export function createPackageManagerCachePlan(
manager.mode !== "managed" ||
!Object.hasOwn(PACKAGE_MANAGERS, manager.name) ||
typeof manager.version !== "string" ||
valid(manager.version) !== manager.version
normalize(manager.version) !== manager.version
)
return;
const name = manager.name as PackageManagerName;
Expand Down
21 changes: 21 additions & 0 deletions src/ci/project-node-version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ describe("project Node selection for the installer", () => {
expect(resolveProjectNodeVersion(root)).toBe(expected);
});

it.each([
"22.x",
"~22.18.0",
"^22.18.0 || >=24.11.0",
"22.18.0 - 24.11.0",
">=22.18.0 <23",
"26.0.0-rc.1",
"*",
])("preserves the project Node selector %s", (version) => {
const root = fixture({ "package.json": JSON.stringify({ engines: { node: version } }) });
expect(resolveProjectNodeVersion(root)).toBe(version);
});

it.each(["22.018.0", "22.18.0-rc.01", ">=not-a-version", "9007199254740992.0.0"])(
"does not inherit a parent pin after invalid Node selector %s",
(version) => {
const root = fixture({ ".node-version": "24.11.0", "app/.node-version": version });
expect(resolveProjectNodeVersion(join(root, "app"))).toBeUndefined();
},
);

it.each([
["# comment\nnode\n", "latest"],
["stable # comment\n", "latest"],
Expand Down
4 changes: 2 additions & 2 deletions src/ci/project-node-version.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { validRange } from "semver";
import { normalizeRange } from "verkit";

const VERSION_PREFIX_RE = /^v/;
const NODE_VERSION_ALIAS_RE = /^(?:lts|latest|lts\/(?:\*|[a-z]+|-\d+))$/i;
Expand Down Expand Up @@ -65,6 +65,6 @@ function parseNvmrc(content?: string): string | undefined {
function normalize(value?: string): string | undefined {
const version = value?.trim().replace(VERSION_PREFIX_RE, "");
if (!version) return undefined;
if (NODE_VERSION_ALIAS_RE.test(version) || validRange(version)) return version;
if (NODE_VERSION_ALIAS_RE.test(version) || normalizeRange(version)) return version;
return undefined;
}
49 changes: 33 additions & 16 deletions src/ci/toolchain-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,33 @@ afterEach(() => {
});

describe("toolchain cache selection", () => {
it.each(["latest", "^1.3.0", "0.2.9", "0.0.0-commit.1234567"])(
"skips unsupported Vite+ selector %s",
(version) => {
expect(createToolchainCachePlan({ ...options, version }, env)).toBeUndefined();
},
);
it.each([undefined, "lts", "22", "^22.18.0"])(
"does not freeze Node selector %s",
(nodeVersion) => {
expect(
createToolchainCachePlan({ ...options, nodeVersion, projectDir: root }, env),
).toBeUndefined();
},
);
it.each([
"latest",
"^1.3.0",
"0.2.9",
"0.0.0-commit.1234567",
"v1.3.0",
"1.3.0+build.1",
"01.3.0",
"1.3.0-rc.01",
"9007199254740992.0.0",
])("skips unsupported Vite+ selector %s", (version) => {
expect(createToolchainCachePlan({ ...options, version }, env)).toBeUndefined();
});
it.each([
undefined,
"lts",
"22",
"^22.18.0",
"22.018.0",
"22.18.0+build.1",
"22.18.0-rc.1",
"9007199254740992.0.0",
])("does not freeze Node selector %s", (nodeVersion) => {
expect(
createToolchainCachePlan({ ...options, nodeVersion, projectDir: root }, env),
).toBeUndefined();
});
it("discovers exact project pins but gives explicit inputs priority", () => {
writeFixture(join(root, ".node-version"), "24.15.0\n");
expect(createToolchainCachePlan({ version: "1.3.0", projectDir: root }, env)?.nodeVersion).toBe(
Expand All @@ -63,8 +76,12 @@ describe("toolchain cache selection", () => {
it("accepts immutable preview builds and normalizes a Node v prefix", () => {
const version = `0.0.0-commit.${"a".repeat(40)}`;
expect(
createToolchainCachePlan({ ...options, version, nodeVersion: "v22.23.1" }, env)?.version,
).toBe(version);
createToolchainCachePlan({ ...options, version, nodeVersion: "v22.23.1" }, env),
).toMatchObject({ version, nodeVersion: "22.23.1" });
});
it("accepts an exact prerelease version", () => {
const version = "1.3.0-rc.1";
expect(createToolchainCachePlan({ ...options, version }, env)?.version).toBe(version);
});
it.each(["VP_LOCAL_TGZ", "VP_LOCAL_BINARY", "VP_SKIP_DEPS_INSTALL", "VP_PR_VERSION"])(
"bypasses %s",
Expand Down
6 changes: 3 additions & 3 deletions src/ci/toolchain-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
import { lstatSync, realpathSync, rmSync, statSync } from "node:fs";
import { homedir, tmpdir } from "node:os";
import { isAbsolute, join } from "node:path";
import { valid } from "semver";
import { normalize } from "verkit";
import { isMuslLinux } from "./install-sfw.js";
import { pkgPrNewCommitSha } from "./install-script-urls.js";
import { isWindows } from "./platform.js";
Expand Down Expand Up @@ -96,7 +96,7 @@ export function createToolchainCachePlan(

function cacheableVersion(version: string, env: NodeJS.ProcessEnv): boolean {
const preview = pkgPrNewCommitSha(version);
if (valid(version) !== version || !supportsVitePlusDirs(version)) return false;
if (normalize(version) !== version || !supportsVitePlusDirs(version)) return false;
if (preview && !PREVIEW.test(version)) return false;
if (env.VP_PR_VERSION && env.VP_PR_VERSION !== preview) return false;
return !LOCAL_OVERRIDES.some((name) => env[name]);
Expand All @@ -106,7 +106,7 @@ function exactNodeVersion(selector?: string): string | undefined {
// Do not freeze mutable selectors (including the default LTS) in an immutable cache.
if (!selector || !EXACT_NODE.test(selector)) return;
const version = selector.startsWith("v") ? selector.slice(1) : selector;
return valid(version) || undefined;
return normalize(version) || undefined;
}

export function canRestoreToolchain(plan: ToolchainCachePlan): boolean {
Expand Down
Loading