mirror of
https://github.com/pnpm/action-setup.git
synced 2026-05-14 22:33:32 +08:00
fix: self-update bootstrap to packageManager-pinned version (#233)
When package.json pins pnpm via `packageManager` or an exact `devEngines.packageManager.version`, self-update the bootstrap up front. The bootstrap's `pnpm store path` skips pnpm's auto-switch (the `store` command sets `skipPackageManagerCheck = true`), so it reports the bootstrap's `STORE_VERSION` while the user's actual install runs under the pinned version and writes to a different STORE_VERSION — breaking `cache: true` and `setup-node`'s `cache: pnpm` on cold caches.
This commit is contained in:
@@ -98,15 +98,21 @@ export async function runSelfInstaller(inputs: Inputs): Promise<SelfInstallerRes
|
||||
// the bootstrap symlinks in pnpmHome pointing at the old version. Use
|
||||
// PNPM_HOME/bin so consumers of the bin_dest output (e.g.
|
||||
// `${steps.pnpm.outputs.bin_dest}/pnpm`) invoke the requested version.
|
||||
return { exitCode: 0, binDest: path.join(pnpmHome, 'bin') }
|
||||
//
|
||||
// When the requested version equals the bootstrap version, self-update
|
||||
// is a no-op and PNPM_HOME/bin/pnpm is not created — fall back to
|
||||
// pnpmHome, whose symlinks already point at the right version.
|
||||
const updatedBin = path.join(pnpmHome, 'bin', process.platform === 'win32' ? 'pnpm.exe' : 'pnpm')
|
||||
return { exitCode: 0, binDest: existsSync(updatedBin) ? path.join(pnpmHome, 'bin') : pnpmHome }
|
||||
}
|
||||
|
||||
// No explicit target version: rely on the bootstrap pnpm to switch to
|
||||
// the version declared in packageManager/devEngines at runtime. Force
|
||||
// `pmOnFail=download` so a project that pins
|
||||
// `devEngines.packageManager.onFail = "error"` doesn't trip BAD_PM_VERSION
|
||||
// before the switch can happen (issue #252). Scoped to this branch so users
|
||||
// who pass an explicit `version:` input keep strict onFail behavior.
|
||||
// No exact target version we can self-update to (devEngines pins a
|
||||
// semver range, or nothing is pinned at all). Rely on the bootstrap
|
||||
// pnpm to switch versions at runtime. Force `pmOnFail=download` so a
|
||||
// project that pins `devEngines.packageManager.onFail = "error"` doesn't
|
||||
// trip BAD_PM_VERSION before the switch can happen (issue #252). Scoped
|
||||
// to this branch so users who pass an explicit `version:` input keep
|
||||
// strict onFail behavior.
|
||||
exportVariable('pnpm_config_pm_on_fail', 'download')
|
||||
return { exitCode: 0, binDest: pnpmHome }
|
||||
}
|
||||
@@ -135,12 +141,15 @@ function readTargetVersion(opts: {
|
||||
}
|
||||
}
|
||||
|
||||
// packageManager is always exact `pnpm@<version>[+<integrity>]` 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' })
|
||||
|
||||
Reference in New Issue
Block a user