Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pnpm/setup
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.0.1
Choose a base ref
...
head repository: pnpm/setup
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.0.2
Choose a head ref
  • 2 commits
  • 10 files changed
  • 2 contributors

Commits on Aug 9, 2026

  1. feat: install pnpm from the npm registry, verified against npm's sign…

    …ature (#24)
    
    ## Summary
    
    The action verified the release archive against the `digest` GitHub publishes for it. GitHub serves both the asset and the digest, so whoever can replace one can replace the other — that catches a corrupted download, not a tampered one.
    
    The npm registry carries the same executable, **byte for byte**, and npm signs `<name>@<version>:<integrity>` with a key `get-pnpm` pins. That signature can't be produced without npm's private key, and behind it sits the maintainer's approval of the staged publish. **Every version this action installs now comes from the registry**, and is refused unless the signature *and* the checksum both check out.
    
    I verified the executables are identical across both sources before relying on it:
    
    | v12.0.0-rc.1 | GitHub release | npm platform package |
    |---|---|---|
    | linux-x64 | `f58dff16…` | `f58dff16…` |
    | darwin-arm64 | `180f2c62…` | `180f2c62…` |
    | win32-x64 (`pnpm.exe`) | `290aac61…` | `290aac61…` |
    
    v11 was going to stay on the release asset, because the registry copy declares `@reflink/reflink` as a dependency where the tarball bundles it, and this action has no step that would install it. That reasoning did not survive testing — an A/B with the dependency present and absent clones either way, and the tarball's bundled copy carries no native binding at all — so v11 comes from the registry too.
    
    That is the more important half: `latest` resolves to v11, so until now the *common* case was the one verified only by GitHub's digest.
    
    ## Two things fall out of not touching the GitHub API at all
    
    - **`token` is unused.** It existed to lift the anonymous 60 requests/hour limit on the release lookup, which no longer happens. Kept as a deprecated input so workflows passing it keep working.
    - **Versions published to npm without a GitHub release now install**, instead of hitting the "Some prerelease versions are published to npm but not released as downloadable binaries" error this action used to raise.
    
    With no source left that needs it, the release lookup, the asset naming, the sha256 check and the archive extraction are all gone — `download.ts` loses about 200 lines.
    
    ## Testing
    
    Every spec form, end to end against the real registry, each one placed and executed:
    
    | input | resolves to | placed |
    |---|---|---|
    | `next-12` | 12.0.0-rc.1 | `dist, pn, pnpm, pnpx, pnx` |
    | `latest` | 11.20.0 | `dist, pn, pnpm, pnpx, pnx` |
    | `^11.0.0` | 11.20.0 | `dist, pn, pnpm, pnpx, pnx` |
    | `11.20.0` | 11.20.0 | `dist, pn, pnpm, pnpx, pnx` |
    
    The existing smoke matrix covers Ubuntu (x64 + arm), macOS and Windows.
    
    ## It uses `get-pnpm` rather than its own copy
    
    The verification first landed here as its own implementation, because the package that does the same thing wasn't published. It is now (`get-pnpm@0.0.1`, from [pnpm/get.pnpm.io](https://github.com/pnpm/get.pnpm.io/tree/main/get-pnpm)), so the second commit deletes ~140 lines and depends on it.
    
    `downloadPnpm` verifies and places the executable and nothing else — it exists precisely so a caller that manages its own directory can use it without the global install and shell-rc editing that `pnpm setup` does. The action keeps what is genuinely its own: version resolution with semver ranges, the alias hardlinks, and PATH.
    
    That also puts the pinned keys in one place. `get.pnpm.io` refreshes them for all three of its installers on a schedule, so a rotation reaches this action as a dependency bump rather than by someone noticing.
    
    `get-pnpm` is excluded from `minimumReleaseAge` in `pnpm-workspace.yaml` — pnpm added that itself when I installed a package published minutes earlier. Worth a look if you'd rather wait for it to age instead.
    
    ---
    Written by an agent (Claude Code, claude-opus-5).
    zkochan authored Aug 9, 2026
    Configuration menu
    Copy the full SHA
    ed0c46d View commit details
    Browse the repository at this point in the history
  2. fix: keep the installed runtime authoritative against context-aware s…

    …hims (#25)
    
    * fix: keep the installed runtime authoritative against context-aware shims
    
    pnpm 12 links global runtime bins as context-aware shims: running `node`
    from `$PNPM_HOME/bin` inside a project switches to the version that project
    pins in `devEngines.runtime`, fetching it on demand. That defeats the
    runtime this action was asked to install — a matrix job asking for
    `node@22` runs the repository's pinned version instead — and even when the
    two versions agree pnpm materializes a second copy outside `$PNPM_HOME`.
    
    Whenever the action installs a runtime, export `PNPM_CONFIG_GLOBAL_SHIMS`
    with that runtime disabled (`{"node":false}`). The setting merges key-wise
    over pnpm's defaults, so the runtimes the action did not install keep
    theirs. A value the workflow set itself always wins, under either of the
    two names pnpm reads it from, so opting back into the switching behaviour
    stays possible.
    
    `pnpm install --no-runtime` already expresses this for the install step;
    the shims reintroduced the shadowing for every step after it.
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    
    * test: cover both spellings of the globalShims environment variable
    
    pnpm reads the setting from PNPM_CONFIG_GLOBAL_SHIMS or
    pnpm_config_global_shims, whichever comes first, so honouring only one of
    them would let the action override a workflow that used the other. Drive
    the opt-out job from a matrix over both names.
    
    Also record why an empty value counts as unset: pnpm ignores an empty
    value, so stepping aside for one would leave the shim enabled while
    looking like the workflow had opted in.
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
    zkochan and claude authored Aug 9, 2026
    Configuration menu
    Copy the full SHA
    84cb39b View commit details
    Browse the repository at this point in the history
Loading