Compare commits

...

10 Commits

Author SHA1 Message Date
Eric Nemchik 0977fd9972 docs: Update README to include devEngines.packageManager (#273)
Support added in #211 and #256
2026-08-03 13:44:13 +02:00
Jamie Tanna 48261aca05 fix: update pnpm to v11.19.0 (#283)
* fix: update pnpm to v11.19.0

Via `scripts/update-bootstrap.mjs 11.19.0`

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>

* fixup! fix: update pnpm to v11.19.0

As it needs to be rebuilt on Linux.

---------

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-08-03 11:54:52 +02:00
Jamie Tanna 75677f717d ci: use pnpm 11 for pr-check (#284)
Noticed while working on #283.
2026-08-03 11:02:17 +02:00
Sukka 769ae71fb3 refactor: introduce restore keys for cache (#280) 2026-08-03 10:32:23 +02:00
Kevin Cui 6fed91f804 docs(README): point users to the successor pnpm/setup action (#282)
* docs(README): point users to the successor pnpm/setup action

The pnpm setup action has moved to `pnpm/setup`, which installs
pnpm v11+ as a self-contained native executable and can install a
JavaScript runtime (Node.js, Bun, or Deno) in the same step,
replacing `actions/setup-node`.

Add a notice at the top of the README, a migration section with a
before/after workflow example and an input/output mapping table,
and mention the successor in the Notes section. The rest of the
docs stay intact since `pnpm/action-setup` remains the way to
install pnpm v10 and older.

Signed-off-by: Kevin Cui <bh@bugs.cc>

* docs(README): address review feedback on migration section

Add an explicit version: 11 to the migration example and note when the
input can be omitted, since pnpm/setup requires pnpm v11+ and a repo
migrating from version: 10 may have no packageManager field or one that
pins v10.

Move the v2 upgrade warning below the page title so the two callouts
are no longer adjacent blockquotes (markdownlint MD028), keeping the
successor notice and the legacy warning as separate blocks.

Fix the comma splice and use "set up" as the verb in the Notes
section.

Signed-off-by: Kevin Cui <bh@bugs.cc>

---------

Signed-off-by: Kevin Cui <bh@bugs.cc>
2026-08-02 22:56:56 -04:00
Zoltan Kochan 0ebf47130e fix: update pnpm to v11.7.0 (#267)
* fix: update pnpm to v11.7.0

* fix: update bundle
2026-06-15 14:04:13 +02:00
mungodewar 0e279bb959 fix: update pnpm to 11.1.1 (#248)
* fix: update pnpm to v11.1.1

* fix: update bundle

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-12 14:32:25 +02:00
Zoltan Kochan 3e835812ef fix: drop patchPnpmEnv so standalone+self-update works on Windows (#258)
`patchPnpmEnv` prepended `dest/node_modules/.bin` to PATH before
spawning `pnpm install` / `pnpm store prune`. On Windows in standalone
mode, `.bin/pnpm.cmd` is an npm-created shim that always points at the
BOOTSTRAP pnpm (currently 11.0.4) — the binary npm linked when it
installed `@pnpm/exe` into `node_modules`. The self-updated pnpm
written by `pnpm self-update` lives at `$PNPM_HOME/bin`, which is
separately added to PATH via `addPath()` in install-pnpm.

When the user requested a pnpm version different from the bootstrap
under `standalone: true` on Windows, patchPnpmEnv's `.bin` entry
shadowed the self-updated `$PNPM_HOME/bin` and the action's internal
`pnpm install` ran on the bootstrap. On a pnpm 11.0.x bootstrap this
broke any 11.1+ install flag (e.g. `--no-runtime`), reporting:

    ERROR  Unknown option: 'runtime'

POSIX standalone got lucky because `.bin` and `$PNPM_HOME` resolve to
the same directory there. Non-standalone never tripped on this since
the `.bin/pnpm` symlink for a regular `pnpm` package keeps working
across self-updates.

Removed `patchPnpmEnv` and the now-empty `src/utils/` module.
`spawnSync` now inherits `process.env`, whose PATH is already
correctly fronted by `$PNPM_HOME/bin` and `$PNPM_HOME` via the
`addPath` calls in install-pnpm.

Added `standalone_windows_self_update` to test.yaml as a regression
guard: standalone on Windows + target 11.1.0 + `run_install` with
`--no-runtime`. With the previous code, the install would have run
under the bootstrap (11.0.4) and errored on the unknown flag.

Originally found while building pnpm/setup (the new combined
pnpm + runtime action).
2026-05-11 22:48:51 +02:00
Andrew Haines 551b42e879 docs(README): fix cache_dependency_path type (#257)
* docs(README): fix `cache_dependency_path` type

* docs(README): add multiple lockfile example

* docs: update input description to match README
2026-05-11 16:41:41 +02:00
Zoltan Kochan 739bfe42ca fix: self-update bootstrap to packageManager-pinned version (#233) (#256)
* fix: self-update bootstrap to packageManager-pinned version (#233)

When package.json pins pnpm via `packageManager` or an exact
`devEngines.packageManager.version`, self-update the bootstrap up
front. The bootstrap's `pnpm store path` skips pnpm's auto-switch
(the `store` command sets `skipPackageManagerCheck = true`), so it
reports the bootstrap's `STORE_VERSION` while the user's actual
install runs under the pinned version and writes to a different
STORE_VERSION — breaking `cache: true` and `setup-node`'s
`cache: pnpm` on cold caches.

* fix: check bin/ dir instead of pnpm.exe for self-update detection

On Windows pnpm self-update writes `.bin/bin/pnpm` (a JS launcher),
not `.bin/bin/pnpm.exe`, so the previous existsSync probe always
fell back to pnpmHome and the bin_dest output pointed at the
bootstrap pnpm. Check whether the `bin` directory itself exists.

* fix: self-update accepts ranges; drop exact-semver gate

`pnpm self-update` resolves semver ranges to a specific version, so
`devEngines.packageManager.version: ">=10 <11"` can also go through
the self-update path. That makes readTargetVersion total — it always
returns a string or throws — so the runtime auto-switch fallback (and
the `pnpm_config_pm_on_fail=download` export from #252) is no longer
reachable and gets removed.

Adds a range case to the cache_store_path matrix.
2026-05-11 14:06:33 +02:00
12 changed files with 408 additions and 245 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with: with:
run_install: true run_install: true
version: 9 version: 11
- name: Update dist/index.js - name: Update dist/index.js
run: pnpm run build run: pnpm run build
+96
View File
@@ -194,6 +194,102 @@ jobs:
pnpm add is-odd pnpm add is-odd
shell: bash shell: bash
standalone_windows_self_update:
# Regression guard for the patchPnpmEnv PATH-shadow bug. When
# standalone: true on Windows AND the requested pnpm differs from the
# bootstrap, the previous patchPnpmEnv prepended node_modules/.bin to
# PATH; that directory contains an npm-created pnpm.cmd shim pointing
# at the BOOTSTRAP pnpm, which shadowed the self-updated pnpm at
# $PNPM_HOME/bin and caused `pnpm install` inside the action to run
# under the bootstrap version. Exercising a newer-pnpm-only flag
# (`--no-runtime`, added in 11.1.0) makes the regression assertable:
# if the bootstrap (11.0.4) handles the install, it errors with
# "Unknown option: 'runtime'".
name: 'Standalone Windows self-update (PATH regression)'
runs-on: windows-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up package.json with a minimal manifest
# run_install needs a manifest to install against. Removing the
# repo's existing pnpm-lock.yaml avoids frozen-lockfile mismatch.
run: |
rm -f pnpm-lock.yaml
echo '{"name":"sw","private":true,"packageManager":"pnpm@11.1.0"}' > package.json
shell: bash
- name: Run the action
uses: ./
with:
version: 11.1.0
standalone: true
run_install: |
args: ['--no-runtime']
- name: 'Test: pnpm install completed under the self-updated pnpm'
# If the bug recurs, the previous step's run_install will have failed
# the job with "Unknown option: 'runtime'", so reaching this step
# implies success. Still verify the version on PATH matches request.
env:
REQUIRED: '11.1.0'
run: |
set -e
actual="$(pnpm --version)"
echo "pnpm --version: ${actual}"
if [ "${actual}" != "${REQUIRED}" ]; then
echo "Expected pnpm ${REQUIRED}, got ${actual}"
exit 1
fi
shell: bash
cache_store_path:
# Regression guard for #233. When package.json pins a pnpm major that
# differs from the bootstrap pnpm's major, the bootstrap reports its
# own STORE_VERSION from `pnpm store path` (the `store` command skips
# pnpm's auto-switch). The user's actual `pnpm install` runs under the
# pinned version and writes to a different STORE_VERSION, so the post
# step's saveCache then fails with "Path Validation Error". The fix is
# to self-update the bootstrap to the pinned version up front.
name: 'Cache store path matches install (#233): ${{ matrix.label }}'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- label: 'packageManager pnpm@10.33.0'
manifest: '{"packageManager":"pnpm@10.33.0","dependencies":{"is-odd":"3.0.1"}}'
- label: 'devEngines exact pnpm@10.33.0'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":"10.33.0"}},"dependencies":{"is-odd":"3.0.1"}}'
- label: 'devEngines range >=10 <11'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":">=10 <11"}},"dependencies":{"is-odd":"3.0.1"}}'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up package.json
run: echo '${{ matrix.manifest }}' > package.json
shell: bash
- id: pnpm
uses: ./
with:
cache: true
run_install: |
- args: [--no-frozen-lockfile]
- name: 'Test: store path computed by the action exists on disk'
run: |
set -e
actual="$(pnpm store path --silent)"
echo "pnpm store path: ${actual}"
if [ ! -d "${actual}" ]; then
echo "Expected store path to exist on disk; cache save would fail"
exit 1
fi
shell: bash
run_install: run_install:
name: 'run_install (${{ matrix.run_install.name }})' name: 'run_install (${{ matrix.run_install.name }})'
+85 -8
View File
@@ -1,18 +1,68 @@
> ## :warning: Upgrade from v2! > [!IMPORTANT]
> **This action has a successor: [`pnpm/setup`](https://github.com/pnpm/setup).**
> >
> The v2 version of this action [has stopped working](https://github.com/pnpm/action-setup/issues/135) with newer Node.js versions. Please, upgrade to the latest version to fix any issues. > For pnpm v11 and newer, use [`pnpm/setup`](https://github.com/pnpm/setup) instead. It downloads pnpm's self-contained release binary (no Node.js or npm required) and can install a JavaScript runtime (Node.js, Bun, or Deno) in the same step, replacing `actions/setup-node`.
>
> `pnpm/action-setup` remains the action to use for installing pnpm v10 and older. See [Migrating to pnpm/setup](#migrating-to-pnpmsetup) below.
# Setup pnpm # Setup pnpm
Install pnpm package manager. Install pnpm package manager.
> ## :warning: Upgrade from v2!
>
> The v2 version of this action [has stopped working](https://github.com/pnpm/action-setup/issues/135) with newer Node.js versions. Please, upgrade to the latest version to fix any issues.
## Migrating to pnpm/setup
[`pnpm/setup`](https://github.com/pnpm/setup) installs pnpm v11+ as a native standalone executable and can install Node.js, Bun, or Deno in the same step, so a typical workflow no longer needs `actions/setup-node` or an explicit `pnpm install` step:
```yaml
steps:
- uses: actions/checkout@v6
# Before:
# - uses: pnpm/action-setup@v6
# with:
# version: 10
# cache: true
# - uses: actions/setup-node@v4
# with:
# node-version: 22
# - run: pnpm install
# After:
- uses: pnpm/setup@v1
with:
version: 11
runtime: node@22
cache: true
```
The `version` input can be omitted only when `packageManager` (or `devEngines.packageManager`) in `package.json` declares pnpm v11 or newer; otherwise keep it explicit, since `pnpm/setup` requires pnpm v11+.
Input and output changes:
| `pnpm/action-setup` | `pnpm/setup` | Notes |
| ------------------- | ------------ | ----- |
| `version` | `version` | Must resolve to pnpm v11 or newer. As before, it can be omitted when `packageManager` (or `devEngines.packageManager`) is set in `package.json`. |
| `dest` | `dest` | Unchanged. |
| `run_install` | `install` | `pnpm/setup` runs `pnpm install` automatically when a `package.json` is present (`install: true` by default); set `install: false` to skip it. The object/array form (`recursive`, `cwd`, `args`) is not supported — run those commands in separate steps. |
| `cache` | `cache` | Unchanged. |
| `cache_dependency_path` | `cache-dependency-path` | Renamed to kebab-case. |
| `package_json_file` | `package-json-file` | Renamed to kebab-case. |
| `standalone` | removed | `pnpm/setup` always installs the standalone native executable. |
| n/a | `runtime` | New: installs Node.js, Bun, or Deno (e.g. `node@22`, `bun@latest`, `deno@2`), or reads `devEngines.runtime` from `package.json`. |
| n/a | `token` | New: GitHub token for release lookup; defaults to `${{ github.token }}` and rarely needs to be set. |
| `bin_dest` (output) | `bin-dest` (output) | Renamed to kebab-case. New outputs `runtime-name` and `runtime-version` describe the installed runtime. |
## Inputs ## Inputs
### `version` ### `version`
Version of pnpm to install. Version of pnpm to install.
**Optional** when there is a [`packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html). **Optional** when there is a [`packageManager` or `devEngines.packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html).
otherwise, this field is **required** It supports npm versioning scheme, it could be an exact version (such as `10.9.8`), or a version range (such as `10`, `10.x.x`, `10.9.x`, `^10.9.8`, `*`, etc.), or `latest`. otherwise, this field is **required** It supports npm versioning scheme, it could be an exact version (such as `10.9.8`), or a version range (such as `10`, `10.x.x`, `10.9.x`, `^10.9.8`, `*`, etc.), or `latest`.
@@ -48,11 +98,11 @@ If `run_install` is a YAML string representation of either an object or an array
### `cache_dependency_path` ### `cache_dependency_path`
**Optional** (_type:_ `string|string[]`, _default:_ `pnpm-lock.yaml`) File path to the pnpm lockfile, which contents hash will be used as a cache key. **Optional** (_type:_ `string`, _default:_ `pnpm-lock.yaml`) File path to the pnpm lockfile, whose contents hash will be used as a cache key. Accepts multiple paths delimited by newlines.
### `package_json_file` ### `package_json_file`
**Optional** (_type:_ `string`, _default:_ `package.json`) File path to the `package.json`/[`package.yaml`](https://github.com/pnpm/pnpm/pull/1799) to read "packageManager" configuration. **Optional** (_type:_ `string`, _default:_ `package.json`) File path to the `package.json`/[`package.yaml`](https://github.com/pnpm/pnpm/pull/1799) to read `packageManager` or `devEngines.packageManager` configuration.
### `standalone` ### `standalone`
@@ -74,7 +124,7 @@ Location of `pnpm` and `pnpx` command.
### Install only pnpm without `packageManager` ### Install only pnpm without `packageManager`
This works when the repo either doesn't have a `package.json` or has a `package.json` but it doesn't specify `packageManager`. This works when the repo either doesn't have a `package.json` or has a `package.json` but it doesn't specify `packageManager` or `devEngines.packageManager`.
```yaml ```yaml
on: on:
@@ -93,7 +143,7 @@ jobs:
### Install only pnpm with `packageManager` ### Install only pnpm with `packageManager`
Omit `version` input to use the version in the [`packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html). Omit `version` input to use the version in the [`packageManager` or `devEngines.packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html).
```yaml ```yaml
on: on:
@@ -158,9 +208,36 @@ jobs:
**Note:** You don't need to run `pnpm store prune` at the end; post-action has already taken care of that. **Note:** You don't need to run `pnpm store prune` at the end; post-action has already taken care of that.
### Cache dependencies from multiple lockfiles
```yaml
on:
- push
- pull_request
jobs:
cache-and-install-multiple:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
with:
version: 10
cache: true
cache_dependency_path: |
one/pnpm-lock.yaml
two/pnpm-lock.yaml
run_install: |
- cwd: one
- cwd: two
```
## Notes ## Notes
This action does not setup Node.js for you, use [actions/setup-node](https://github.com/actions/setup-node) yourself. This action does not set up Node.js. Use [actions/setup-node](https://github.com/actions/setup-node) yourself. If you are on pnpm v11 or newer, [`pnpm/setup`](https://github.com/pnpm/setup) can install pnpm and Node.js in a single step.
## License ## License
+1 -1
View File
@@ -20,7 +20,7 @@ inputs:
required: false required: false
default: 'false' default: 'false'
cache_dependency_path: cache_dependency_path:
description: File path to the pnpm lockfile, which contents hash will be used as a cache key description: File path to the pnpm lockfile, whose contents hash will be used as a cache key. Accepts multiple paths delimited by newlines.
required: false required: false
default: 'pnpm-lock.yaml' default: 'pnpm-lock.yaml'
package_json_file: package_json_file:
+129 -129
View File
File diff suppressed because one or more lines are too long
+11 -2
View File
@@ -18,9 +18,18 @@ export async function runRestoreCache(inputs: Inputs) {
debug(`Primary key is ${primaryKey}`) debug(`Primary key is ${primaryKey}`)
saveState('cache_primary_key', primaryKey) saveState('cache_primary_key', primaryKey)
let cacheKey = await restoreCache([cachePath], primaryKey) // We don't need to download everything again if only one dependency changed
// We can still re-use previous store to cache the rest of the unchanged dependencies
const restoreKeys = [
`pnpm-cache-${process.env.RUNNER_OS}-${os.arch()}-`
];
setOutput('cache-hit', Boolean(cacheKey)) let cacheKey = await restoreCache([cachePath], primaryKey, restoreKeys)
// A restore-key (prefix) match still restores an older store, but "cache-hit"
// must only report an exact primary-key match, so dependency installation
// is not skipped when the lockfile has changed.
setOutput('cache-hit', cacheKey === primaryKey)
if (!cacheKey) { if (!cacheKey) {
info(`Cache is not found`) info(`Cache is not found`)
+33 -50
View File
@@ -5,18 +5,18 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@pnpm/exe": "11.0.4" "@pnpm/exe": "11.19.0"
} }
}, },
"node_modules/@pnpm/exe": { "node_modules/@pnpm/exe": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-11.19.0.tgz",
"integrity": "sha512-3OwYqbbj1KtuUqoMo5OEkY8nU/WutZ7L5ADFl0bbW9oyqU55U37aDqA3NJNSk28CyszNARfrjerAF2DW2TsV7w==", "integrity": "sha512-P1mw8BZaNkEpttlyzKsxTj7PVs94bMB4cQ8pJhgbrCTSBP7xCzKS3VlOwIInS7aS5by6KAbfO5Vs9yK/ekugrg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@reflink/reflink": "0.1.19", "@reflink/reflink": "0.1.19",
"detect-libc": "^2.0.3" "detect-libc": "^2.1.2"
}, },
"bin": { "bin": {
"pn": "pn", "pn": "pn",
@@ -28,20 +28,19 @@
"url": "https://opencollective.com/pnpm" "url": "https://opencollective.com/pnpm"
}, },
"optionalDependencies": { "optionalDependencies": {
"@pnpm/linux-arm64": "11.0.4", "@pnpm/linux-arm64": "11.19.0",
"@pnpm/linux-x64": "11.0.4", "@pnpm/linux-x64": "11.19.0",
"@pnpm/linuxstatic-arm64": "11.0.4", "@pnpm/linuxstatic-arm64": "11.19.0",
"@pnpm/linuxstatic-x64": "11.0.4", "@pnpm/linuxstatic-x64": "11.19.0",
"@pnpm/macos-arm64": "11.0.4", "@pnpm/macos-arm64": "11.19.0",
"@pnpm/macos-x64": "11.0.4", "@pnpm/win-arm64": "11.19.0",
"@pnpm/win-arm64": "11.0.4", "@pnpm/win-x64": "11.19.0"
"@pnpm/win-x64": "11.0.4"
} }
}, },
"node_modules/@pnpm/linux-arm64": { "node_modules/@pnpm/linux-arm64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-11.19.0.tgz",
"integrity": "sha512-Bz7V2sFypoGHX/t5w/w7jnCw5DCK3C8s5q8whHJJ3iS5kRznX3Q1F4LwSjjy+lsi777fHyNIvD7qtNmdt9IKoA==", "integrity": "sha512-c5AJqnsj0BMqMCOtctOzsCqyfY+afFe1kdM9F2FrNhYwtnQRMHoJy+51qfee4yuTPOVRyk3Yh1dwvyAadiznvA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -55,9 +54,9 @@
} }
}, },
"node_modules/@pnpm/linux-x64": { "node_modules/@pnpm/linux-x64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-11.19.0.tgz",
"integrity": "sha512-u0Yn1gytR1vKdPk6fYF500H8ZWQlj0cTuIQPp+5GYVPkMmA5bSw41RNIDPBfjDlE8ERmQWaQcrgmTcmTZ+n22A==", "integrity": "sha512-KOKpA9o75SvmRQgWO+EqEpQzJg1b9uHk5y61PAx90sSpdYtKDPua2eBXBglzv8YK1xM3DsYXFEf7Scs+3HeDsA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -71,9 +70,9 @@
} }
}, },
"node_modules/@pnpm/linuxstatic-arm64": { "node_modules/@pnpm/linuxstatic-arm64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-arm64/-/linuxstatic-arm64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-arm64/-/linuxstatic-arm64-11.19.0.tgz",
"integrity": "sha512-0aitEcfhWNXNZhfJGt/kJaRvfcdtJzXZpV+toJN94kfawSJnhuawfnUSXMi/3m0G97HkJc7BH8rOz3sojUKt0g==", "integrity": "sha512-Ci9WxgCInZc3F43R6BfaHevi+dYqaI5CLi6421egFsK649eHvYPytvu+zABhTNlYGNJbD8j9w+AUYInz/TPsyQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -90,9 +89,9 @@
} }
}, },
"node_modules/@pnpm/linuxstatic-x64": { "node_modules/@pnpm/linuxstatic-x64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-x64/-/linuxstatic-x64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-x64/-/linuxstatic-x64-11.19.0.tgz",
"integrity": "sha512-xDJdeJ7D2YvDBy2/IH9lEqMKiSuZiV8190XKWOgQgxUGGeuW4z3j6Ewpl0S5bXsWuNjAgC+uCKp7Qp3P7cXAvw==", "integrity": "sha512-GNMk96vNJ4uEWigekarjHofXNDFsTYRL0Mw2YlDkv/W+u0cn/UAVPHfd5aAfsf6Arel2ZQfxPUTXlpunFujWnA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -109,9 +108,9 @@
} }
}, },
"node_modules/@pnpm/macos-arm64": { "node_modules/@pnpm/macos-arm64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-11.19.0.tgz",
"integrity": "sha512-dNR69jUARtGFuyyLE9VuyxhRUKC8MO/7/xIyAdeIMZAD5ej0Y/Ct0DYCa/FLbgFL1nXaXmp4+gRMfJBkkrKfQQ==", "integrity": "sha512-I3Ee/GQlPxEOeBSzqxBNwcTHxVebjMiN2xLdTBS6OK2TAPzbWMIExjU5brxhr8rAk73p7mbv/a/2tFC/3gl6FA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -124,26 +123,10 @@
"url": "https://opencollective.com/pnpm" "url": "https://opencollective.com/pnpm"
} }
}, },
"node_modules/@pnpm/macos-x64": {
"version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/macos-x64/-/macos-x64-11.0.4.tgz",
"integrity": "sha512-RfyrxSBajeEU16dZsgFjbdagDV9F4HNCJfbBgm8IbGjL0+J95naM/VmCDLd6S3+1tISeI2MxtcyCxqjKJsD/BA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/pnpm"
}
},
"node_modules/@pnpm/win-arm64": { "node_modules/@pnpm/win-arm64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-11.19.0.tgz",
"integrity": "sha512-fOQEv8b9KxZlUAxPPXSQQUUIrt2nY24Qwd4RzCPpatacBnsE4JIadlr/B4V5z2zFxmV7FdHr7nYUhv2RqTlY/w==", "integrity": "sha512-rOiWuJ9wjaFY9ZDVPGVjpvjIQHZNB72rioxvuyzDKJ7dqKQ97VF27a4rmOtjhMB5u83cdDGEJ+OY+H1+TP+frw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -157,9 +140,9 @@
} }
}, },
"node_modules/@pnpm/win-x64": { "node_modules/@pnpm/win-x64": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-11.0.4.tgz", "resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-11.19.0.tgz",
"integrity": "sha512-pErHAV8m3NZuPSeCmH3senTSHX0nwkH5lLzQSpiFuyt08hq8sqL3jDymT4ri9N7ixPN9RFZghZIiT3h+Croaew==", "integrity": "sha512-l26XeTxoGxfU+mVcZ5jFdP9hNe5yrOlLPSkAlwwpXPI1iyyU4JUSxm6Jdyxu1UAU1FerD/BvLK2vrlR6d9ogag==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
+4 -4
View File
@@ -5,13 +5,13 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"pnpm": "11.0.4" "pnpm": "11.19.0"
} }
}, },
"node_modules/pnpm": { "node_modules/pnpm": {
"version": "11.0.4", "version": "11.19.0",
"resolved": "https://registry.npmjs.org/pnpm/-/pnpm-11.0.4.tgz", "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-11.19.0.tgz",
"integrity": "sha512-CjlxZQB6AU7VKRmmHl9GxIubyohATDA+yuzGP2Le9WOJjTxril1epYEes5jP4DqwXaGlzpY/Em1erUwC+TuDww==", "integrity": "sha512-eIHz7VkNRyxKlV4riLISF5ERYGbcyIy8o4SeybYPG7qm0syyIfqR2k4cZb7yvL43k2Wup6xTnHv4be3DobItzg==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"pn": "bin/pnpm.mjs", "pn": "bin/pnpm.mjs",
+31 -25
View File
@@ -84,10 +84,9 @@ export async function runSelfInstaller(inputs: Inputs): Promise<SelfInstallerRes
? path.join(dest, 'node_modules', '@pnpm', 'exe', process.platform === 'win32' ? 'pnpm.exe' : 'pnpm') ? path.join(dest, 'node_modules', '@pnpm', 'exe', process.platform === 'win32' ? 'pnpm.exe' : 'pnpm')
: path.join(dest, 'node_modules', 'pnpm', 'bin', 'pnpm.mjs') : path.join(dest, 'node_modules', 'pnpm', 'bin', 'pnpm.mjs')
// Determine the target version // Self-update the bootstrap to the requested pnpm version. readTargetVersion
// either returns a value or throws, so this always runs.
const targetVersion = readTargetVersion({ version, packageJsonFile }) const targetVersion = readTargetVersion({ version, packageJsonFile })
if (targetVersion) {
const cmd = standalone ? bootstrapPnpm : process.execPath const cmd = standalone ? bootstrapPnpm : process.execPath
const args = standalone ? ['self-update', targetVersion] : [bootstrapPnpm, 'self-update', targetVersion] const args = standalone ? ['self-update', targetVersion] : [bootstrapPnpm, 'self-update', targetVersion]
const exitCode = await runCommand(cmd, args, { cwd: dest }) const exitCode = await runCommand(cmd, args, { cwd: dest })
@@ -98,23 +97,18 @@ export async function runSelfInstaller(inputs: Inputs): Promise<SelfInstallerRes
// the bootstrap symlinks in pnpmHome pointing at the old version. Use // the bootstrap symlinks in pnpmHome pointing at the old version. Use
// PNPM_HOME/bin so consumers of the bin_dest output (e.g. // PNPM_HOME/bin so consumers of the bin_dest output (e.g.
// `${steps.pnpm.outputs.bin_dest}/pnpm`) invoke the requested version. // `${steps.pnpm.outputs.bin_dest}/pnpm`) invoke the requested version.
return { exitCode: 0, binDest: path.join(pnpmHome, 'bin') } //
} // When the requested version resolves to the bootstrap version, self-update
// is a no-op and PNPM_HOME/bin is not created — fall back to pnpmHome,
// No explicit target version: rely on the bootstrap pnpm to switch to // whose symlinks already point at the right version.
// the version declared in packageManager/devEngines at runtime. Force const updatedBinDir = path.join(pnpmHome, 'bin')
// `pmOnFail=download` so a project that pins return { exitCode: 0, binDest: existsSync(updatedBinDir) ? updatedBinDir : pnpmHome }
// `devEngines.packageManager.onFail = "error"` doesn't trip BAD_PM_VERSION
// before the switch can happen (issue #252). Scoped to this branch so users
// who pass an explicit `version:` input keep strict onFail behavior.
exportVariable('pnpm_config_pm_on_fail', 'download')
return { exitCode: 0, binDest: pnpmHome }
} }
function readTargetVersion(opts: { function readTargetVersion(opts: {
readonly version?: string | undefined readonly version?: string | undefined
readonly packageJsonFile: string readonly packageJsonFile: string
}): string | undefined { }): string {
const { version, packageJsonFile } = opts const { version, packageJsonFile } = opts
const { GITHUB_WORKSPACE } = process.env const { GITHUB_WORKSPACE } = process.env
@@ -135,12 +129,15 @@ function readTargetVersion(opts: {
} }
} }
// packageManager is always exact `pnpm@<version>[+<integrity>]` per spec.
// Strip the integrity hash for self-update.
const packageManagerVersion =
typeof packageManager === 'string' && packageManager.startsWith('pnpm@')
? packageManager.slice('pnpm@'.length).split('+')[0]
: undefined
if (version) { if (version) {
if ( if (packageManagerVersion && packageManagerVersion !== version) {
typeof packageManager === 'string' &&
packageManager.startsWith('pnpm@') &&
packageManager.replace('pnpm@', '') !== version
) {
throw new Error(`Multiple versions of pnpm specified: throw new Error(`Multiple versions of pnpm specified:
- version ${version} in the GitHub Action config with the key "version" - version ${version} in the GitHub Action config with the key "version"
- version ${packageManager} in the package.json with the key "packageManager" - version ${packageManager} in the package.json with the key "packageManager"
@@ -150,13 +147,22 @@ Remove one of these versions to avoid version mismatch errors like ERR_PNPM_BAD_
return version return version
} }
// pnpm will automatically download and switch to the right version // Self-update the bootstrap pnpm to the version pinned in package.json so
if (typeof packageManager === 'string' && packageManager.startsWith('pnpm@')) { // PATH-resolved `pnpm` (and the bin_dest output) reflect the target
return undefined // version. Without this, `pnpm store path` runs as the bootstrap and
// reports a different STORE_VERSION than the one the user's actual
// install writes to — breaking cache: true and actions/setup-node's
// `cache: pnpm` on cold caches (issue #233).
//
// devEngines.packageManager takes priority over packageManager, matching
// pnpm's getWantedPackageManager logic. `pnpm self-update` accepts both
// exact versions and semver ranges, so we pass either through directly.
if (devEngines?.packageManager?.name === 'pnpm' && devEngines.packageManager.version) {
return devEngines.packageManager.version
} }
if (devEngines?.packageManager?.name === 'pnpm' && devEngines.packageManager.version) { if (packageManagerVersion) {
return undefined return packageManagerVersion
} }
if (!GITHUB_WORKSPACE) { if (!GITHUB_WORKSPACE) {
+6 -4
View File
@@ -1,11 +1,8 @@
import { setFailed, startGroup, endGroup } from '@actions/core' import { setFailed, startGroup, endGroup } from '@actions/core'
import { spawnSync } from 'child_process' import { spawnSync } from 'child_process'
import { Inputs } from '../inputs' import { Inputs } from '../inputs'
import { patchPnpmEnv } from '../utils'
export function runPnpmInstall(inputs: Inputs) { export function runPnpmInstall(inputs: Inputs) {
const env = patchPnpmEnv(inputs)
for (const options of inputs.runInstall) { for (const options of inputs.runInstall) {
const args = ['install'] const args = ['install']
if (options.recursive) args.unshift('recursive') if (options.recursive) args.unshift('recursive')
@@ -14,11 +11,16 @@ export function runPnpmInstall(inputs: Inputs) {
const cmdStr = ['pnpm', ...args].join(' ') const cmdStr = ['pnpm', ...args].join(' ')
startGroup(`Running ${cmdStr}...`) startGroup(`Running ${cmdStr}...`)
// spawnSync inherits process.env, which already has $PNPM_HOME/bin and
// $PNPM_HOME prepended via addPath() in install-pnpm. Do NOT pass a
// hand-patched env that adds node_modules/.bin to the front — on
// Windows standalone, .bin/pnpm.cmd is an npm shim pointing at the
// BOOTSTRAP pnpm, which would shadow the self-updated one and break
// newer-pnpm-only behavior.
const { error, status } = spawnSync('pnpm', args, { const { error, status } = spawnSync('pnpm', args, {
stdio: 'inherit', stdio: 'inherit',
cwd: options.cwd, cwd: options.cwd,
shell: true, shell: true,
env,
}) })
endGroup() endGroup()
+2 -2
View File
@@ -1,7 +1,6 @@
import { warning, startGroup, endGroup } from '@actions/core' import { warning, startGroup, endGroup } from '@actions/core'
import { spawnSync } from 'child_process' import { spawnSync } from 'child_process'
import { Inputs } from '../inputs' import { Inputs } from '../inputs'
import { patchPnpmEnv } from '../utils'
export function pruneStore(inputs: Inputs) { export function pruneStore(inputs: Inputs) {
if (inputs.runInstall.length === 0) { if (inputs.runInstall.length === 0) {
@@ -10,10 +9,11 @@ export function pruneStore(inputs: Inputs) {
} }
startGroup('Running pnpm store prune...') startGroup('Running pnpm store prune...')
// spawnSync inherits process.env (which has the right PATH from addPath
// in install-pnpm). See pnpm-install/index.ts for the rationale.
const { error, status } = spawnSync('pnpm', ['store', 'prune'], { const { error, status } = spawnSync('pnpm', ['store', 'prune'], {
stdio: 'inherit', stdio: 'inherit',
shell: true, shell: true,
env: patchPnpmEnv(inputs),
}) })
endGroup() endGroup()
-10
View File
@@ -1,10 +0,0 @@
import path from 'path'
import process from 'process'
import { Inputs } from '../inputs'
export const getBinDest = (inputs: Inputs): string => path.join(inputs.dest, 'node_modules', '.bin')
export const patchPnpmEnv = (inputs: Inputs): NodeJS.ProcessEnv => ({
...process.env,
PATH: path.join(getBinDest(inputs), 'bin') + path.delimiter + getBinDest(inputs) + path.delimiter + process.env.PATH,
})