mirror of
https://github.com/pnpm/action-setup.git
synced 2026-08-13 16:01:31 +00:00
c0a6b0ff36
pnpm v11 and newer verify every lockfile entry against the configured supply-chain policies (`minimumReleaseAge`, `trustPolicy`, ...) and memoize the verdict in `<cacheDir>/lockfile-verified.jsonl`. The action cached only the store, so every job started with that verdict missing and re-checked the whole lockfile against the registry — on typescript-eslint's repository, 16.6s of a 17.6s install on Linux and 40.1s of 42.4s on Windows. The verdict depends on the lockfile content and the policies, never on the runner, so it is cached under its own key alongside the store cache and restored without prefix fallback: an entry recorded for a different lockfile could never be reused. Saving happens before `pnpm store prune`, which drops the log along with the store's other derived state. Anything that goes wrong here only costs the next job the re-verification, so failures are reported as warnings instead of failing the build. Older pnpm versions never write the log, and the post step then finds nothing to save.
380 lines
13 KiB
YAML
380 lines
13 KiB
YAML
name: Test Action
|
|
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches:
|
|
- master
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
smoke:
|
|
# 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 }}
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: 'ubuntu / v9.15.5'
|
|
os: ubuntu-latest
|
|
version: '9.15.5'
|
|
- name: 'ubuntu / v10.33.0'
|
|
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:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- id: pnpm
|
|
name: Run the action
|
|
uses: ./
|
|
with:
|
|
version: ${{ matrix.version }}
|
|
dest: ${{ matrix.dest || '~/setup-pnpm' }}
|
|
|
|
- name: 'Test: pnpm/pnpx on PATH report the requested version (incl. via bin_dest)'
|
|
# Pass paths via env, not template interpolation, so Windows
|
|
# backslashes in `bin_dest` aren't eaten by bash's escape handling.
|
|
env:
|
|
BIN_DEST: ${{ steps.pnpm.outputs.bin_dest }}
|
|
REQUIRED: ${{ matrix.version }}
|
|
run: |
|
|
set -e
|
|
which pnpm
|
|
which pnpx
|
|
actual="$(pnpm --version)"
|
|
echo "pnpm --version: ${actual}"
|
|
if [ "${actual}" != "${REQUIRED}" ]; then
|
|
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
|
|
fi
|
|
shell: bash
|
|
|
|
- name: 'Test: install in a fresh project'
|
|
run: |
|
|
mkdir /tmp/test-project
|
|
cd /tmp/test-project
|
|
pnpm init
|
|
pnpm add is-odd
|
|
shell: bash
|
|
|
|
manifest_pin:
|
|
# 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: ubuntu-latest
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- label: 'packageManager pnpm@9.15.5 (#227)'
|
|
manifest: '{"packageManager":"pnpm@9.15.5"}'
|
|
version: '9.15.5'
|
|
- label: 'packageManager pnpm@10.33.0'
|
|
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:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Set up package.json
|
|
run: echo '${{ matrix.manifest }}' > package.json
|
|
shell: bash
|
|
|
|
- name: Run the action
|
|
uses: ./
|
|
with:
|
|
version: ${{ matrix.explicit_version }}
|
|
|
|
- name: 'Test: pnpm reports the pinned version'
|
|
if: ${{ !matrix.expect_pm_on_fail_unset }}
|
|
env:
|
|
REQUIRED: ${{ matrix.version }}
|
|
run: |
|
|
set -e
|
|
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
|
|
|
|
- name: 'Test: pnpm_config_pm_on_fail not exported (explicit version preserves strict policy)'
|
|
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
|
|
|
|
standalone:
|
|
name: Standalone mode
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Run the action
|
|
uses: ./
|
|
with:
|
|
version: 9.15.0
|
|
standalone: true
|
|
|
|
- name: 'Test: pnpm works'
|
|
run: |
|
|
set -e
|
|
which pnpm
|
|
actual="$(pnpm --version)"
|
|
if [ "${actual}" != "9.15.0" ]; then
|
|
echo "Expected 9.15.0, got ${actual}"
|
|
exit 1
|
|
fi
|
|
mkdir /tmp/test-standalone
|
|
cd /tmp/test-standalone
|
|
pnpm init
|
|
pnpm add is-odd
|
|
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:
|
|
name: 'run_install (${{ matrix.run_install.name }})'
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
run_install:
|
|
- name: 'null'
|
|
value: 'null'
|
|
- name: 'global'
|
|
value: |
|
|
args:
|
|
- --global
|
|
- --global-dir=./pnpm-global
|
|
- npm
|
|
- yarn
|
|
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Run the action
|
|
uses: ./
|
|
with:
|
|
version: 9.15.5
|
|
run_install: ${{ matrix.run_install.value }}
|
|
|
|
- name: 'Test: pnpm works'
|
|
run: |
|
|
set -e
|
|
which pnpm
|
|
which pnpx
|
|
actual="$(pnpm --version)"
|
|
if [ "${actual}" != "9.15.5" ]; then
|
|
echo "Expected 9.15.5, got ${actual}"
|
|
exit 1
|
|
fi
|
|
shell: bash
|
|
|
|
cache_lockfile_verification:
|
|
# The action caches pnpm's lockfile verification log, which lives in
|
|
# `cacheDir` — a directory pnpm resolves per platform and does not print.
|
|
# Guard the action's copy of that default against pnpm's own.
|
|
name: 'Lockfile verification cache (${{ matrix.os }})'
|
|
|
|
runs-on: ${{ matrix.os }}
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
|
|
- name: Set up a project with a supply-chain policy
|
|
# A one-minute floor activates the verification without holding back
|
|
# any version the install resolves.
|
|
run: |
|
|
echo '{"dependencies":{"is-odd":"3.0.1"}}' > package.json
|
|
printf 'packages:\n - .\nminimumReleaseAge: 1\n' > pnpm-workspace.yaml
|
|
shell: bash
|
|
|
|
- uses: ./
|
|
with:
|
|
version: '12.0.0-rc.4'
|
|
cache: true
|
|
run_install: |
|
|
- args: [--no-frozen-lockfile]
|
|
|
|
- name: 'Test: pnpm wrote the verification log where the action looks for it'
|
|
run: |
|
|
set -e
|
|
case "$RUNNER_OS" in
|
|
Linux) cacheDir="${XDG_CACHE_HOME:-$HOME/.cache}/pnpm" ;;
|
|
macOS) cacheDir="$HOME/Library/Caches/pnpm" ;;
|
|
Windows) cacheDir="$(cygpath -u "$LOCALAPPDATA")/pnpm-cache" ;;
|
|
*) echo "Unexpected RUNNER_OS: $RUNNER_OS"; exit 1 ;;
|
|
esac
|
|
echo "Expecting the verification log in ${cacheDir}"
|
|
if [ ! -f "${cacheDir}/lockfile-verified.jsonl" ]; then
|
|
echo "No lockfile-verified.jsonl there; the action would cache nothing"
|
|
ls -la "${cacheDir}" || true
|
|
exit 1
|
|
fi
|
|
shell: bash
|