fix!: do not allow to install pnpm globally via pnpm add (#8728)

This commit is contained in:
Zoltan Kochan
2024-11-03 20:47:45 +01:00
committed by GitHub
parent ab041d648d
commit 6b27c811e7
7 changed files with 58 additions and 29 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-installation": major
"pnpm": major
---
`pnpm add --global pnpm` or (`pnpm add --global @pnpm/exe`) fails with an error suggesting to use `pnpm self-update`.

View File

@@ -193,10 +193,15 @@ export async function handler (
'If you don\'t want to see this warning anymore, you may set the ignore-workspace-root-check setting to true.'
)
}
if (opts.global && !opts.bin) {
throw new PnpmError('NO_GLOBAL_BIN_DIR', 'Unable to find the global bin directory', {
hint: 'Run "pnpm setup" to create it automatically, or set the global-bin-dir setting, or the PNPM_HOME env variable. The global bin directory should be in the PATH.',
})
if (opts.global) {
if (!opts.bin) {
throw new PnpmError('NO_GLOBAL_BIN_DIR', 'Unable to find the global bin directory', {
hint: 'Run "pnpm setup" to create it automatically, or set the global-bin-dir setting, or the PNPM_HOME env variable. The global bin directory should be in the PATH.',
})
}
if (params.includes('pnpm') || params.includes('@pnpm/exe')) {
throw new PnpmError('GLOBAL_PNPM_INSTALL', 'Use the "pnpm self-update" command to install or update pnpm')
}
}
const include = {

View File

@@ -353,3 +353,43 @@ test('add: fail when global bin directory is not found', async () => {
}
expect(err.code).toBe('ERR_PNPM_NO_GLOBAL_BIN_DIR')
})
test('add: fail trying to install pnpm', async () => {
prepareEmpty()
let err!: PnpmError
try {
await add.handler({
...DEFAULT_OPTIONS,
bin: path.resolve('project/bin'),
dir: path.resolve('project'),
global: true,
linkWorkspacePackages: false,
saveWorkspaceProtocol: false,
workspace: false,
}, ['pnpm'])
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err.code).toBe('ERR_PNPM_GLOBAL_PNPM_INSTALL')
})
test('add: fail trying to install @pnpm/exe', async () => {
prepareEmpty()
let err!: PnpmError
try {
await add.handler({
...DEFAULT_OPTIONS,
bin: path.resolve('project/bin'),
dir: path.resolve('project'),
global: true,
linkWorkspacePackages: false,
saveWorkspaceProtocol: false,
workspace: false,
}, ['@pnpm/exe'])
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err.code).toBe('ERR_PNPM_GLOBAL_PNPM_INSTALL')
})

6
pnpm-lock.yaml generated
View File

@@ -5796,9 +5796,6 @@ importers:
'@types/semver':
specifier: 'catalog:'
version: 7.5.3
'@types/which':
specifier: 'catalog:'
version: 2.0.2
'@zkochan/retry':
specifier: 'catalog:'
version: 0.2.0
@@ -5886,9 +5883,6 @@ importers:
tree-kill:
specifier: 'catalog:'
version: 1.2.2
which:
specifier: 'catalog:'
version: '@pnpm/which@3.0.1'
write-json-file:
specifier: 'catalog:'
version: 4.3.0

View File

@@ -86,7 +86,6 @@
"@types/pnpm__byline": "catalog:",
"@types/ramda": "catalog:",
"@types/semver": "catalog:",
"@types/which": "catalog:",
"@zkochan/retry": "catalog:",
"@zkochan/rimraf": "catalog:",
"chalk": "catalog:",
@@ -116,7 +115,6 @@
"symlink-dir": "catalog:",
"tempy": "catalog:",
"tree-kill": "catalog:",
"which": "catalog:",
"write-json-file": "catalog:",
"write-pkg": "catalog:",
"write-yaml-file": "catalog:"

View File

@@ -23,7 +23,6 @@ import { isCI } from 'ci-info'
import path from 'path'
import isEmpty from 'ramda/src/isEmpty'
import stripAnsi from 'strip-ansi'
import which from 'which'
import { checkForUpdates } from './checkForUpdates'
import { pnpmCmds, rcOptionsTypes } from './cmd'
import { formatUnknownOptionsError } from './formatError'
@@ -170,21 +169,8 @@ export async function main (inputArgv: string[]): Promise<void> {
global[REPORTER_INITIALIZED] = reporterType
}
const selfUpdate = config.global && (cmd === 'add' || cmd === 'update') && cliParams.includes(packageManager.name)
if (selfUpdate) {
if (cmd === 'self-update') {
await pnpmCmds.server(config as any, ['stop']) // eslint-disable-line @typescript-eslint/no-explicit-any
try {
const currentPnpmDir = path.dirname(which.sync('pnpm'))
if (path.relative(currentPnpmDir, config.bin) !== '') {
console.log(`The location of the currently running pnpm differs from the location where pnpm will be installed
Current pnpm location: ${currentPnpmDir}
Target location: ${config.bin}
`)
}
} catch {
// if pnpm not found, then ignore
}
}
if (
@@ -261,7 +247,7 @@ export async function main (inputArgv: string[]): Promise<void> {
if (
config.updateNotifier !== false &&
!isCI &&
!selfUpdate &&
cmd !== 'self-update' &&
!config.offline &&
!config.preferOffline &&
!config.fallbackCommandUsed &&

View File

@@ -30,7 +30,7 @@ skipOnWindows('self-update stops the store server', async () => {
XDG_DATA_HOME: path.resolve('data'),
}
await execPnpm(['install', '-g', 'pnpm', '--store-dir', path.resolve('..', 'store'), '--reporter=append-only'], { env })
await execPnpm(['self-update', `--config.store-dir=${path.resolve('..', 'store')}`, '--reporter=append-only'], { env })
expect(fs.existsSync(serverJsonPath)).toBeFalsy()
project.isExecutable('../pnpm')