fix: self-update should never install a brokne pnpm CLI (#9194)

This commit is contained in:
Zoltan Kochan
2025-02-28 01:51:58 +01:00
committed by GitHub
parent 331deacd45
commit e0918712ae
4 changed files with 55 additions and 17 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/tools.plugin-commands-self-updater": patch
"pnpm": patch
---
`pnpm self-update` should not leave a directory with a broken pnpm installation if the installation fails.

9
pnpm-lock.yaml generated
View File

@@ -7810,9 +7810,18 @@ importers:
'@pnpm/tools.path':
specifier: workspace:*
version: link:../path
'@zkochan/rimraf':
specifier: 'catalog:'
version: 3.0.2
path-temp:
specifier: 'catalog:'
version: 2.1.0
ramda:
specifier: 'catalog:'
version: '@pnpm/ramda@0.28.1'
rename-overwrite:
specifier: 'catalog:'
version: 6.0.2
render-help:
specifier: 'catalog:'
version: 1.0.3

View File

@@ -39,7 +39,10 @@
"@pnpm/plugin-commands-installation": "workspace:*",
"@pnpm/read-project-manifest": "workspace:*",
"@pnpm/tools.path": "workspace:*",
"@zkochan/rimraf": "catalog:",
"path-temp": "catalog:",
"ramda": "catalog:",
"rename-overwrite": "catalog:",
"render-help": "catalog:"
},
"devDependencies": {

View File

@@ -11,7 +11,10 @@ import { add, type InstallCommandOptions } from '@pnpm/plugin-commands-installat
import { readProjectManifest } from '@pnpm/read-project-manifest'
import { getToolDirPath } from '@pnpm/tools.path'
import { linkBins } from '@pnpm/link-bins'
import { sync as rimraf } from '@zkochan/rimraf'
import { fastPathTemp as pathTemp } from 'path-temp'
import pick from 'ramda/src/pick'
import renameOverwrite from 'rename-overwrite'
import renderHelp from 'render-help'
export function rcOptionsTypes (): Record<string, unknown> {
@@ -80,24 +83,41 @@ export async function handler (
version: resolution.manifest.version,
},
})
if (fs.existsSync(dir)) {
await linkBins(path.join(dir, opts.modulesDir ?? 'node_modules'), opts.pnpmHomeDir,
{
warn: globalWarn,
}
)
return `The ${pref} version, v${resolution.manifest.version}, is already present on the system. It was activated by linking it from ${dir}.`
const alreadyExists = fs.existsSync(dir)
if (!alreadyExists) {
const stage = pathTemp(dir)
fs.mkdirSync(stage, { recursive: true })
fs.writeFileSync(path.join(stage, 'package.json'), '{}')
try {
await add.handler(
{
...opts,
dir: stage,
lockfileDir: stage,
// We want to avoid symlinks because of the rename step,
// which breaks the junctions on Windows.
nodeLinker: 'hoisted',
// This won't be used but there is currently no way to skip the bin creation
// and we can't create the bin shims in the pnpm home directory
// because the stage directory will be renamed.
bin: path.join(stage, 'node_modules/.bin'),
},
[`${currentPkgName}@${resolution.manifest.version}`]
)
renameOverwrite.sync(stage, dir)
} catch (err: unknown) {
try {
rimraf(stage)
} catch {} // eslint-disable-line:no-empty
throw err
}
}
fs.mkdirSync(dir, { recursive: true })
fs.writeFileSync(path.join(dir, 'package.json'), '{}')
await add.handler(
await linkBins(path.join(dir, opts.modulesDir ?? 'node_modules'), opts.pnpmHomeDir,
{
...opts,
dir,
lockfileDir: dir,
bin: opts.pnpmHomeDir,
},
[`${currentPkgName}@${resolution.manifest.version}`]
warn: globalWarn,
}
)
return undefined
return alreadyExists
? `The ${pref} version, v${resolution.manifest.version}, is already present on the system. It was activated by linking it from ${dir}.`
: undefined
}