Skip to content

Commit 6523ce9

Browse files
authored
feat: support pnpm v11 by downloading from GitHub releases (#12)
## What Lowers the supported floor to **pnpm v11** and switches the download source from the npm registry to **pnpm's GitHub releases**. ## Why the source changed The original README claimed v11 couldn't be used because it "isn't a standalone executable." Investigating (and CI) showed the real situation is subtler: - pnpm's **npm** per-platform packages differ by major, and for **v11** (`@pnpm/<os>-<arch>`) they are only **Node-SEA launchers** that `require` the JS `dist/` shipped separately in the main `@pnpm/exe` package. Downloading a single npm package yields a broken install (`Cannot find module dist/pnpm.mjs`) — caught by CI. - pnpm's **GitHub release** archives (`pnpm-<os>-<arch>.tar.gz`, `pnpm-win32-<arch>.zip`) bundle the launcher **and** its `dist/` together as one self-contained download, for **both v11 and v12**, under one consistent naming scheme. So downloading from GitHub releases unifies v11 and v12 under a single code path and Just Works. ## Changes - `src/install-pnpm/download.ts`: resolve version against npm (exact/range/dist-tag), then download the matching self-contained archive from GitHub releases and extract it whole. Integrity is verified against the **SHA-256 `digest`** GitHub publishes per asset (replacing the previous npm sha512 check). `MIN_SUPPORTED_MAJOR` 12 → 11. Clear error when a resolved version has no GitHub release (some prereleases are npm-only). - New `token` input (default `${{ github.token }}`) authenticates the release API lookup to avoid the anonymous rate limit. - `README.md` / `action.yml`: describe the GitHub-releases flow; document the `token` input; note v11 has no Intel-macOS (`darwin-x64`) binary. - `.github/workflows/test.yaml`: add v11 coverage — a cross-OS `smoke-v11` job (ubuntu x64/arm64, macOS arm64, Windows) and a `runtime-node-pnpm11` job (runtime install + `pnpm install` on v11). Repin v12 test versions from `alpha.19` (no GitHub release) to `alpha.21`. - Rebuilt `dist/index.js`.
1 parent 6bfbb82 commit 6523ce9

7 files changed

Lines changed: 421 additions & 245 deletions

File tree

.github/workflows/test.yaml

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ jobs:
2020
matrix:
2121
include:
2222
- os: ubuntu-latest
23-
version: '12.0.0-alpha.19'
23+
version: '12.0.0-alpha.21'
2424
- os: ubuntu-24.04-arm
25-
version: '12.0.0-alpha.19'
25+
version: '12.0.0-alpha.21'
2626
- os: macos-latest
27-
version: '12.0.0-alpha.19'
27+
version: '12.0.0-alpha.21'
2828
- os: windows-latest
29-
version: '12.0.0-alpha.19'
29+
version: '12.0.0-alpha.21'
3030

3131
steps:
3232
- uses: actions/checkout@v6
@@ -56,6 +56,104 @@ jobs:
5656
fi
5757
shell: bash
5858

59+
smoke-v11:
60+
# v11's release archive bundles a Node-SEA launcher plus a sibling `dist/`,
61+
# unlike v12's single self-contained binary. Verify the action downloads and
62+
# lays it out correctly and puts pnpm on PATH across OSes/arches. (macos-latest
63+
# is arm64; pnpm v11 ships no Intel-macOS binary, so no macos-13 entry here.)
64+
name: 'Smoke pnpm 11 (${{ matrix.os }})'
65+
runs-on: ${{ matrix.os }}
66+
strategy:
67+
fail-fast: false
68+
matrix:
69+
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
70+
71+
steps:
72+
- uses: actions/checkout@v6
73+
74+
- id: pnpm
75+
uses: ./
76+
with:
77+
version: '11.17.0'
78+
install: false
79+
80+
- name: 'Test: pnpm 11 on PATH matches request'
81+
env:
82+
BIN_DEST: ${{ steps.pnpm.outputs.bin-dest }}
83+
run: |
84+
set -e
85+
which pnpm
86+
actual="$(pnpm --version)"
87+
echo "pnpm --version: ${actual}"
88+
if [ "${actual}" != "11.17.0" ]; then
89+
echo "Expected pnpm 11.17.0, got ${actual}"
90+
exit 1
91+
fi
92+
bin_dest_version="$("$BIN_DEST/pnpm" --version)"
93+
if [ "${bin_dest_version}" != "11.17.0" ]; then
94+
echo "Expected 11.17.0 via bin_dest, got ${bin_dest_version}"
95+
exit 1
96+
fi
97+
shell: bash
98+
99+
runtime-node-pnpm11:
100+
# The whole premise of supporting v11 is that `pnpm runtime` works there.
101+
# Install pnpm v11, install a runtime through it, and run `pnpm install`.
102+
name: 'Runtime node + install on pnpm 11'
103+
runs-on: ubuntu-latest
104+
steps:
105+
- uses: actions/checkout@v6
106+
107+
- name: Set up a synthetic package.json
108+
# Use a fresh manifest (and drop the repo lockfile) so `pnpm install`
109+
# under v11 resolves cleanly rather than against a v12-authored lockfile.
110+
run: |
111+
rm -f pnpm-lock.yaml
112+
cat > package.json <<'EOF'
113+
{
114+
"dependencies": {
115+
"is-odd": "3.0.1"
116+
}
117+
}
118+
EOF
119+
shell: bash
120+
121+
- id: pnpm
122+
uses: ./
123+
with:
124+
version: '11.17.0'
125+
runtime: node@22
126+
127+
- name: 'Test: pnpm 11, node 22, and install all worked'
128+
env:
129+
OUT_NAME: ${{ steps.pnpm.outputs.runtime-name }}
130+
OUT_VERSION: ${{ steps.pnpm.outputs.runtime-version }}
131+
run: |
132+
set -e
133+
pnpm_version="$(pnpm --version)"
134+
echo "pnpm --version: ${pnpm_version}"
135+
case "${pnpm_version}" in
136+
11.*) ;;
137+
*) echo "Expected pnpm 11.x, got ${pnpm_version}"; exit 1 ;;
138+
esac
139+
which node
140+
node_version="$(node --version)"
141+
echo "node --version: ${node_version}"
142+
case "${node_version}" in
143+
v22.*) ;;
144+
*) echo "Expected node v22.x, got ${node_version}"; exit 1 ;;
145+
esac
146+
if [ "${OUT_NAME}" != "node" ]; then
147+
echo "Expected outputs.runtime-name=node, got ${OUT_NAME}"; exit 1
148+
fi
149+
if [ "${OUT_VERSION}" != "22" ]; then
150+
echo "Expected outputs.runtime-version=22, got ${OUT_VERSION}"; exit 1
151+
fi
152+
if [ ! -d node_modules/is-odd ]; then
153+
echo "Expected pnpm install to populate node_modules/is-odd"; exit 1
154+
fi
155+
shell: bash
156+
59157
version-from-dist-tag:
60158
# `version` may be an npm dist-tag; it is resolved against the main
61159
# `pnpm` package's dist-tags.
@@ -127,7 +225,7 @@ jobs:
127225
- id: pnpm
128226
uses: ./
129227
with:
130-
version: '12.0.0-alpha.19'
228+
version: '12.0.0-alpha.21'
131229
runtime: node@${{ matrix.major }}
132230

