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:
Zoltan Kochan
2020-07-25 18:01:13 +03:00
committed by GitHub
parent bbf492d1c8
commit ad69677a7a
7 changed files with 42 additions and 8 deletions

View 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.

View 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.

View File

@@ -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,

View File

@@ -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

View File

@@ -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'])

View File

@@ -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)

View File

@@ -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