ci: update dependencies with pnpm/update instead of Dependabot #92
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test Action | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| smoke: | |
| # Direct binary download + pnpm on PATH across OSes and architectures. | |
| name: 'Smoke (${{ matrix.os }} / pnpm ${{ matrix.version }})' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| version: '12.0.0' | |
| - os: ubuntu-24.04-arm | |
| version: '12.0.0' | |
| - os: macos-latest | |
| version: '12.0.0' | |
| - os: windows-latest | |
| version: '12.0.0' | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: ${{ matrix.version }} | |
| - name: 'Test: pnpm version on PATH matches request' | |
| env: | |
| BIN_DEST: ${{ steps.pnpm.outputs.bin-dest }} | |
| REQUIRED: ${{ matrix.version }} | |
| run: | | |
| set -e | |
| which pnpm | |
| actual="$(pnpm --version)" | |
| echo "pnpm --version: ${actual}" | |
| if [ "${actual}" != "${REQUIRED}" ]; then | |
| echo "Expected pnpm ${REQUIRED}, got ${actual}" | |
| exit 1 | |
| fi | |
| bin_dest_version="$("$BIN_DEST/pnpm" --version)" | |
| if [ "${bin_dest_version}" != "${REQUIRED}" ]; then | |
| echo "Expected ${REQUIRED} via bin_dest, got ${bin_dest_version}" | |
| exit 1 | |
| fi | |
| shell: bash | |
| smoke-v11: | |
| # v11's release archive bundles a Node-SEA launcher plus a sibling `dist/`, | |
| # unlike v12's single self-contained binary. Verify the action downloads and | |
| # lays it out correctly and puts pnpm on PATH across OSes/arches. (macos-latest | |
| # is arm64; pnpm v11 ships no Intel-macOS binary, so no macos-13 entry here.) | |
| name: 'Smoke pnpm 11 (${{ matrix.os }})' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: '11.17.0' | |
| install: false | |
| - name: 'Test: pnpm 11 on PATH matches request' | |
| env: | |
| BIN_DEST: ${{ steps.pnpm.outputs.bin-dest }} | |
| run: | | |
| set -e | |
| which pnpm | |
| actual="$(pnpm --version)" | |
| echo "pnpm --version: ${actual}" | |
| if [ "${actual}" != "11.17.0" ]; then | |
| echo "Expected pnpm 11.17.0, got ${actual}" | |
| exit 1 | |
| fi | |
| bin_dest_version="$("$BIN_DEST/pnpm" --version)" | |
| if [ "${bin_dest_version}" != "11.17.0" ]; then | |
| echo "Expected 11.17.0 via bin_dest, got ${bin_dest_version}" | |
| exit 1 | |
| fi | |
| shell: bash | |
| cache-windows: | |
| # pnpm can report a Windows store path with the extended-length `\\?\` | |
| # prefix. Cache paths must use the regular drive-path form because the | |
| # cache toolkit otherwise interprets `?` as a glob in the root segment. | |
| # | |
| # The pnpm version below is load-bearing, not stale: 12.0.0 reports a | |
| # plain `D:\.pnpm-store\v11` for this store-dir, so only an older | |
| # release still reproduces the prefix this job exists to cover. Do not | |
| # bump it without checking that `pnpm store path` still starts `\\?\`. | |
| name: 'Cache Windows extended-length store path' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - name: Configure store at the drive root | |
| run: Set-Content -Path .npmrc -Value 'store-dir=/.pnpm-store' | |
| - uses: ./ | |
| with: | |
| version: '12.0.0-rc.0' | |
| runtime: node@22 | |
| cache: true | |
| - name: 'Test: pnpm reports an extended-length store path' | |
| run: | | |
| $storePath = pnpm store path | |
| Write-Host "pnpm store path: $storePath" | |
| if (-not $storePath.StartsWith('\\?\')) { | |
| throw "Expected an extended-length store path, got: $storePath" | |
| } | |
| runtime-cache-prime: | |
| name: 'Prime runtime cache' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: ./ | |
| with: | |
| version: '11.22.0' | |
| runtime: node@24.19.0 | |
| cache: true | |
| - name: 'Test: moving selector cache keys' | |
| run: pnpm test | |
| runtime-cache-hit: | |
| name: 'Restore runtime cache' | |
| needs: runtime-cache-prime | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # `pnpm runtime set` always fetches SHASUMS256.txt(.sig) and index.json | |
| # from nodejs.org to verify the runtime, warm store or not, so the job | |
| # cannot prove the restore happened by cutting the network. What it can | |
| # prove is the key scheme: a second run of the same selector must land | |
| # an exact hit on the key the first run saved under the resolved | |
| # version, which only holds if the restore precedes the install. | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: '11.22.0' | |
| runtime: node@24.19.0 | |
| cache: true | |
| install: false | |
| - name: 'Test: runtime cache was restored' | |
| env: | |
| CACHE_HIT: ${{ steps.pnpm.outputs.cache-hit }} | |
| run: | | |
| set -e | |
| if [ "${CACHE_HIT}" != "true" ]; then | |
| echo "Expected a runtime cache hit, got: ${CACHE_HIT}" | |
| exit 1 | |
| fi | |
| actual="$(node --version)" | |
| if [ "${actual}" != "v24.19.0" ]; then | |
| echo "Expected node v24.19.0, got: ${actual}" | |
| exit 1 | |
| fi | |
| shell: bash | |
| runtime-node-pnpm11: | |
| # The whole premise of supporting v11 is that `pnpm runtime` works there. | |
| # Install pnpm v11, install a runtime through it, and run `pnpm install`. | |
| name: 'Runtime node + install on pnpm 11' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up a synthetic package.json | |
| # Use a fresh manifest (and drop the repo lockfile) so `pnpm install` | |
| # under v11 resolves cleanly rather than against a v12-authored lockfile. | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "dependencies": { | |
| "is-odd": "3.0.1" | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: '11.17.0' | |
| runtime: node@22 | |
| - name: 'Test: pnpm 11, node 22, and install all worked' | |
| env: | |
| OUT_NAME: ${{ steps.pnpm.outputs.runtime-name }} | |
| OUT_VERSION: ${{ steps.pnpm.outputs.runtime-version }} | |
| run: | | |
| set -e | |
| pnpm_version="$(pnpm --version)" | |
| echo "pnpm --version: ${pnpm_version}" | |
| case "${pnpm_version}" in | |
| 11.*) ;; | |
| *) echo "Expected pnpm 11.x, got ${pnpm_version}"; exit 1 ;; | |
| esac | |
| which node | |
| node_version="$(node --version)" | |
| echo "node --version: ${node_version}" | |
| case "${node_version}" in | |
| v22.*) ;; | |
| *) echo "Expected node v22.x, got ${node_version}"; exit 1 ;; | |
| esac | |
| if [ "${OUT_NAME}" != "node" ]; then | |
| echo "Expected outputs.runtime-name=node, got ${OUT_NAME}"; exit 1 | |
| fi | |
| case "${OUT_VERSION}" in | |
| 22.*) ;; | |
| *) echo "Expected outputs.runtime-version=22.x, got ${OUT_VERSION}"; exit 1 ;; | |
| esac | |
| if [ ! -d node_modules/is-odd ]; then | |
| echo "Expected pnpm install to populate node_modules/is-odd"; exit 1 | |
| fi | |
| shell: bash | |
| version-from-dist-tag: | |
| # `version` may be an npm dist-tag; it is resolved against the main | |
| # `pnpm` package's dist-tags. | |
| name: 'Version from dist-tag (next-12)' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: ./ | |
| with: | |
| version: next-12 | |
| install: false | |
| - name: 'Test: dist-tag resolves to a 12.x version' | |
| run: | | |
| set -e | |
| which pnpm | |
| actual="$(pnpm --version)" | |
| echo "pnpm --version: ${actual}" | |
| case "${actual}" in | |
| 12.*) echo "ok" ;; | |
| *) echo "Expected pnpm 12.x, got ${actual}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| version-range: | |
| # `version` may be a semver range. While v12 only has prereleases the | |
| # range resolution falls back to prerelease versions. | |
| name: 'Version from semver range (^12.0.0-0)' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: ./ | |
| with: | |
| version: '^12.0.0-0' | |
| install: false | |
| - name: 'Test: range resolves to a 12.x version' | |
| run: | | |
| set -e | |
| actual="$(pnpm --version)" | |
| echo "pnpm --version: ${actual}" | |
| case "${actual}" in | |
| 12.*) echo "ok" ;; | |
| *) echo "Expected pnpm 12.x, got ${actual}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| runtime-node: | |
| # Explicit runtime input across OSes. Asserts node binary is on PATH and | |
| # the action's outputs reflect what was installed. | |
| name: 'Runtime node@${{ matrix.major }} (${{ matrix.os }})' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| major: '22' | |
| - os: macos-latest | |
| major: '22' | |
| - os: windows-latest | |
| major: '22' | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: '12.0.0' | |
| runtime: node@${{ matrix.major }} | |
| - name: 'Test: node binary on PATH' | |
| env: | |
| MAJOR: ${{ matrix.major }} | |
| run: | | |
| set -e | |
| which node | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v${MAJOR}.*) echo "ok" ;; | |
| *) echo "Expected node v${MAJOR}.x, got ${actual}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| - name: 'Test: outputs.runtime-name and outputs.runtime-version' | |
| env: | |
| OUT_NAME: ${{ steps.pnpm.outputs.runtime-name }} | |
| OUT_VERSION: ${{ steps.pnpm.outputs.runtime-version }} | |
| EXPECTED_VERSION: ${{ matrix.major }} | |
| run: | | |
| set -e | |
| if [ "${OUT_NAME}" != "node" ]; then | |
| echo "Expected outputs.runtime-name=node, got ${OUT_NAME}" | |
| exit 1 | |
| fi | |
| case "${OUT_VERSION}" in | |
| "${EXPECTED_VERSION}".*) ;; | |
| *) echo "Expected outputs.runtime-version=${EXPECTED_VERSION}.x, got ${OUT_VERSION}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| runtime-bun: | |
| name: Runtime bun | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| runtime: bun@latest | |
| - name: 'Test: bun on PATH' | |
| run: | | |
| set -e | |
| which bun | |
| bun --version | |
| shell: bash | |
| runtime-deno: | |
| name: Runtime deno | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| runtime: deno@2 | |
| - name: 'Test: deno on PATH' | |
| run: | | |
| set -e | |
| which deno | |
| deno --version | |
| shell: bash | |
| runtime-from-devengines: | |
| # No `runtime` input — the action should pick up devEngines.runtime from | |
| # package.json and install every entry. Also asserts that `pnpm install` runs by | |
| # default when a manifest is present. | |
| name: 'Runtime from devEngines.runtime' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with devEngines.runtime | |
| # Remove the action's own pnpm-lock.yaml so `pnpm install` doesn't | |
| # hit a frozen-lockfile mismatch against the synthetic manifest. | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "devEngines": { | |
| "runtime": [ | |
| { "name": "bun", "version": "1.3.12", "onFail": "warn" }, | |
| { "name": "node", "version": "24", "onFail": "warn" }, | |
| { "name": "bun", "version": "1.3.13", "onFail": "warn" } | |
| ] | |
| }, | |
| "dependencies": { | |
| "is-odd": "3.0.1" | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - id: pnpm | |
| uses: ./ | |
| - name: 'Test: Bun and Node are installed and dependencies are resolved' | |
| env: | |
| OUT_NAME: ${{ steps.pnpm.outputs.runtime-name }} | |
| OUT_VERSION: ${{ steps.pnpm.outputs.runtime-version }} | |
| OUT_RUNTIMES: ${{ steps.pnpm.outputs.runtimes }} | |
| run: | | |
| set -e | |
| which bun | |
| bun_actual="$(bun --version)" | |
| echo "bun --version: ${bun_actual}" | |
| if [ "${bun_actual}" != "1.3.13" ]; then | |
| echo "Expected bun 1.3.13, got ${bun_actual}" | |
| exit 1 | |
| fi | |
| which node | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v24.*) ;; | |
| *) echo "Expected node v24.x, got ${actual}"; exit 1 ;; | |
| esac | |
| if [ "${OUT_NAME}" != "bun" ]; then | |
| echo "Expected outputs.runtime-name=bun, got ${OUT_NAME}" | |
| exit 1 | |
| fi | |
| if [ "${OUT_VERSION}" != "1.3.13" ]; then | |
| echo "Expected outputs.runtime-version=1.3.13, got ${OUT_VERSION}" | |
| exit 1 | |
| fi | |
| # Declaration order is part of the contract, and each version must be | |
| # the one that landed rather than the selector that was asked for. | |
| first="$(printf '%s' "${OUT_RUNTIMES}" | jq -r '.[0].name + "@" + .[0].version')" | |
| second="$(printf '%s' "${OUT_RUNTIMES}" | jq -r '.[1].name + "@" + .[1].version')" | |
| count="$(printf '%s' "${OUT_RUNTIMES}" | jq -r 'length')" | |
| if [ "${count}" != "2" ]; then | |
| echo "Expected 2 entries in outputs.runtimes, got ${count}: ${OUT_RUNTIMES}" | |
| exit 1 | |
| fi | |
| if [ "${first}" != "bun@1.3.13" ]; then | |
| echo "Expected outputs.runtimes[0]=bun@1.3.13, got ${first}" | |
| exit 1 | |
| fi | |
| case "${second}" in | |
| node@24.*) ;; | |
| *) echo "Expected outputs.runtimes[1]=node@24.x, got ${second}"; exit 1 ;; | |
| esac | |
| # `pnpm install` should have run automatically — node_modules must exist. | |
| if [ ! -d node_modules/is-odd ]; then | |
| echo "Expected pnpm install to have populated node_modules/is-odd" | |
| exit 1 | |
| fi | |
| shell: bash | |
| runtime-overrides-devengines: | |
| # Explicit `runtime` input conflicts with `devEngines.runtime` in | |
| # package.json. The action should install the explicit version AND pass | |
| # `--no-runtime` to pnpm install so the devEngines runtime doesn't shadow | |
| # the matrix one. | |
| name: 'Explicit runtime overrides devEngines.runtime' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with conflicting devEngines.runtime | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "devEngines": { | |
| "runtime": { "name": "node", "version": "^20.0.0", "onFail": "download" } | |
| }, | |
| "dependencies": { | |
| "is-odd": "3.0.1" | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| runtime: node@22 | |
| - name: 'Test: node 22 stays active after pnpm install' | |
| run: | | |
| set -e | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v22.*) ;; | |
| *) echo "Expected node v22.x after install (--no-runtime should have suppressed the devEngines runtime fetch), got ${actual}"; exit 1 ;; | |
| esac | |
| # pnpm install should still have run. | |
| if [ ! -d node_modules/is-odd ]; then | |
| echo "Expected pnpm install to have populated node_modules/is-odd" | |
| exit 1 | |
| fi | |
| shell: bash | |
| runtime-survives-context-shims: | |
| # pnpm 12 links the global `node` bin as a context-aware shim that would | |
| # switch to the version the project pins in devEngines.runtime. The | |
| # exported PNPM_CONFIG_GLOBAL_SHIMS must keep the installed runtime | |
| # authoritative for every later step. | |
| name: 'Installed runtime beats context-aware shims' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with conflicting devEngines.runtime | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "devEngines": { | |
| "runtime": { "name": "node", "version": "20.19.0", "onFail": "download" } | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| runtime: node@22 | |
| - name: 'Test: node 22 runs inside the project' | |
| run: | | |
| set -e | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v22.*) ;; | |
| *) echo "Expected node v22.x, got ${actual} — the context-aware shim switched to the project's pin"; exit 1 ;; | |
| esac | |
| echo "PNPM_CONFIG_GLOBAL_SHIMS: ${PNPM_CONFIG_GLOBAL_SHIMS}" | |
| if [ "${PNPM_CONFIG_GLOBAL_SHIMS}" != '{"node":false}' ]; then | |
| echo 'Expected PNPM_CONFIG_GLOBAL_SHIMS={"node":false}' | |
| exit 1 | |
| fi | |
| shell: bash | |
| context-shims-opt-out: | |
| # The counterpart to the job above, and its canary: a workflow that sets | |
| # the setting itself keeps pnpm's switching behaviour, so the project's | |
| # pin wins. If this job stops switching, the job above is no longer | |
| # proving anything. Both spellings pnpm accepts are covered, since | |
| # honouring only one of them would silently override the other. | |
| name: 'Workflow-set ${{ matrix.env_name }} is not overwritten' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| env_name: [PNPM_CONFIG_GLOBAL_SHIMS, pnpm_config_global_shims] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with conflicting devEngines.runtime | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "devEngines": { | |
| "runtime": { "name": "node", "version": "20.19.0", "onFail": "download" } | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - name: Opt back into context-aware shims | |
| env: | |
| ENV_NAME: ${{ matrix.env_name }} | |
| run: printf '%s={"node":"auto"}\n' "$ENV_NAME" >> "$GITHUB_ENV" | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| runtime: node@22 | |
| - name: "Test: the project's pin still wins" | |
| run: | | |
| set -e | |
| echo "PNPM_CONFIG_GLOBAL_SHIMS: ${PNPM_CONFIG_GLOBAL_SHIMS}" | |
| if [ "${PNPM_CONFIG_GLOBAL_SHIMS}" = '{"node":false}' ]; then | |
| echo "The action overwrote the setting the workflow provided" | |
| exit 1 | |
| fi | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v20.19.0) ;; | |
| *) echo "Expected node v20.19.0 from the context-aware shim, got ${actual}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| runtime-version-fallback: | |
| # `runtime: node` without an `@<version>` suffix should fall back to the | |
| # version declared in devEngines.runtime. | |
| name: 'runtime version falls back to devEngines' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with devEngines.runtime version | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "devEngines": { | |
| "runtime": { "name": "node", "version": "^20.0.0", "onFail": "download" } | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| runtime: node | |
| - name: 'Test: node 20 via fallback' | |
| run: | | |
| set -e | |
| actual="$(node --version)" | |
| echo "node --version: ${actual}" | |
| case "${actual}" in | |
| v20.*) ;; | |
| *) echo "Expected node v20.x, got ${actual}"; exit 1 ;; | |
| esac | |
| shell: bash | |
| install-false: | |
| # `install: false` skips the auto-install step even though a manifest is | |
| # present. | |
| name: 'install: false skips pnpm install' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up package.json with a dependency | |
| run: | | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "dependencies": { | |
| "is-odd": "3.0.1" | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| install: false | |
| - name: 'Test: node_modules was not populated' | |
| run: | | |
| set -e | |
| if [ -d node_modules/is-odd ]; then | |
| echo "Expected install: false to skip pnpm install, but node_modules/is-odd exists" | |
| exit 1 | |
| fi | |
| # pnpm itself should still be on PATH. | |
| which pnpm | |
| pnpm --version | |
| shell: bash | |
| no-runtime: | |
| # No runtime input, no devEngines.runtime. Action installs pnpm only and | |
| # leaves the runtime outputs empty. With no package.json, `pnpm install` | |
| # is also skipped. | |
| name: 'No runtime, no manifest' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| version: '12.0.0' | |
| - name: 'Test: pnpm works, runtime outputs are empty' | |
| env: | |
| OUT_NAME: ${{ steps.pnpm.outputs.runtime-name }} | |
| OUT_VERSION: ${{ steps.pnpm.outputs.runtime-version }} | |
| OUT_RUNTIMES: ${{ steps.pnpm.outputs.runtimes }} | |
| run: | | |
| set -e | |
| which pnpm | |
| pnpm --version | |
| if [ -n "${OUT_NAME}" ]; then | |
| echo "Expected outputs.runtime-name to be empty, got '${OUT_NAME}'" | |
| exit 1 | |
| fi | |
| if [ -n "${OUT_VERSION}" ]; then | |
| echo "Expected outputs.runtime-version to be empty, got '${OUT_VERSION}'" | |
| exit 1 | |
| fi | |
| if [ "${OUT_RUNTIMES}" != "[]" ]; then | |
| echo "Expected outputs.runtimes=[], got '${OUT_RUNTIMES}'" | |
| exit 1 | |
| fi | |
| shell: bash | |
| cache-lockfile-verification: | |
| # The action caches pnpm's lockfile verification log, which lives in | |
| # `cacheDir`. Newer pnpm reports that via `pnpm cache path`; older | |
| # releases have no such subcommand and the action derives pnpm's | |
| # per-platform default instead. Guard that derivation against pnpm's own. | |
| name: 'Lockfile verification cache (${{ matrix.os }}, cache=${{ matrix.cache }})' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # The log is cached independently of the store, so the store-less | |
| # configuration has to reach it too. | |
| - os: ubuntu-latest | |
| cache: false | |
| - os: ubuntu-latest | |
| cache: true | |
| - os: macos-latest | |
| cache: true | |
| - os: windows-latest | |
| cache: true | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Configure a supply-chain policy so the verification runs | |
| # A one-minute floor activates the check without holding back any | |
| # version this repo already locks. | |
| run: | | |
| printf '\nminimumReleaseAge: 1\n' >> pnpm-workspace.yaml | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| runtime: node@22 | |
| cache: ${{ matrix.cache }} | |
| - 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 | |
| working-directory: | |
| # A project whose root is not the repository root. Running the install at | |
| # the repository root would exit 0 having installed nothing, so assert the | |
| # dependency actually landed. `cache: true` is part of the test: without | |
| # `cache-dependency-path` rebasing onto the working directory it matches no | |
| # lockfile and the restore throws outright. | |
| name: 'Project in a subdirectory' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Move this project into docs/ | |
| run: | | |
| set -e | |
| mkdir -p docs | |
| mv package.json pnpm-lock.yaml pnpm-workspace.yaml docs/ | |
| node -e " | |
| const fs = require('fs') | |
| const manifest = JSON.parse(fs.readFileSync('docs/package.json', 'utf8')) | |
| manifest.packageManager = 'pnpm@12.0.0' | |
| fs.writeFileSync('docs/package.json', JSON.stringify(manifest, null, 2)) | |
| " | |
| shell: bash | |
| - id: pnpm | |
| uses: ./ | |
| with: | |
| working-directory: docs | |
| runtime: node@22 | |
| cache: true | |
| - name: 'Test: the subdirectory project was installed' | |
| run: | | |
| set -e | |
| if [ ! -d docs/node_modules/@actions/cache ]; then | |
| echo "Expected pnpm install to populate docs/node_modules" | |
| ls -la docs || true | |
| exit 1 | |
| fi | |
| if [ -d node_modules ]; then | |
| echo "Did not expect node_modules at the repository root" | |
| exit 1 | |
| fi | |
| # `packageManager` was read from docs/package.json, not the root. | |
| pnpm_version="$(pnpm --version)" | |
| if [ "${pnpm_version}" != "12.0.0" ]; then | |
| echo "Expected pnpm 12.0.0 from docs/package.json, got ${pnpm_version}" | |
| exit 1 | |
| fi | |
| shell: bash | |
| working-directory-deprecated-input: | |
| # `package-json-file` is deprecated but must keep working exactly as it | |
| # did: the directory holding the file becomes the working directory, and | |
| # an explicit `cache-dependency-path` stays relative to the repository | |
| # root. Rebasing it onto the working directory would look for | |
| # `web/web/pnpm-lock.yaml` and fail the restore. | |
| name: 'Deprecated package-json-file still works' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Move this project into web/ | |
| run: | | |
| set -e | |
| mkdir -p web | |
| mv package.json pnpm-lock.yaml pnpm-workspace.yaml web/ | |
| node -e " | |
| const fs = require('fs') | |
| const manifest = JSON.parse(fs.readFileSync('web/package.json', 'utf8')) | |
| manifest.packageManager = 'pnpm@12.0.0' | |
| fs.writeFileSync('web/package.json', JSON.stringify(manifest, null, 2)) | |
| " | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| package-json-file: web/package.json | |
| cache: true | |
| cache-dependency-path: web/pnpm-lock.yaml | |
| - name: 'Test: the install ran in web/' | |
| run: | | |
| set -e | |
| if [ ! -d web/node_modules/@actions/cache ]; then | |
| echo "Expected pnpm install to populate web/node_modules" | |
| ls -la web || true | |
| exit 1 | |
| fi | |
| pnpm_version="$(pnpm --version)" | |
| if [ "${pnpm_version}" != "12.0.0" ]; then | |
| echo "Expected pnpm 12.0.0 from web/package.json, got ${pnpm_version}" | |
| exit 1 | |
| fi | |
| shell: bash | |
| require-lockfile: | |
| # `require-lockfile` installs against an existing lockfile and fails when | |
| # there is none — the case pnpm's CI default leaves open, since a plain | |
| # install resolves from the registry and exits 0 instead. | |
| name: 'require-lockfile' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up a manifest with a matching lockfile | |
| run: | | |
| set -e | |
| rm -f pnpm-lock.yaml | |
| cat > package.json <<'EOF' | |
| { | |
| "packageManager": "pnpm@12.0.0", | |
| "dependencies": { | |
| "is-odd": "3.0.1" | |
| } | |
| } | |
| EOF | |
| shell: bash | |
| - name: Put pnpm on PATH without installing | |
| uses: ./ | |
| with: | |
| version: '12.0.0' | |
| install: false | |
| - name: Write the lockfile and record its checksum | |
| run: | | |
| set -e | |
| pnpm install --lockfile-only | |
| sha256sum pnpm-lock.yaml > lockfile.sha256 | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| require-lockfile: true | |
| - name: 'Test: installed from the lockfile, which is untouched' | |
| run: | | |
| set -e | |
| if [ ! -d node_modules/is-odd ]; then | |
| echo "Expected require-lockfile to populate node_modules/is-odd"; exit 1 | |
| fi | |
| if ! sha256sum --check --status lockfile.sha256; then | |
| echo "Expected a successful require-lockfile install to leave the lockfile alone"; exit 1 | |
| fi | |
| shell: bash | |
| - name: Remove the lockfile entirely | |
| run: rm -f pnpm-lock.yaml | |
| shell: bash | |
| - id: missing | |
| continue-on-error: true | |
| uses: ./ | |
| with: | |
| version: '12.0.0' | |
| require-lockfile: true | |
| - name: 'Test: a missing lockfile failed the step before pnpm ran' | |
| env: | |
| OUTCOME: ${{ steps.missing.outcome }} | |
| run: | | |
| set -e | |
| echo "outcome: ${OUTCOME}" | |
| if [ "${OUTCOME}" != "failure" ]; then | |
| echo "Expected require-lockfile to fail when no lockfile exists"; exit 1 | |
| fi | |
| shell: bash | |
| - name: 'Test: without require-lockfile the same state installs and writes one' | |
| # The contrast the input exists for: pnpm's CI default does not require | |
| # a lockfile, so this succeeds where the step above failed. | |
| run: | | |
| set -e | |
| rm -f pnpm-lock.yaml | |
| shell: bash | |
| - uses: ./ | |
| with: | |
| version: '12.0.0' | |
| - name: 'Test: a plain install wrote the lockfile and succeeded' | |
| run: | | |
| set -e | |
| if [ ! -f pnpm-lock.yaml ]; then | |
| echo "Expected a plain install to write a lockfile"; exit 1 | |
| fi | |
| shell: bash | |