133231
- name: 'Test: node binary on PATH'
@@ -168,7 +266,7 @@ jobs:
168266
- uses: actions/checkout@v6
169267
- uses: ./
170268
with:
171-
version: '12.0.0-alpha.19'
269+
version: '12.0.0-alpha.21'
172270
runtime: bun@latest
173271
- name: 'Test: bun on PATH'
174272
run: |
@@ -184,7 +282,7 @@ jobs:
184282
- uses: actions/checkout@v6
185283
- uses: ./
186284
with:
187-
version: '12.0.0-alpha.19'
285+
version: '12.0.0-alpha.21'
188286
runtime: deno@2
189287
- name: 'Test: deno on PATH'
190288
run: |
@@ -209,7 +307,7 @@ jobs:
209307
rm -f pnpm-lock.yaml
210308
cat > package.json <<'EOF'
211309
{
212-
"packageManager": "pnpm@12.0.0-alpha.19",
310+
"packageManager": "pnpm@12.0.0-alpha.21",
213311
"devEngines": {
214312
"runtime": { "name": "node", "version": "^22.0.0", "onFail": "download" }
215313
},
@@ -266,7 +364,7 @@ jobs:
266364
rm -f pnpm-lock.yaml
267365
cat > package.json <<'EOF'
268366
{
269-
"packageManager": "pnpm@12.0.0-alpha.19",
367+
"packageManager": "pnpm@12.0.0-alpha.21",
270368
"devEngines": {
271369
"runtime": { "name": "node", "version": "^20.0.0", "onFail": "download" }
272370
},
@@ -311,7 +409,7 @@ jobs:
311409
rm -f pnpm-lock.yaml
312410
cat > package.json <<'EOF'
313411
{
314-
"packageManager": "pnpm@12.0.0-alpha.19",
412+
"packageManager": "pnpm@12.0.0-alpha.21",
315413
"devEngines": {
316414
"runtime": { "name": "node", "version": "^20.0.0", "onFail": "download" }
317415
}
@@ -348,7 +446,7 @@ jobs:
348446
rm -f pnpm-lock.yaml
349447
cat > package.json <<'EOF'
350448
{
351-
"packageManager": "pnpm@12.0.0-alpha.19",
449+
"packageManager": "pnpm@12.0.0-alpha.21",
352450
"dependencies": {
353451
"is-odd": "3.0.1"
354452
}
@@ -358,7 +456,7 @@ jobs:
358456

359457
- uses: ./
360458
with:
361-
version: '12.0.0-alpha.19'
459+
version: '12.0.0-alpha.21'
362460
install: false
363461

364462
- name: 'Test: node_modules was not populated'
@@ -385,7 +483,7 @@ jobs:
385483
- id: pnpm
386484
uses: ./
387485
with:
388-
version: '12.0.0-alpha.19'
486+
version: '12.0.0-alpha.21'
389487

390488
- name: 'Test: pnpm works, runtime outputs are empty'
391489
env:

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@
22

33
Install pnpm **and** a JavaScript runtime (Node.js, Bun, or Deno) in a single GitHub Actions step.
44

5-
Since v12, pnpm is a standalone executable — the action downloads the native binary for the runner's platform directly from the npm registry (no Node.js or npm needed) and then uses `pnpm runtime set` to install the requested runtime. The runtime binary is placed on `PATH` for subsequent steps, replacing the need for `actions/setup-node`, `oven-sh/setup-bun`, or `denoland/setup-deno`. `pnpm install` runs automatically when a `package.json` is present.
5+
pnpm ships a self-contained release binary — the action downloads it for the runner's platform directly from pnpm's GitHub releases (no Node.js or npm needed) and then uses `pnpm runtime set` to install the requested runtime. The runtime binary is placed on `PATH` for subsequent steps, replacing the need for `actions/setup-node`, `oven-sh/setup-bun`, or `denoland/setup-deno`. `pnpm install` runs automatically when a `package.json` is present.
66

77
> [!NOTE]
8-
> This action installs pnpm v12 and newer only. To install pnpm 11 or older (which are Node.js programs, not standalone executables), use [`pnpm/action-setup`](https://github.com/pnpm/action-setup) instead.
8+
> This action installs pnpm v11 and newer only — it relies on pnpm's self-contained release binaries and the `pnpm runtime` command, both available from v11. To install pnpm 10 or older, use [`pnpm/action-setup`](https://github.com/pnpm/action-setup) instead.
9+
>
10+
> One caveat: pnpm v11 publishes no binary for Intel macOS (`darwin-x64`); use v12 or newer on Intel macOS runners.
911
1012
If your `package.json` declares `devEngines.runtime`, the action picks up the runtime and version from there automatically — no inputs required.
1113

1214
## Inputs
1315

1416
| Name | Description |
1517
|------|-------------|
16-
| `version` | Version of pnpm to install: an exact version, a semver range (`^12.0.0`), or a dist-tag (`next-12`). Must resolve to v12 or newer. Optional when `packageManager` or `devEngines.packageManager` is set in `package.json`. |
18+
| `version` | Version of pnpm to install: an exact version, a semver range (`^12.0.0`), or a dist-tag (`next-12`). Must resolve to v11 or newer. Optional when `packageManager` or `devEngines.packageManager` is set in `package.json`. |
1719
| `dest` | Where to store pnpm files. Defaults to `~/setup-pnpm`. |
1820
| `runtime` | Runtime spec, in `<name>` or `<name>@<version>` form (e.g. `node@22`, `node@lts`, `bun@latest`, `deno@2`). Supported names: `node`, `bun`, `deno`. When the version is omitted, falls back to `devEngines.runtime` in `package.json`, then to `lts` (for `node`) / `latest`. If the input itself is omitted, the action reads `devEngines.runtime` from `package.json`. |
1921
| `cache` | Cache the pnpm store directory. Default: `false`. |
2022
| `cache-dependency-path` | Path(s) to the pnpm lockfile, used to compute the cache key. Default: `pnpm-lock.yaml`. |
2123
| `package-json-file` | Path to `package.json` (relative to `GITHUB_WORKSPACE`). Default: `package.json`. |
2224
| `install` | Run `pnpm install` after setup. Default: `true`. Set to `false` for jobs that only need pnpm itself (e.g. `pnpm audit`, lockfile-only regeneration). |
25+
| `token` | GitHub token used to look up the pnpm release and its asset checksum via the GitHub API. Defaults to `${{ github.token }}`, which lifts the low anonymous rate limit. Rarely needs to be set. |
2326

2427
## Outputs
2528

@@ -107,7 +110,7 @@ For jobs that only need pnpm itself — e.g. `pnpm audit`, lockfile-only regener
107110

108111
## How it works
109112

110-
1. The action resolves the requested version (exact, range, or dist-tag) against the npm registry, downloads the `@pnpm/exe.<os>-<arch>` tarball for the runner's platform, verifies its integrity, and places the `pnpm` executable (plus the `pnpx`, `pn`, and `pnx` aliases) into `dest`. No Node.js or npm is involved.
113+
1. The action resolves the requested version (exact, range, or dist-tag) against the npm registry, then downloads the matching self-contained release archive for the runner's platform (`pnpm-<os>-<arch>.tar.gz`, or `pnpm-win32-<arch>.zip` on Windows) from pnpm's GitHub releases. It verifies the archive against the SHA-256 digest GitHub publishes for the asset, extracts the `pnpm` executable (and, for pnpm builds that need it, its bundled `dist/`), and links the `pnpx`, `pn`, and `pnx` aliases into `dest`. No Node.js or npm is involved.
111114
2. `PNPM_HOME` is exported and `dest` plus `$PNPM_HOME/bin` are added to `PATH`.
112115
3. The action runs `pnpm runtime set <name> <version> -g`, which downloads the requested runtime into `$PNPM_HOME/bin` — making `node`, `bun`, or `deno` available to later workflow steps.
113116
4. If a `package.json` exists in the workspace, the action runs `pnpm install` (unless `install: false` is set). When the `runtime` input is set, `--no-runtime` is appended so the just-installed runtime isn't shadowed by a different version declared in `devEngines.runtime`.

action.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ inputs:
88
description: |
99
Version of pnpm to install: an exact version (`12.0.0-alpha.17`), a
1010
semver range (`^12.0.0`), or an npm dist-tag (`next-12`). Must resolve
11-
to pnpm v12 or newer — since v12, pnpm is a standalone executable, and
12-
the action downloads it directly without needing Node.js or npm.
11+
to pnpm v11 or newer — the action downloads pnpm's native per-platform
12+
executable directly, without needing Node.js or npm.
1313
1414
When omitted, the version is read from `devEngines.packageManager` or
1515
`packageManager` in the project's package.json.
@@ -59,6 +59,13 @@ inputs:
5959
need pnpm itself (e.g. `pnpm audit`, lockfile-only regeneration).
6060
required: false
6161
default: 'true'
62+
token:
63+
description: |
64+
GitHub token used to look up the pnpm release (and its asset checksum)
65+
via the GitHub API. Defaults to the workflow's automatic token, which
66+
lifts the low anonymous API rate limit. Rarely needs to be set.
67+
required: false
68+
default: ${{ github.token }}
6269
outputs:
6370
dest:
6471
description: Expanded path of inputs#dest

0 commit comments

Comments
 (0)