Compare commits

...

9 Commits

Author SHA1 Message Date
Zoltan Kochan
b635341b3e chore: rebuild bundle after reverting bootstrap pnpm to 11.0.4 2026-05-11 01:49:47 +02:00
Zoltan Kochan
025de3876f test: assert env var directly instead of relying on pnpm runtime behavior
The previous version of this test self-updated to pnpm 10.33.0 and expected
pnpm --version to fail under devEngines.onFail=error. But pnpm v10 only reads
the packageManager field, not devEngines, so the check never fired and the
test failed.

Switch the assertion to the contract the af8e203 scope fix actually enforces:
when an explicit version: is supplied, pnpm_config_pm_on_fail must not be
exported by the action. That's deterministic and pnpm-version-agnostic.
2026-05-11 01:49:23 +02:00
Zoltan Kochan
5c388c2b3b test: assert onFail=error stays strict when explicit version is supplied
Adds a manifest_pin matrix entry combining `version: 10.33.0` with
`devEngines.packageManager` pinned to 9.15.5/onFail=error. The action
self-updates to 10.33.0, then `pnpm --version` must fail with
BAD_PM_VERSION — confirming pnpm_config_pm_on_fail=download is scoped to
the no-target-version branch and does not silently override the user's
strict policy.
2026-05-11 01:49:23 +02:00
Zoltan Kochan
84fa52c3d7 ci: consolidate test matrix and stop double-firing on PRs
- Limit push trigger to master so PRs run jobs once instead of twice.
- Fold test_default_inputs / test_dest / test_version_respects_request /
  test_bin_dest_output into one cross-OS `smoke` job (5 matrix entries:
  ubuntu × 2 versions + ubuntu custom-dest + macos + windows).
- Fold test_package_manager_field / test_dev_engines /
  test_dev_engines_on_fail_error into one ubuntu-only `manifest_pin` job
  (6 matrix entries) — manifest handling is OS-independent.
- Keep standalone and run_install but ubuntu-only.

