diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index bd024e7..6299b63 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -194,6 +194,52 @@ jobs: pnpm add is-odd 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"}}' + + 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 }})' diff --git a/dist/index.js b/dist/index.js index 4c2ed42..ddb8afe 100644 Binary files a/dist/index.js and b/dist/index.js differ diff --git a/src/install-pnpm/run.ts b/src/install-pnpm/run.ts index ffcf3b8..0afe211 100644 --- a/src/install-pnpm/run.ts +++ b/src/install-pnpm/run.ts @@ -98,15 +98,21 @@ export async function runSelfInstaller(inputs: Inputs): Promise[+]` per spec. + // Strip the integrity hash for self-update. + const packageManagerVersion = + typeof packageManager === 'string' && packageManager.startsWith('pnpm@') + ? packageManager.slice('pnpm@'.length).split('+')[0] + : undefined + if (version) { - if ( - typeof packageManager === 'string' && - packageManager.startsWith('pnpm@') && - packageManager.replace('pnpm@', '') !== version - ) { + if (packageManagerVersion && packageManagerVersion !== version) { throw new Error(`Multiple versions of pnpm specified: - version ${version} in the GitHub Action config with the key "version" - version ${packageManager} in the package.json with the key "packageManager" @@ -150,13 +159,27 @@ Remove one of these versions to avoid version mismatch errors like ERR_PNPM_BAD_ return version } - // pnpm will automatically download and switch to the right version - if (typeof packageManager === 'string' && packageManager.startsWith('pnpm@')) { + // Self-update the bootstrap pnpm to the version pinned in package.json so + // PATH-resolved `pnpm` (and the bin_dest output) reflect the target + // version. Without this, `pnpm store path` runs as the bootstrap and + // reports a different STORE_VERSION than the one the user's actual + // install writes to — breaking cache: true and actions/setup-node's + // `cache: pnpm` on cold caches (issue #233). + // + // devEngines.packageManager takes priority over packageManager, matching + // pnpm's getWantedPackageManager logic. devEngines.packageManager.version + // can be a semver range; `pnpm self-update` needs a specific version, so + // for ranges we fall through to the bootstrap auto-switch path. + if (devEngines?.packageManager?.name === 'pnpm') { + const v = devEngines.packageManager.version + if (v != null && isExactSemver(v)) { + return v + } return undefined } - if (devEngines?.packageManager?.name === 'pnpm' && devEngines.packageManager.version) { - return undefined + if (packageManagerVersion) { + return packageManagerVersion } if (!GITHUB_WORKSPACE) { @@ -173,6 +196,10 @@ Please specify it by one of the following ways: - in the package.json with the key "devEngines.packageManager"`) } +function isExactSemver(v: string): boolean { + return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(v) +} + function getSystemNodeVersion(): Promise<{ major: number; minor: number }> { return new Promise((resolve) => { const cp = spawn('node', ['--version'], { stdio: ['pipe', 'pipe', 'pipe'], shell: process.platform === 'win32' })