feat: extend symlink error message (#8879)

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
btea
2024-12-26 08:24:47 +08:00
committed by GitHub
parent ecb30d11b4
commit 7272992741
5 changed files with 70 additions and 3 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/worker": patch
"pnpm": patch
---
Print a hint, when installation fails due to an exFAT drive.

29
pnpm-lock.yaml generated
View File

@@ -144,6 +144,9 @@ catalogs:
'@types/semver':
specifier: 7.5.3
version: 7.5.3
'@types/shell-quote':
specifier: ^1.7.5
version: 1.7.5
'@types/signal-exit':
specifier: ^3.0.4
version: 3.0.4
@@ -561,6 +564,9 @@ catalogs:
semver-utils:
specifier: ^1.1.4
version: 1.1.4
shell-quote:
specifier: ^1.8.2
version: 1.8.2
signal-exit:
specifier: ^3.0.7
version: 3.0.7
@@ -7728,9 +7734,15 @@ importers:
'@rushstack/worker-pool':
specifier: 'catalog:'
version: 0.4.9(@types/node@22.5.3)
is-windows:
specifier: 'catalog:'
version: 1.0.2
load-json-file:
specifier: 'catalog:'
version: 6.2.0
shell-quote:
specifier: 'catalog:'
version: 1.8.2
devDependencies:
'@pnpm/logger':
specifier: workspace:*
@@ -7741,6 +7753,12 @@ importers:
'@pnpm/worker':
specifier: workspace:*
version: 'link:'
'@types/is-windows':
specifier: 'catalog:'
version: 1.0.2
'@types/shell-quote':
specifier: 'catalog:'
version: 1.7.5
workspace/filter-packages-from-dir:
dependencies:
@@ -9387,6 +9405,9 @@ packages:
'@types/semver@7.5.8':
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
'@types/shell-quote@1.7.5':
resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==}
'@types/signal-exit@3.0.4':
resolution: {integrity: sha512-e7EUPfU9afHyWc5CXtlqbvVHEshrb05uPlDCenWIbMgtWoFrTuTDVYNLKk6o4X2/4oHTfNqrJX/vaJ3uBhtXTg==}
@@ -13511,6 +13532,10 @@ packages:
shell-quote@1.8.1:
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
shell-quote@1.8.2:
resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==}
engines: {node: '>= 0.4'}
shelljs@0.8.5:
resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==}
engines: {node: '>=4'}
@@ -16408,6 +16433,8 @@ snapshots:
'@types/semver@7.5.8': {}
'@types/shell-quote@1.7.5': {}
'@types/signal-exit@3.0.4': {}
'@types/sinon@10.0.20':
@@ -21248,6 +21275,8 @@ snapshots:
shell-quote@1.8.1: {}
shell-quote@1.8.2: {}
shelljs@0.8.5:
dependencies:
glob: 7.2.3

View File

@@ -87,6 +87,7 @@ catalog:
"@types/semver": 7.5.3
"@types/signal-exit": ^3.0.4
"@types/sinon": ^10.0.20
"@types/shell-quote": ^1.7.5
"@types/ssri": ^7.1.5
"@types/tar": ^6.1.13
"@types/tar-stream": ^2.2.3
@@ -226,6 +227,7 @@ catalog:
semver-range-intersect: ^0.3.1
semver-utils: ^1.1.4
semver: ^7.6.2
shell-quote: ^1.8.2
signal-exit: ^3.0.7
sinon: ^16.1.3
sort-keys: ^4.2.0

View File

@@ -42,12 +42,16 @@
"@pnpm/store.cafs": "workspace:*",
"@pnpm/symlink-dependency": "workspace:*",
"@rushstack/worker-pool": "catalog:",
"load-json-file": "catalog:"
"is-windows": "catalog:",
"load-json-file": "catalog:",
"shell-quote": "catalog:"
},
"devDependencies": {
"@pnpm/logger": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/worker": "workspace:*"
"@pnpm/worker": "workspace:*",
"@types/is-windows": "catalog:",
"@types/shell-quote": "catalog:"
},
"funding": "https://opencollective.com/pnpm",
"exports": {

View File

@@ -3,8 +3,11 @@ import path from 'path'
import os from 'os'
import { WorkerPool } from '@rushstack/worker-pool/lib/WorkerPool'
import { PnpmError } from '@pnpm/error'
import { execSync } from 'child_process'
import isWindows from 'is-windows'
import { type PackageFilesIndex } from '@pnpm/store.cafs'
import { type DependencyManifest } from '@pnpm/types'
import { quote as shellQuote } from 'shell-quote'
import {
type TarballExtractMessage,
type AddDirToStoreMessage,
@@ -217,7 +220,8 @@ export async function symlinkAllModules (
localWorker.once('message', ({ status, error, value }: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
workerPool!.checkinWorker(localWorker)
if (status === 'error') {
reject(new PnpmError(error.code ?? 'SYMLINK_FAILED', error.message as string))
const hint = opts.deps?.[0]?.modules != null ? createErrorHint(error, opts.deps[0].modules) : undefined
reject(new PnpmError(error.code ?? 'SYMLINK_FAILED', error.message as string, { hint }))
return
}
resolve(value)
@@ -229,6 +233,28 @@ export async function symlinkAllModules (
})
}
function createErrorHint (err: Error, checkedDir: string): string | undefined {
if ('code' in err && err.code === 'EISDIR' && isWindows()) {
const checkedDrive = `${checkedDir.split(':')[0]}:`
if (isDriveExFat(checkedDrive)) {
return `The "${checkedDrive}" drive is exFAT, which does not support symlinks. This will cause installation to fail. You can set the node-linker to "hoisted" to avoid this issue.`
}
}
return undefined
}
// In Windows system exFAT drive, symlink will result in error.
function isDriveExFat (drive: string): boolean {
try {
const output = execSync(`wmic logicaldisk where ${shellQuote([`DeviceID='${drive}'`])} get FileSystem`).toString()
const lines = output.trim().split('\n')
const name = lines.length > 1 ? lines[1].trim() : ''
return name === 'exFAT'
} catch {
return false
}
}
export async function hardLinkDir (src: string, destDirs: string[]): Promise<void> {
if (!workerPool) {
workerPool = createTarballWorkerPool()