Total runs per PR push: ~88 → 14.
2026-05-11 01:49:22 +02:00
Zoltan Kochan
07ad39cba4 refactor: scope pm_on_fail override to the no-target-version branch
Only export `pnpm_config_pm_on_fail=download` when the action is relying on
bootstrap pnpm's runtime switch. When the user passes an explicit `version:`
input, self-update aligns the binary and we should not silently override their
`devEngines.packageManager.onFail = "error"` policy.
2026-05-11 01:49:22 +02:00
Zoltan Kochan
052c4ffb7d fix: honor devEngines.packageManager.onFail=error (#252)
Export pnpm_config_pm_on_fail=download so the bootstrap pnpm switches to
the version pinned in devEngines.packageManager instead of throwing
BAD_PM_VERSION on the user's first invocation.
2026-05-11 01:49:22 +02:00
Zoltan Kochan
91ab88e261 fix: bin_dest output points to self-updated pnpm, not bootstrap (#249)
* fix: bin_dest output points to self-updated pnpm, not bootstrap (#247)

`pnpm self-update <version>` writes the target binary to
`${PNPM_HOME}/bin/`, leaving the bootstrap symlink at `${PNPM_HOME}/pnpm`
untouched. The `bin_dest` output was set to `${PNPM_HOME}`, so consumers
invoking `${{ steps.pnpm.outputs.bin_dest }}/pnpm` got the bootstrap
version (currently 11.0.4) instead of the version they requested.

PATH lookup hid the bug: `${PNPM_HOME}/bin` was prepended ahead of
`${PNPM_HOME}`, so `pnpm` resolved from PATH was the right one. Existing
version-respect tests only checked `pnpm --version`, not `bin_dest`.

Resolve `binDest` inside `runSelfInstaller` (target lives in
`${PNPM_HOME}/bin` after self-update, otherwise stays at `${PNPM_HOME}`)
and plumb it through to `setOutputs`. Add a regression test that invokes
`${bin_dest}/pnpm --version` directly across Linux/macOS/Windows.

* test(ci): pass bin_dest via env to survive Windows backslashes

Direct GitHub-expression interpolation of `${{ steps.pnpm.outputs.bin_dest }}`
into the bash script let bash eat the backslashes in the Windows path
(`C:Usersrunneradminsetup-pnpmnode_modules.binbin/pnpm`), failing with
"No such file or directory". Forward the value via env so the path
reaches bash unmangled.

* build: rebuild dist with clean lockfile-matched deps
2026-05-07 12:58:58 +02:00
Zoltan Kochan
e578e19d19 fix: update pnpm to 11.0.4 2026-05-04 12:08:02 +02:00
Zoltan Kochan
8912a9102a fix: append (not prepend) action node dir to PATH for npm bootstrap (#241) 2026-05-02 16:10:46 +02:00
8 changed files with 379 additions and 408 deletions

View File

@@ -1,43 +1,73 @@
name: Test Action name: Test Action
on: on:
- push pull_request:
- pull_request push:
- workflow_dispatch branches:
- master
workflow_dispatch:
jobs: jobs:
test_default_inputs: smoke:
name: Test with default inputs # Cross-OS coverage. Exercises the bootstrap install + PATH on each platform,
# the version-respects-request regression (#225 / #230 — Windows PATH shadow),
# and the bin_dest output regression (#247). Multi-version coverage on Linux
# so we don't pay 3x for major-version differences.
name: 'Smoke (${{ matrix.name }})'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
pnpm: include:
- 9.15.5 - name: 'ubuntu / v9.15.5'
os: os: ubuntu-latest
- ubuntu-latest version: '9.15.5'
- macos-latest - name: 'ubuntu / v10.33.0'
- windows-latest os: ubuntu-latest
version: '10.33.0'
- name: 'ubuntu / v9.15.5 / custom-dest'
os: ubuntu-latest
version: '9.15.5'
dest: '~/test/pnpm'
- name: 'macos / v9.15.5'
os: macos-latest
version: '9.15.5'
- name: 'windows / v9.15.5'
os: windows-latest
version: '9.15.5'
steps: steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Run the action - id: pnpm
name: Run the action
uses: ./ uses: ./
with: with:
version: 9.15.5 version: ${{ matrix.version }}
dest: ${{ matrix.dest || '~/setup-pnpm' }}
- name: 'Test: which' - name: 'Test: pnpm/pnpx on PATH report the requested version (incl. via bin_dest)'
run: which pnpm; which pnpx # Pass paths via env, not template interpolation, so Windows
# backslashes in `bin_dest` aren't eaten by bash's escape handling.
- name: 'Test: version' env:
BIN_DEST: ${{ steps.pnpm.outputs.bin_dest }}
REQUIRED: ${{ matrix.version }}
run: | run: |
set -e
which pnpm
which pnpx
actual="$(pnpm --version)" actual="$(pnpm --version)"
echo "pnpm version: ${actual}" echo "pnpm --version: ${actual}"
if [[ ! "${actual}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then if [ "${actual}" != "${REQUIRED}" ]; then
echo "ERROR: pnpm --version did not produce valid output" echo "Expected pnpm version ${REQUIRED}, but got ${actual}"
exit 1
fi
bin_dest_version="$("$BIN_DEST/pnpm" --version)"
echo "bin_dest pnpm --version: ${bin_dest_version}"
if [ "${bin_dest_version}" != "${REQUIRED}" ]; then
echo "Expected ${REQUIRED} via bin_dest, but got ${bin_dest_version}"
exit 1 exit 1
fi fi
shell: bash shell: bash
@@ -50,54 +80,95 @@ jobs:
pnpm add is-odd pnpm add is-odd
shell: bash shell: bash
test_dest: manifest_pin:
name: Test with dest # Folds the old test_package_manager_field, test_dev_engines, and
# test_dev_engines_on_fail_error jobs. The action's manifest handling is
# OS-independent, so ubuntu-only is sufficient.
name: 'Manifest pin: ${{ matrix.label }}'
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
pnpm: include:
- 9.15.5 - label: 'packageManager pnpm@9.15.5 (#227)'
os: manifest: '{"packageManager":"pnpm@9.15.5"}'
- ubuntu-latest version: '9.15.5'
- macos-latest - label: 'packageManager pnpm@10.33.0'
- windows-latest manifest: '{"packageManager":"pnpm@10.33.0"}'
version: '10.33.0'
- label: 'devEngines onFail=download, exact'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":"9.15.5","onFail":"download"}}}'
version: '9.15.5'
- label: 'devEngines onFail=download, range'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":">=9.15.0","onFail":"download"}}}'
version: '>=9.15.0'
- label: 'devEngines onFail=error, exact (#252)'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":"9.15.5","onFail":"error"}}}'
version: '9.15.5'
- label: 'devEngines onFail=error, range (#252)'
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":">=9.15.0","onFail":"error"}}}'
version: '>=9.15.0'
- label: 'explicit version: pnpm_config_pm_on_fail not exported'
# Regression guard for the af8e203 scope fix: when the user passes an
# explicit `version:` input, the action must NOT export
# pnpm_config_pm_on_fail=download, so the user's strict onFail policy
# is preserved. Asserted directly on the env var rather than pnpm
# runtime behavior — different pnpm majors read devEngines
# differently (v10 ignores it, v11+ honors it).
manifest: '{"devEngines":{"packageManager":{"name":"pnpm","version":"9.15.5","onFail":"error"}}}'
explicit_version: '10.33.0'
expect_pm_on_fail_unset: true
steps: steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up package.json
run: echo '${{ matrix.manifest }}' > package.json
shell: bash
- name: Run the action - name: Run the action
uses: ./ uses: ./
with: with:
version: 9.15.5 version: ${{ matrix.explicit_version }}
dest: ~/test/pnpm
- name: 'Test: which' - name: 'Test: pnpm reports the pinned version'
run: which pnpm && which pnpx if: ${{ !matrix.expect_pm_on_fail_unset }}
env:
- name: 'Test: version' REQUIRED: ${{ matrix.version }}
run: | run: |
set -e
actual="$(pnpm --version)" actual="$(pnpm --version)"
echo "pnpm version: ${actual}" echo "pnpm version: ${actual}"
if [[ ! "${actual}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then if [ "${REQUIRED}" = ">=9.15.0" ]; then
echo "ERROR: pnpm --version did not produce valid output" min="9.15.0"
exit 1 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 fi
shell: bash shell: bash
test_standalone: - name: 'Test: pnpm_config_pm_on_fail not exported (explicit version preserves strict policy)'
name: Test with standalone if: ${{ matrix.expect_pm_on_fail_unset }}
run: |
if [ -n "${pnpm_config_pm_on_fail:-}" ]; then
echo "Expected pnpm_config_pm_on_fail to be unset, but got: '${pnpm_config_pm_on_fail}'"
exit 1
fi
echo "pnpm_config_pm_on_fail is unset, as expected"
shell: bash
runs-on: ${{ matrix.os }} standalone:
name: Standalone mode
strategy: runs-on: ubuntu-latest
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
steps: steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
@@ -108,167 +179,29 @@ jobs:
version: 9.15.0 version: 9.15.0
standalone: true standalone: true
- name: 'Test: which' - name: 'Test: pnpm works'
run: which pnpm
- name: 'Test: version'
run: | run: |
set -e
which pnpm
actual="$(pnpm --version)" actual="$(pnpm --version)"
echo "pnpm version: ${actual}" if [ "${actual}" != "9.15.0" ]; then
if [[ ! "${actual}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then echo "Expected 9.15.0, got ${actual}"
echo "ERROR: pnpm --version did not produce valid output"
exit 1 exit 1
fi fi
shell: bash
- name: 'Test: install in a fresh project'
run: |
mkdir /tmp/test-standalone mkdir /tmp/test-standalone
cd /tmp/test-standalone cd /tmp/test-standalone
pnpm init pnpm init
pnpm add is-odd pnpm add is-odd
shell: bash shell: bash
test_version_respects_request: run_install:
name: 'Test version input is actually installed (${{ matrix.version }}, ${{ matrix.os }})' name: 'run_install (${{ matrix.run_install.name }})'
# Regression test for #225 / #230: the bootstrap pnpm on PATH was shadowing the self-updated binary,
# so a user requesting e.g. `version: 9.15.5` would silently get the bootstrap version.
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
version:
- '9.15.5'
- '10.33.0'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Run the action
uses: ./
with:
version: ${{ matrix.version }}
- name: 'Test: exact version installed'
run: |
required='${{ matrix.version }}'
actual="$(pnpm --version)"
echo "pnpm version: ${actual}"
if [ "${actual}" != "${required}" ]; then
echo "Expected pnpm version ${required}, but got ${actual}"
exit 1
fi
shell: bash
test_package_manager_field:
name: 'Test packageManager field is respected (${{ matrix.version }}, ${{ matrix.os }})'
# Reproduces #227: when `packageManager` is set in package.json and no `version:` input is given,
# the action should install the version specified there.
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
version:
- '9.15.5'
- '10.33.0'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up package.json with packageManager field
run: echo '{"packageManager":"pnpm@${{ matrix.version }}"}' > package.json
shell: bash
- name: Run the action
uses: ./
- name: 'Test: exact version installed'
run: |
required='${{ matrix.version }}'
actual="$(pnpm --version)"
echo "pnpm version: ${actual}"
if [ "${actual}" != "${required}" ]; then
echo "Expected pnpm version ${required}, but got ${actual}"
exit 1
fi
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 }})'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
pnpm:
- 9.15.5
os:
- ubuntu-latest
- macos-latest
- windows-latest
run_install: run_install:
- name: 'null' - name: 'null'
value: 'null' value: 'null'
@@ -289,15 +222,14 @@ jobs:
version: 9.15.5 version: 9.15.5
run_install: ${{ matrix.run_install.value }} run_install: ${{ matrix.run_install.value }}
- name: 'Test: which' - name: 'Test: pnpm works'
run: which pnpm; which pnpx
- name: 'Test: version'
run: | run: |
set -e
which pnpm
which pnpx
actual="$(pnpm --version)" actual="$(pnpm --version)"
echo "pnpm version: ${actual}" if [ "${actual}" != "9.15.5" ]; then
if [[ ! "${actual}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then echo "Expected 9.15.5, got ${actual}"
echo "ERROR: pnpm --version did not produce valid output"
exit 1 exit 1
fi fi
shell: bash shell: bash

299
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -20,9 +20,10 @@ async function main() {
async function runMain(inputs: Inputs) { async function runMain(inputs: Inputs) {
saveState('is_post', 'true') saveState('is_post', 'true')
await installPnpm(inputs) const binDest = await installPnpm(inputs)
if (binDest === undefined) return
console.log('Installation Completed!') console.log('Installation Completed!')
setOutputs(inputs) setOutputs(inputs, binDest)
await restoreCache(inputs) await restoreCache(inputs)

View File

@@ -5,13 +5,13 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@pnpm/exe": "11.0.0-rc.5" "@pnpm/exe": "11.0.4"
} }
}, },
"node_modules/@pnpm/exe": { "node_modules/@pnpm/exe": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/exe/-/exe-11.0.4.tgz",
"integrity": "sha512-HT1HxzeFc6RVIMhngQZ7bQgTNzF0IckeFpOvnwCJKfsjfsD/po3LvUVsidCvpALxCWOft1TuBZUkdHq03pEolA==", "integrity": "sha512-3OwYqbbj1KtuUqoMo5OEkY8nU/WutZ7L5ADFl0bbW9oyqU55U37aDqA3NJNSk28CyszNARfrjerAF2DW2TsV7w==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -28,20 +28,20 @@
"url": "https://opencollective.com/pnpm" "url": "https://opencollective.com/pnpm"
}, },
"optionalDependencies": { "optionalDependencies": {
"@pnpm/linux-arm64": "11.0.0-rc.5", "@pnpm/linux-arm64": "11.0.4",
"@pnpm/linux-x64": "11.0.0-rc.5", "@pnpm/linux-x64": "11.0.4",
"@pnpm/linuxstatic-arm64": "11.0.0-rc.5", "@pnpm/linuxstatic-arm64": "11.0.4",
"@pnpm/linuxstatic-x64": "11.0.0-rc.5", "@pnpm/linuxstatic-x64": "11.0.4",
"@pnpm/macos-arm64": "11.0.0-rc.5", "@pnpm/macos-arm64": "11.0.4",
"@pnpm/macos-x64": "11.0.0-rc.5", "@pnpm/macos-x64": "11.0.4",
"@pnpm/win-arm64": "11.0.0-rc.5", "@pnpm/win-arm64": "11.0.4",
"@pnpm/win-x64": "11.0.0-rc.5" "@pnpm/win-x64": "11.0.4"
} }
}, },
"node_modules/@pnpm/linux-arm64": { "node_modules/@pnpm/linux-arm64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linux-arm64/-/linux-arm64-11.0.4.tgz",
"integrity": "sha512-AreNJJI0r5oEsv5+i+FMVK8AeYs0MpWTGWc2GQwf7qi/w8uA8UxVlIDwhgwY+R6YgdrYVrEjgbU4WcqIqYfgog==", "integrity": "sha512-Bz7V2sFypoGHX/t5w/w7jnCw5DCK3C8s5q8whHJJ3iS5kRznX3Q1F4LwSjjy+lsi777fHyNIvD7qtNmdt9IKoA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -55,9 +55,9 @@
} }
}, },
"node_modules/@pnpm/linux-x64": { "node_modules/@pnpm/linux-x64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linux-x64/-/linux-x64-11.0.4.tgz",
"integrity": "sha512-NzZPWeIVxCEfQs84wR/O3IND2HSDOClPB2L8vvkWb8KQ4pczOG2x3aNkltXDwYVKxvw4URmwct5u57JGTEvtfg==", "integrity": "sha512-u0Yn1gytR1vKdPk6fYF500H8ZWQlj0cTuIQPp+5GYVPkMmA5bSw41RNIDPBfjDlE8ERmQWaQcrgmTcmTZ+n22A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -71,12 +71,15 @@
} }
}, },
"node_modules/@pnpm/linuxstatic-arm64": { "node_modules/@pnpm/linuxstatic-arm64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-arm64/-/linuxstatic-arm64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-arm64/-/linuxstatic-arm64-11.0.4.tgz",
"integrity": "sha512-xK+U/fJDkvzs4ktswrCZ03cTSEAeFTfgUG88r2J+6JEDGuY/foNOMnnSNOiSplpaufY+Ie+uL+PEDlTyIy46Xg==", "integrity": "sha512-0aitEcfhWNXNZhfJGt/kJaRvfcdtJzXZpV+toJN94kfawSJnhuawfnUSXMi/3m0G97HkJc7BH8rOz3sojUKt0g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -87,12 +90,15 @@
} }
}, },
"node_modules/@pnpm/linuxstatic-x64": { "node_modules/@pnpm/linuxstatic-x64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-x64/-/linuxstatic-x64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/linuxstatic-x64/-/linuxstatic-x64-11.0.4.tgz",
"integrity": "sha512-Z1kSilngaM2URfPhBjam/xhMDAn5jl8V0L5CjG/Gg5unmKkipyF93OYMpfnny7A9p1KWi6sNql/KufzUmRP4Eg==", "integrity": "sha512-xDJdeJ7D2YvDBy2/IH9lEqMKiSuZiV8190XKWOgQgxUGGeuW4z3j6Ewpl0S5bXsWuNjAgC+uCKp7Qp3P7cXAvw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -103,9 +109,9 @@
} }
}, },
"node_modules/@pnpm/macos-arm64": { "node_modules/@pnpm/macos-arm64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/macos-arm64/-/macos-arm64-11.0.4.tgz",
"integrity": "sha512-98p3ilSzkyusC2bxk7Ya34CWt9MeJy/+kpXfwn9YgnOD7GDqCjYY9dlPB9yrkdtKUUMeOIvOuacAQTWnCg2GOQ==", "integrity": "sha512-dNR69jUARtGFuyyLE9VuyxhRUKC8MO/7/xIyAdeIMZAD5ej0Y/Ct0DYCa/FLbgFL1nXaXmp4+gRMfJBkkrKfQQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -119,9 +125,9 @@
} }
}, },
"node_modules/@pnpm/macos-x64": { "node_modules/@pnpm/macos-x64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/macos-x64/-/macos-x64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/macos-x64/-/macos-x64-11.0.4.tgz",
"integrity": "sha512-WZ9UqjTbZN+dMZcy4qaPDsEo4sxTIrw5H+fDvdxT1GUavsf8SBDpvzZMHrGDQ/k22H8oKvPtJ+RGd/Ie5dvbuA==", "integrity": "sha512-RfyrxSBajeEU16dZsgFjbdagDV9F4HNCJfbBgm8IbGjL0+J95naM/VmCDLd6S3+1tISeI2MxtcyCxqjKJsD/BA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -135,9 +141,9 @@
} }
}, },
"node_modules/@pnpm/win-arm64": { "node_modules/@pnpm/win-arm64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/win-arm64/-/win-arm64-11.0.4.tgz",
"integrity": "sha512-hxgDmF4xpSVvUPvH+HdMllvHcV2zuYUn/uK182gzFvZ9DE0xEGVj09XaSn5VMbpa32i25oIqaT89QfMcOw/TJg==", "integrity": "sha512-fOQEv8b9KxZlUAxPPXSQQUUIrt2nY24Qwd4RzCPpatacBnsE4JIadlr/B4V5z2zFxmV7FdHr7nYUhv2RqTlY/w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -151,9 +157,9 @@
} }
}, },
"node_modules/@pnpm/win-x64": { "node_modules/@pnpm/win-x64": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/@pnpm/win-x64/-/win-x64-11.0.4.tgz",
"integrity": "sha512-B1H/6jhzW/6VLr8Bc3zOhMd2P3srANWytuBq0uppR5c7OJwUlqXqJtB6Q4nOEjWYZ2sA5m2xuFiaMeioZiiqgA==", "integrity": "sha512-pErHAV8m3NZuPSeCmH3senTSHX0nwkH5lLzQSpiFuyt08hq8sqL3jDymT4ri9N7ixPN9RFZghZIiT3h+Croaew==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -224,6 +230,9 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -240,6 +249,9 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -256,6 +268,9 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -272,6 +287,9 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [

View File

@@ -5,13 +5,13 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"pnpm": "11.0.0-rc.5" "pnpm": "11.0.4"
} }
}, },
"node_modules/pnpm": { "node_modules/pnpm": {
"version": "11.0.0-rc.5", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/pnpm/-/pnpm-11.0.0-rc.5.tgz", "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-11.0.4.tgz",
"integrity": "sha512-xGn7aqE6meV67JNc17hv9CJwH0YC7KwiMdPcIJEFhuv7a1CntFXQd47CKuVpEtjY6I6fngoDwIdaakF4OpShvQ==", "integrity": "sha512-CjlxZQB6AU7VKRmmHl9GxIubyohATDA+yuzGP2Le9WOJjTxril1epYEes5jP4DqwXaGlzpY/Em1erUwC+TuDww==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"pn": "bin/pnpm.mjs", "pn": "bin/pnpm.mjs",

View File

@@ -4,13 +4,15 @@ import runSelfInstaller from './run'
export { runSelfInstaller } export { runSelfInstaller }
export async function install(inputs: Inputs) { export async function install(inputs: Inputs): Promise<string | undefined> {
startGroup('Running self-installer...') startGroup('Running self-installer...')
const status = await runSelfInstaller(inputs) const { exitCode, binDest } = await runSelfInstaller(inputs)
endGroup() endGroup()
if (status) { if (exitCode) {
return setFailed(`Something went wrong, self-installer exits with code ${status}`) setFailed(`Something went wrong, self-installer exits with code ${exitCode}`)
return undefined
} }
return binDest
} }
export default install export default install

View File

@@ -12,7 +12,12 @@ import exeLock from './bootstrap/exe-lock.json'
const BOOTSTRAP_PNPM_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { pnpm: pnpmLock.packages['node_modules/pnpm'].version } }) const BOOTSTRAP_PNPM_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { pnpm: pnpmLock.packages['node_modules/pnpm'].version } })
const BOOTSTRAP_EXE_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { '@pnpm/exe': exeLock.packages['node_modules/@pnpm/exe'].version } }) 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<number> { export interface SelfInstallerResult {
exitCode: number
binDest: string
}
export async function runSelfInstaller(inputs: Inputs): Promise<SelfInstallerResult> {
const { version, dest, packageJsonFile } = inputs const { version, dest, packageJsonFile } = inputs
// pnpm v11 requires Node >= 22.13; use standalone (exe) bootstrap which // pnpm v11 requires Node >= 22.13; use standalone (exe) bootstrap which
@@ -29,19 +34,23 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
await writeFile(path.join(dest, 'package.json'), packageJson) await writeFile(path.join(dest, 'package.json'), packageJson)
await writeFile(path.join(dest, 'package-lock.json'), JSON.stringify(lockfile)) await writeFile(path.join(dest, 'package-lock.json'), JSON.stringify(lockfile))
// Prepend the action's node directory to PATH so npm's // Append the action's node directory to PATH so npm's
// `#!/usr/bin/env node` shebang resolves on runners (e.g. GHE // `#!/usr/bin/env node` shebang resolves on runners (e.g. GHE
// self-hosted) where node isn't already on PATH. npm itself is // self-hosted) where node isn't already on PATH. Append (not
// resolved via PATH — on the GitHub Actions runner it is not // prepend) so a user-installed toolchain on PATH — e.g. from a
// co-located with `process.execPath`. // prior `setup-node` step — keeps precedence; otherwise the
// runner-bundled node would shadow it and pair the user's npm
// with a mismatched node version. npm itself is resolved via
// PATH — on the GitHub Actions runner it is not co-located with
// `process.execPath`.
const nodeDir = path.dirname(process.execPath) const nodeDir = path.dirname(process.execPath)
// On Windows, the PATH key casing varies; search case-insensitively. // On Windows, the PATH key casing varies; search case-insensitively.
const pathKey = Object.keys(process.env).find(k => k.toUpperCase() === 'PATH') ?? 'PATH' const pathKey = Object.keys(process.env).find(k => k.toUpperCase() === 'PATH') ?? 'PATH'
const currentPath = process.env[pathKey] const currentPath = process.env[pathKey]
const npmEnv = { ...process.env, [pathKey]: currentPath ? nodeDir + path.delimiter + currentPath : nodeDir } const npmEnv = { ...process.env, [pathKey]: currentPath ? currentPath + path.delimiter + nodeDir : nodeDir }
const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest, env: npmEnv }) const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest, env: npmEnv })
if (npmExitCode !== 0) { if (npmExitCode !== 0) {
return npmExitCode return { exitCode: npmExitCode, binDest: path.join(dest, 'node_modules', '.bin') }
} }
// On Windows with standalone mode, npm's .bin shims can't properly // On Windows with standalone mode, npm's .bin shims can't properly
@@ -83,11 +92,23 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
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 })
if (exitCode !== 0) { if (exitCode !== 0) {
return exitCode return { exitCode, binDest: pnpmHome }
} }
// self-update writes the target pnpm/pnpx into PNPM_HOME/bin, leaving
// the bootstrap symlinks in pnpmHome pointing at the old version. Use
// PNPM_HOME/bin so consumers of the bin_dest output (e.g.
// `${steps.pnpm.outputs.bin_dest}/pnpm`) invoke the requested version.
return { exitCode: 0, binDest: path.join(pnpmHome, 'bin') }
} }
return 0 // No explicit target version: rely on the bootstrap pnpm to switch to
// the version declared in packageManager/devEngines at runtime. Force
// `pmOnFail=download` so a project that pins
// `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: {

View File

@@ -1,9 +1,7 @@
import { setOutput } from '@actions/core' import { setOutput } from '@actions/core'
import { Inputs } from '../inputs' import { Inputs } from '../inputs'
import { getBinDest } from '../utils'
export function setOutputs(inputs: Inputs) { export function setOutputs(inputs: Inputs, binDest: string) {
const binDest = getBinDest(inputs)
// NOTE: addPath is already called in installPnpm — do not call it again // NOTE: addPath is already called in installPnpm — do not call it again
// here, as a second addPath would shadow the correct entry on Windows. // here, as a second addPath would shadow the correct entry on Windows.
setOutput('dest', inputs.dest) setOutput('dest', inputs.dest)