2
mirror of https://github.com/pnpm/action-setup.git synced 2026-08-13 16:01:31 +00:00

feat: cache the lockfile verification log regardless of cache

The log is under a kilobyte and pnpm writes it on every install, not only
where supply-chain policies are configured: the integrity and tarball-URL
checks are unconditional. A job that starts without it re-checks every
lockfile entry against the registry — on a ~2000-entry lockfile with a warm
store, 13.5s vs 1.5s with `minimumReleaseAge` and `trustPolicy` configured,
and still 6.7s vs 1.6s with no policies at all.

Tying that to the `cache` input made the common case slow for no saving worth
counting, so the log is now restored and saved on its own key whether or not
the store is cached. `cache` goes back to meaning what its name says.
This commit is contained in:
Zoltan Kochan
2026-08-13 16:56:49 +02:00
parent 544072d0b9
commit b543421fa5
6 changed files with 47 additions and 14 deletions
+13 -3
View File
@@ -334,14 +334,24 @@ jobs:
# 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 }})'
name: 'Lockfile verification cache (${{ matrix.os }}, cache=${{ matrix.cache }})'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
@@ -357,7 +367,7 @@ jobs:
- uses: ./
with:
version: '12.0.0-rc.4'
cache: true
cache: ${{ matrix.cache }}
run_install: |
- args: [--no-frozen-lockfile]
+13 -2
View File
@@ -94,7 +94,7 @@ If `run_install` is a YAML string representation of either an object or an array
### `cache`
**Optional** (_type:_ `boolean`, _default:_ `false`) Whether to cache the pnpm store directory and, on pnpm v11 and newer, the results of pnpm's lockfile verification against the configured supply-chain policies. Both are keyed on the lockfile's content hash.
**Optional** (_type:_ `boolean`, _default:_ `false`) Whether to cache the pnpm store directory, keyed on the lockfile's content hash. On pnpm v11 and newer, the results of pnpm's lockfile verification are cached regardless of this input — see [Lockfile verification cache](#lockfile-verification-cache).
### `cache_dependency_path`
@@ -208,7 +208,18 @@ jobs:
**Note:** You don't need to run `pnpm store prune` at the end; post-action has already taken care of that.
Besides the store, this also caches pnpm's lockfile verification results (pnpm v11 and newer). Repositories that configure supply-chain policies such as `minimumReleaseAge` or `trustPolicy` make pnpm check every lockfile entry against the registry on each install; that check depends only on the lockfile and the policies, so its result is cached and reused until the lockfile changes.
### Lockfile verification cache
pnpm v11 and newer check every lockfile entry before installing it — that each entry pins an integrity hash, that a pinned tarball URL matches the registry's own metadata, and, where configured, your `minimumReleaseAge` and `trustPolicy` policies. The verdict is memoized in a sub-kilobyte file, so an unchanged lockfile is not re-checked against the registry.
The action restores and saves that file on every run, independently of the `cache` input, because a job that starts without it pays for the check every time. On a repository with ~2000 lockfile entries and a warm store:
| | without the log | with it |
| --- | --- | --- |
| `minimumReleaseAge` + `trustPolicy` | 13.5s | 1.5s |
| no policies configured | 6.7s | 1.6s |
Reusing a verdict is not a weaker check: pnpm re-verifies whenever the lockfile content changes, and whenever the recorded policy is looser than the one now configured.
### Cache dependencies from multiple lockfiles
+3 -3
View File
@@ -17,9 +17,9 @@ inputs:
default: 'null'
cache:
description: |
Whether to cache the pnpm store directory and, on pnpm v11 and newer,
the results of pnpm's lockfile verification against the configured
supply-chain policies. Both are keyed on the lockfile's content hash.
Whether to cache the pnpm store directory, keyed on the lockfile's
content hash. On pnpm v11 and newer, the results of pnpm's lockfile
verification are cached either way — see the README.
required: false
default: 'false'
cache_dependency_path:
+1 -1
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -4,10 +4,10 @@ import { Inputs } from '../inputs'
import { runRestoreCache } from './run'
export async function restoreCache(inputs: Inputs) {
if (!inputs.cache) return
if (!isFeatureAvailable()) {
warning('Cache is not available, skipping cache restoration')
if (inputs.cache) {
warning('Cache is not available, skipping cache restoration')
}
return
}
+14 -2
View File
@@ -10,11 +10,23 @@ import { removeWindowsExtendedPathPrefix } from '../windows-path'
export async function runRestoreCache(inputs: Inputs) {
const fileHash = await hashFiles(inputs.cacheDependencyPath)
if (!fileHash) {
throw new Error('Some specified paths were not resolved, unable to cache dependencies.')
// Both caches are keyed on the lockfile, so neither can be restored
// without one. Only the store cache was asked for by name.
if (inputs.cache) {
throw new Error('Some specified paths were not resolved, unable to cache dependencies.')
}
return
}
await runRestoreStoreCache(fileHash)
// Restored whether or not the store is cached: the log is a fraction of a
// kilobyte, and without it pnpm re-checks every lockfile entry against the
// registry on each run — seconds even on a repository that configures no
// supply-chain policies.
await restoreVerificationCache(fileHash)
if (inputs.cache) {
await runRestoreStoreCache(fileHash)
}
}
async function runRestoreStoreCache(fileHash: string) {