mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
fix: root -g should not fail
`pnpm root -g` should not fail if the pnpm process has no access to the global bin directory. close #2700 PR #2704
This commit is contained in:
6
.changeset/eight-penguins-hunt.md
Normal file
6
.changeset/eight-penguins-hunt.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/cli-utils": minor
|
||||
"@pnpm/config": minor
|
||||
---
|
||||
|
||||
A new option added that allows to resolve the global bin directory from directories to which there is no write access.
|
||||
5
.changeset/orange-clocks-pretend.md
Normal file
5
.changeset/orange-clocks-pretend.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/global-bin-dir": minor
|
||||
---
|
||||
|
||||
Add a new optional argument. When the argument is `false`, a global bin directory is returned even if the process has no write access to it.
|
||||
@@ -5,12 +5,14 @@ export default async function (
|
||||
cliOptions: CliOptions,
|
||||
opts: {
|
||||
excludeReporter: boolean,
|
||||
globalDirShouldAllowWrite?: boolean,
|
||||
rcOptionsTypes: Record<string, unknown>,
|
||||
workspaceDir: string | undefined,
|
||||
}
|
||||
) {
|
||||
const { config, warnings } = await getConfig({
|
||||
cliOptions,
|
||||
globalDirShouldAllowWrite: opts.globalDirShouldAllowWrite,
|
||||
packageManager,
|
||||
rcOptionsTypes: opts.rcOptionsTypes,
|
||||
workspaceDir: opts.workspaceDir,
|
||||
|
||||
@@ -91,6 +91,7 @@ export type CliOptions = Record<string, unknown> & { dir?: string }
|
||||
|
||||
export default async (
|
||||
opts: {
|
||||
globalDirShouldAllowWrite?: boolean,
|
||||
cliOptions: CliOptions,
|
||||
packageManager: {
|
||||
name: string,
|
||||
@@ -229,7 +230,7 @@ export default async (
|
||||
process.platform === 'win32'
|
||||
? cliOptions.dir : path.resolve(cliOptions.dir, 'bin')
|
||||
)
|
||||
: globalBinDir([pnpmConfig.npmGlobalBinDir])
|
||||
: globalBinDir([pnpmConfig.npmGlobalBinDir], { shouldAllowWrite: opts.globalDirShouldAllowWrite === true })
|
||||
pnpmConfig.allowNew = true
|
||||
pnpmConfig.ignoreCurrentPrefs = true
|
||||
pnpmConfig.saveProd = true
|
||||
|
||||
@@ -4,7 +4,10 @@ import fs = require('fs')
|
||||
import path = require('path')
|
||||
import PATH = require('path-name')
|
||||
|
||||
export default function (knownCandidates: string[] = []) {
|
||||
export default function (
|
||||
knownCandidates: string[] = [],
|
||||
{ shouldAllowWrite = true }: { shouldAllowWrite?: boolean } = {}
|
||||
) {
|
||||
if (!process.env[PATH]) {
|
||||
throw new PnpmError('NO_PATH_ENV',
|
||||
`Couldn't find a global directory for executables because the "${PATH}" environment variable is not set.`)
|
||||
@@ -14,13 +17,17 @@ export default function (knownCandidates: string[] = []) {
|
||||
return pickBestGlobalBinDir(dirs, [
|
||||
...knownCandidates,
|
||||
nodeBinDir,
|
||||
])
|
||||
], shouldAllowWrite)
|
||||
}
|
||||
|
||||
const areDirsEqual = (dir1: string, dir2: string) =>
|
||||
path.relative(dir1, dir2) === ''
|
||||
|
||||
function pickBestGlobalBinDir (dirs: string[], knownCandidates: string[]) {
|
||||
function pickBestGlobalBinDir (
|
||||
dirs: string[],
|
||||
knownCandidates: string[],
|
||||
shouldAllowWrite: boolean
|
||||
) {
|
||||
const noWriteAccessDirs = [] as string[]
|
||||
for (const dir of dirs) {
|
||||
const lowCaseDir = dir.toLowerCase()
|
||||
@@ -41,10 +48,13 @@ function pickBestGlobalBinDir (dirs: string[], knownCandidates: string[]) {
|
||||
hint: `There should be a node, nodejs, npm, or pnpm directory in the "${PATH}" environment variable`,
|
||||
})
|
||||
}
|
||||
throw new PnpmError('GLOBAL_BIN_DIR_PERMISSION', 'No write access to the found global executable directories', {
|
||||
hint: `The found directories:
|
||||
${noWriteAccessDirs.join('\n')}`,
|
||||
})
|
||||
if (shouldAllowWrite) {
|
||||
throw new PnpmError('GLOBAL_BIN_DIR_PERMISSION', 'No write access to the found global executable directories', {
|
||||
hint: `The found directories:
|
||||
${noWriteAccessDirs.join('\n')}`,
|
||||
})
|
||||
}
|
||||
return noWriteAccessDirs[0]
|
||||
}
|
||||
|
||||
const NODE_RELATED_COMMANDS = new Set(['pnpm', 'npm', 'node'])
|
||||
|
||||
@@ -100,6 +100,12 @@ test('when the process has no write access to any of the suitable directories, t
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('when the process has no write access to any of the suitable directories, but opts.shouldAllowWrite is false, return the first match', (t) => {
|
||||
canWriteToDir = (dir) => dir === otherDir
|
||||
t.equal(globalBinDir([], { shouldAllowWrite: false }), nodeGlobalBin)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('throw an exception if non of the directories in the PATH are suitable', (t) => {
|
||||
const pathEnv = process.env[FAKE_PATH]
|
||||
process.env[FAKE_PATH] = [otherDir].join(path.delimiter)
|
||||
|
||||
@@ -78,8 +78,12 @@ export default async function run (inputArgv: string[]) {
|
||||
argv: { remain: string[], cooked: string[], original: string[] },
|
||||
}
|
||||
try {
|
||||
// When we just want to print the location of the global bin directory,
|
||||
// we don't need the write permission to it. Related issue: #2700
|
||||
const globalDirShouldAllowWrite = cmd === 'root'
|
||||
config = await getConfig(cliOptions, {
|
||||
excludeReporter: false,
|
||||
globalDirShouldAllowWrite,
|
||||
rcOptionsTypes: getRCOptionsTypes(cmd),
|
||||
workspaceDir,
|
||||
}) as typeof config
|
||||
|
||||
Reference in New Issue
Block a user