diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5086b82..3bcbf44 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -108,6 +108,56 @@ jobs: pnpm add is-odd shell: bash + test_dev_engines: + name: Test with devEngines.packageManager + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + version: + - '9.15.5' + - '>=9.15.0' + + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Set up package.json with devEngines.packageManager + run: echo '{"devEngines":{"packageManager":{"name":"pnpm","version":"${{ matrix.version }}","onFail":"download"}}}' > package.json + shell: bash + + - name: Run the action + uses: ./ + + - name: 'Test: which' + run: which pnpm; which pnpx + + - name: 'Test: version' + run: | + set -e + required='${{ matrix.version }}' + actual="$(pnpm --version)" + echo "pnpm version: ${actual}" + + if [ "${required}" = ">=9.15.0" ]; then + min="9.15.0" + if [ "$(printf '%s\n' "${min}" "${actual}" | sort -V | head -n1)" != "${min}" ]; then + echo "Expected pnpm version >= ${min}, but got ${actual}" + exit 1 + fi + else + if [ "${actual}" != "${required}" ]; then + echo "Expected pnpm version ${required}, but got ${actual}" + exit 1 + fi + fi + shell: bash + test_run_install: name: 'Test with run_install (${{ matrix.run_install.name }}, ${{ matrix.os }})' diff --git a/dist/index.js b/dist/index.js index 885a0c2..26f9903 100644 Binary files a/dist/index.js and b/dist/index.js differ diff --git a/src/install-pnpm/bootstrap/exe-lock.json b/src/install-pnpm/bootstrap/exe-lock.json index c660a11..beb363f 100644 --- a/src/install-pnpm/bootstrap/exe-lock.json +++ b/src/install-pnpm/bootstrap/exe-lock.json @@ -5,34 +5,40 @@ "packages": { "": { "dependencies": { - "@pnpm/exe": "10.32.1" + "@pnpm/exe": "11.0.0-beta.3" } }, "node_modules/@pnpm/exe": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-10.32.1.tgz", - "integrity": "sha512-baEtwHeZwmZAdBuuDDL6tbdGg5KpxhPxr3QFfYTGXvY6ws+Z1bN0mQ7ZjcaXBSC1HuLpVXnZ6NsBiaZ2DMv4vg==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-11.0.0-beta.3.tgz", + "integrity": "sha512-yWNlHHdYmvf4c0MCkCzAa4csJDPdA+7yJCbXBUDXMbUu/0Zv/AxtO77q24MwlnBUC0dWeA+0F/pPmdkR9aTV2A==", "hasInstallScript": true, "license": "MIT", + "dependencies": { + "@reflink/reflink": "0.1.19" + }, "bin": { - "pnpm": "pnpm" + "pn": "pn", + "pnpm": "pnpm", + "pnpx": "pnpx", + "pnx": "pnx" }, "funding": { "url": "https://opencollective.com/pnpm" }, "optionalDependencies": { - "@pnpm/linux-arm64": "10.32.1", - "@pnpm/linux-x64": "10.32.1", - "@pnpm/macos-arm64": "10.32.1", - "@pnpm/macos-x64": "10.32.1", - "@pnpm/win-arm64": "10.32.1", - "@pnpm/win-x64": "10.32.1" + "@pnpm/linux-arm64": "11.0.0-beta.3", + "@pnpm/linux-x64": "11.0.0-beta.3", + "@pnpm/macos-arm64": "11.0.0-beta.3", + "@pnpm/macos-x64": "11.0.0-beta.3", + "@pnpm/win-arm64": "11.0.0-beta.3", + "@pnpm/win-x64": "11.0.0-beta.3" } }, "node_modules/@pnpm/linux-arm64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-10.32.1.tgz", - "integrity": "sha512-6uB0B+XvunQwHGzIMk2JCkl4Ur6BtM4XbJSwB/mgpWmXDoX/KTJmgx2lodcTjgJSGSySCHfIVuTR9sj/F2D4EA==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-11.0.0-beta.3.tgz", + "integrity": "sha512-TF2fyuCY9GggR4kfhjo1hMmgn+rIohenwNoH0tLPM7JlBK7/UAIFt1LI+o999tRwTCEw7gnxHFwtI2vyQuDfNw==", "cpu": [ "arm64" ], @@ -41,17 +47,14 @@ "os": [ "linux" ], - "bin": { - "pnpm": "pnpm" - }, "funding": { "url": "https://opencollective.com/pnpm" } }, "node_modules/@pnpm/linux-x64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-10.32.1.tgz", - "integrity": "sha512-AM2tv23Fg7h+nV+adqA/SkZKUysSIEetHfBwYFl8ArgdgkqbGoQy0rAOdKYQBb920CqfexXfI8OA8kPCzRxYng==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-11.0.0-beta.3.tgz", + "integrity": "sha512-7GrLsnSuDH62y486GUTwJdohGIC1ugz9ZJkbKOHgxIAkNGcSTJ1IkkdARtv7/WMmOEwwESDmtpOQ6LmjnpDMSA==", "cpu": [ "x64" ], @@ -60,17 +63,14 @@ "os": [ "linux" ], - "bin": { - "pnpm": "pnpm" - }, "funding": { "url": "https://opencollective.com/pnpm" } }, "node_modules/@pnpm/macos-arm64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-10.32.1.tgz", - "integrity": "sha512-Zr4JkhRbtGVsYgbuGZO0dq/6FPOn072Pdo0ubmqWtc14cUATKgAJD7efG03yqr3MLgtwFHgdtUzZ1WsaYAtUTA==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-11.0.0-beta.3.tgz", + "integrity": "sha512-NQKgI1DURrEiOUzpxL0Mc+yn7DV4tpShqGnjaJLbz8ZCXsX/qhmybebvCG3r+IfSk3P5KID66lcgC/Osiaz0Dg==", "cpu": [ "arm64" ], @@ -79,17 +79,14 @@ "os": [ "darwin" ], - "bin": { - "pnpm": "pnpm" - }, "funding": { "url": "https://opencollective.com/pnpm" } }, "node_modules/@pnpm/macos-x64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/macos-x64/-/macos-x64-10.32.1.tgz", - "integrity": "sha512-Yk6q3oFDu//OniXJxfTSHo+aew1LX81FcbzJAtEkcCeTQ0SLbQT6J3QiOMNikp8n8IjNhsy+bn2bdkUxaw+akA==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/macos-x64/-/macos-x64-11.0.0-beta.3.tgz", + "integrity": "sha512-Ky22KFYHXx8+8WU4KJT9NXVgzFioL2w9pHTQjsqTK70AbxiErscPYhrFIehlCNbXjgs+tGVIy13QNKkiwvmS8w==", "cpu": [ "x64" ], @@ -98,17 +95,14 @@ "os": [ "darwin" ], - "bin": { - "pnpm": "pnpm" - }, "funding": { "url": "https://opencollective.com/pnpm" } }, "node_modules/@pnpm/win-arm64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-10.32.1.tgz", - "integrity": "sha512-P8rsP5IUetpYjr2iwggoswL2qUukYrJoToXWuMyo8immn58CsYxaXsHVQ1Oq1R3XMfmGGWTXLsiJuQ7H991MRg==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-11.0.0-beta.3.tgz", + "integrity": "sha512-7L8TFNDm25m+XYSyhcola3YFd/li6BZzzl56SsyGnZabsvUslMwnDiJad48wOz8IuN7zsrTSGh+X/x6F+GdrFQ==", "cpu": [ "arm64" ], @@ -117,17 +111,14 @@ "os": [ "win32" ], - "bin": { - "pnpm": "pnpm.exe" - }, "funding": { "url": "https://opencollective.com/pnpm" } }, "node_modules/@pnpm/win-x64": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-10.32.1.tgz", - "integrity": "sha512-i24GwbtBO8ojrhp8WWimX7NgZs0UKH1171oRt6qcRL+a+FxE0Eggv2y0KP7ZI7F3+LZMarwr3tnYsZryfciUOg==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-11.0.0-beta.3.tgz", + "integrity": "sha512-Z/6OpMUaIpggXjCtWEhp6kWjiT/2EImhkJAu8AodOORqeNcWouGEq3sO4XU0em6d+pAHmdV0hWMQ2xCUmPVuiA==", "cpu": [ "x64" ], @@ -136,12 +127,156 @@ "os": [ "win32" ], - "bin": { - "pnpm": "pnpm.exe" - }, "funding": { "url": "https://opencollective.com/pnpm" } + }, + "node_modules/@reflink/reflink": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink/-/reflink-0.1.19.tgz", + "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@reflink/reflink-darwin-arm64": "0.1.19", + "@reflink/reflink-darwin-x64": "0.1.19", + "@reflink/reflink-linux-arm64-gnu": "0.1.19", + "@reflink/reflink-linux-arm64-musl": "0.1.19", + "@reflink/reflink-linux-x64-gnu": "0.1.19", + "@reflink/reflink-linux-x64-musl": "0.1.19", + "@reflink/reflink-win32-arm64-msvc": "0.1.19", + "@reflink/reflink-win32-x64-msvc": "0.1.19" + } + }, + "node_modules/@reflink/reflink-darwin-arm64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", + "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-darwin-x64": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", + "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", + "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-arm64-musl": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", + "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-gnu": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", + "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-linux-x64-musl": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", + "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-arm64-msvc": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", + "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@reflink/reflink-win32-x64-msvc": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", + "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/src/install-pnpm/bootstrap/pnpm-lock.json b/src/install-pnpm/bootstrap/pnpm-lock.json index df0f571..0fd63ad 100644 --- a/src/install-pnpm/bootstrap/pnpm-lock.json +++ b/src/install-pnpm/bootstrap/pnpm-lock.json @@ -5,20 +5,22 @@ "packages": { "": { "dependencies": { - "pnpm": "latest" + "pnpm": "11.0.0-beta.3" } }, "node_modules/pnpm": { - "version": "10.32.1", - "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-10.32.1.tgz", - "integrity": "sha512-pwaTjw6JrBRWtlY+q07fHR+vM2jRGR/FxZeQ6W3JGORFarLmfWE94QQ9LoyB+HMD5rQNT/7KnfFe8a1Wc0jyvg==", + "version": "11.0.0-beta.3", + "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-11.0.0-beta.3.tgz", + "integrity": "sha512-6PrfRjycZV4vRX6ttG9oR6pOgbI2/OcF2QLOzHm35UcRuvtqP4zf3wQfAAPwEbeu1uAbpSg/Q5cL8h32tumy6Q==", "license": "MIT", "bin": { - "pnpm": "bin/pnpm.cjs", - "pnpx": "bin/pnpx.cjs" + "pn": "bin/pnpm.mjs", + "pnpm": "bin/pnpm.mjs", + "pnpx": "bin/pnpx.mjs", + "pnx": "bin/pnpx.mjs" }, "engines": { - "node": ">=18.12" + "node": ">=22.13" }, "funding": { "url": "https://opencollective.com/pnpm" diff --git a/src/install-pnpm/run.ts b/src/install-pnpm/run.ts index 6499130..c4a484a 100644 --- a/src/install-pnpm/run.ts +++ b/src/install-pnpm/run.ts @@ -13,7 +13,12 @@ const BOOTSTRAP_PNPM_PACKAGE_JSON = JSON.stringify({ private: true, dependencies const BOOTSTRAP_EXE_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { '@pnpm/exe': exeLock.packages['node_modules/@pnpm/exe'].version } }) export async function runSelfInstaller(inputs: Inputs): Promise { - const { version, dest, packageJsonFile, standalone } = inputs + const { version, dest, packageJsonFile } = inputs + + // pnpm v11 requires Node >= 22.13; use standalone (exe) bootstrap which + // bundles its own Node.js when the system Node is too old + const systemNode = await getSystemNodeVersion() + const standalone = inputs.standalone || systemNode.major < 22 || (systemNode.major === 22 && systemNode.minor < 13) // Install bootstrap pnpm via npm (integrity verified by committed lockfile) await rm(dest, { recursive: true, force: true }) @@ -40,13 +45,13 @@ export async function runSelfInstaller(inputs: Inputs): Promise { await mkdir(pnpmHome, { recursive: true }) const target = standalone ? path.join('..', '@pnpm', 'exe', 'pnpm') - : path.join('..', 'pnpm', 'bin', 'pnpm.cjs') + : path.join('..', 'pnpm', 'bin', 'pnpm.mjs') await symlink(target, pnpmBinLink) } const bootstrapPnpm = standalone ? path.join(dest, 'node_modules', '@pnpm', 'exe', 'pnpm') - : path.join(dest, 'node_modules', 'pnpm', 'bin', 'pnpm.cjs') + : path.join(dest, 'node_modules', 'pnpm', 'bin', 'pnpm.mjs') // Determine the target version const targetVersion = readTargetVersion({ version, packageJsonFile }) @@ -70,15 +75,17 @@ function readTargetVersion(opts: { const { version, packageJsonFile } = opts const { GITHUB_WORKSPACE } = process.env - let packageManager: unknown + let packageManager: string | undefined + let devEngines: { packageManager?: { name?: string; version?: string } } | undefined if (GITHUB_WORKSPACE) { try { const content = readFileSync(path.join(GITHUB_WORKSPACE, packageJsonFile), 'utf8'); - ({ packageManager } = packageJsonFile.endsWith(".yaml") + const manifest = packageJsonFile.endsWith(".yaml") ? parseYaml(content, { merge: true }) : JSON.parse(content) - ) + packageManager = manifest.packageManager + devEngines = manifest.devEngines } catch (error: unknown) { // Swallow error if package.json doesn't exist in root if (!util.types.isNativeError(error) || !('code' in error) || error.code !== 'ENOENT') throw error @@ -100,9 +107,13 @@ Remove one of these versions to avoid version mismatch errors like ERR_PNPM_BAD_ return version } + // pnpm will automatically download and switch to the right version if (typeof packageManager === 'string' && packageManager.startsWith('pnpm@')) { - // Strip the "pnpm@" prefix and any "+sha..." hash suffix - return packageManager.replace('pnpm@', '').replace(/\+.*$/, '') + return undefined + } + + if (devEngines?.packageManager?.name === 'pnpm' && devEngines.packageManager.version) { + return undefined } if (!GITHUB_WORKSPACE) { @@ -115,7 +126,21 @@ Otherwise, please specify the pnpm version in the action configuration.`) throw new Error(`No pnpm version is specified. Please specify it by one of the following ways: - in the GitHub Action config with the key "version" - - in the package.json with the key "packageManager"`) + - in the package.json with the key "packageManager" + - in the package.json with the key "devEngines.packageManager"`) +} + +function getSystemNodeVersion(): Promise<{ major: number; minor: number }> { + return new Promise((resolve) => { + const cp = spawn('node', ['--version'], { stdio: ['pipe', 'pipe', 'pipe'], shell: process.platform === 'win32' }) + let output = '' + cp.stdout.on('data', (data: Buffer) => { output += data.toString() }) + cp.on('close', () => { + const match = output.match(/^v(\d+)\.(\d+)/) + resolve(match ? { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) } : { major: 0, minor: 0 }) + }) + cp.on('error', () => resolve({ major: 0, minor: 0 })) + }) } function runCommand(cmd: string, args: string[], opts: { cwd: string }): Promise {