fix(env): use hard links instead of symlinks on Windows (#6405)

close #4315
This commit is contained in:
Zoltan Kochan
2023-04-16 20:57:14 +03:00
committed by GitHub
parent 8e7a86dd95
commit 49b15ac2e3
5 changed files with 45 additions and 13 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-env": patch
"pnpm": patch
---
Use hard links to link the node executable on Windows machines [#4315](https://github.com/pnpm/pnpm/issues/4315).

View File

@@ -41,9 +41,11 @@
"@pnpm/store-path": "workspace:*",
"@zkochan/cmd-shim": "^6.0.0",
"@zkochan/rimraf": "^2.1.2",
"is-windows": "^1.0.2",
"load-json-file": "^6.2.0",
"render-help": "^1.0.3",
"semver": "^7.4.0",
"symlink-dir": "^5.1.1",
"write-json-file": "^4.3.0"
},
"funding": "https://opencollective.com/pnpm",
@@ -51,6 +53,7 @@
"@pnpm/plugin-commands-env": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@types/adm-zip": "^0.5.0",
"@types/is-windows": "^1.0.0",
"@types/semver": "7.3.13",
"adm-zip": "^0.5.10",
"execa": "npm:safe-execa@0.1.2",

View File

@@ -4,10 +4,12 @@ import { PnpmError } from '@pnpm/error'
import { createFetchFromRegistry } from '@pnpm/fetch'
import { resolveNodeVersion } from '@pnpm/node.resolver'
import cmdShim from '@zkochan/cmd-shim'
import isWindows from 'is-windows'
import symlinkDir from 'symlink-dir'
import { getNodeDir, type NvmNodeCommandOptions } from './node'
import { getNodeMirror } from './getNodeMirror'
import { parseNodeEditionSpecifier } from './parseNodeEditionSpecifier'
import { getNodeExecPathInBinDir, getNodeExecPathInNodeDir } from './utils'
import { CURRENT_NODE_DIRNAME, getNodeExecPathInBinDir, getNodeExecPathInNodeDir } from './utils'
export async function envUse (opts: NvmNodeCommandOptions, params: string[]) {
if (!opts.global) {
@@ -27,10 +29,11 @@ export async function envUse (opts: NvmNodeCommandOptions, params: string[]) {
})
const src = getNodeExecPathInNodeDir(nodeDir)
const dest = getNodeExecPathInBinDir(opts.bin)
await symlinkDir(nodeDir, path.join(opts.pnpmHomeDir, CURRENT_NODE_DIRNAME))
try {
await fs.unlink(dest)
} catch (err) {}
await fs.symlink(src, dest, 'file')
await symlinkOrHardLink(src, dest)
try {
let npmDir = nodeDir
if (process.platform !== 'win32') {
@@ -52,3 +55,12 @@ export async function envUse (opts: NvmNodeCommandOptions, params: string[]) {
return `Node.js ${nodeVersion as string} is activated
${dest} -> ${src}`
}
// On Windows, symlinks only work with developer mode enabled
// or with admin permissions. So it is better to use hard links on Windows.
async function symlinkOrHardLink (existingPath: string, newPath: string) {
if (isWindows()) {
return fs.link(existingPath, newPath)
}
return fs.symlink(existingPath, newPath, 'file')
}

View File

@@ -1,15 +1,18 @@
import { promises as fs } from 'fs'
import path from 'path'
export const CURRENT_NODE_DIRNAME = 'nodejs_current'
export async function getNodeExecPathAndTargetDir (pnpmHomeDir: string) {
const nodePath = getNodeExecPathInBinDir(pnpmHomeDir)
let nodeLink: string | undefined
const nodeCurrentDirLink = path.join(pnpmHomeDir, CURRENT_NODE_DIRNAME)
let nodeCurrentDir: string | undefined
try {
nodeLink = await fs.readlink(nodePath)
nodeCurrentDir = await fs.readlink(nodeCurrentDirLink)
} catch (err) {
nodeLink = undefined
nodeCurrentDir = undefined
}
return { nodePath, nodeLink }
return { nodePath, nodeLink: nodeCurrentDir ? getNodeExecPathInNodeDir(nodeCurrentDir) : undefined }
}
export function getNodeExecPathInBinDir (pnpmHomeDir: string) {

22
pnpm-lock.yaml generated
View File

@@ -855,6 +855,9 @@ importers:
'@zkochan/rimraf':
specifier: ^2.1.2
version: 2.1.2
is-windows:
specifier: ^1.0.2
version: 1.0.2
load-json-file:
specifier: ^6.2.0
version: 6.2.0
@@ -864,6 +867,9 @@ importers:
semver:
specifier: ^7.4.0
version: 7.4.0
symlink-dir:
specifier: ^5.1.1
version: 5.1.1
write-json-file:
specifier: ^4.3.0
version: 4.3.0
@@ -877,6 +883,9 @@ importers:
'@types/adm-zip':
specifier: ^0.5.0
version: 0.5.0
'@types/is-windows':
specifier: ^1.0.0
version: 1.0.0
'@types/semver':
specifier: 7.3.13
version: 7.3.13
@@ -8804,7 +8813,7 @@ packages:
/@types/byline@4.2.33:
resolution: {integrity: sha512-LJYez7wrWcJQQDknqZtrZuExMGP0IXmPl1rOOGDqLbu+H7UNNRfKNuSxCBcQMLH1EfjeWidLedC/hCc5dDfBog==}
dependencies:
'@types/node': 18.15.11
'@types/node': 14.18.42
dev: true
/@types/cacheable-request@6.0.3:
@@ -8812,7 +8821,7 @@ packages:
dependencies:
'@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.4
'@types/node': 18.15.11
'@types/node': 14.18.42
'@types/responselike': 1.0.0
/@types/concat-stream@2.0.0:
@@ -8840,7 +8849,7 @@ packages:
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 18.15.11
'@types/node': 14.18.42
dev: true
/@types/graceful-fs@4.1.6:
@@ -8914,7 +8923,7 @@ packages:
/@types/keyv@3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
'@types/node': 18.15.11
'@types/node': 14.18.42
/@types/lodash@4.14.181:
resolution: {integrity: sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==}
@@ -8957,7 +8966,6 @@ packages:
/@types/node@14.18.42:
resolution: {integrity: sha512-xefu+RBie4xWlK8hwAzGh3npDz/4VhF6icY/shU+zv/1fNn+ZVG7T7CRwe9LId9sAYRPxI+59QBPuKL3WpyGRg==}
dev: true
/@types/node@18.15.11:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
@@ -8994,7 +9002,7 @@ packages:
/@types/responselike@1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
'@types/node': 18.15.11
'@types/node': 14.18.42
/@types/retry@0.12.2:
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
@@ -13501,7 +13509,7 @@ packages:
jws: 3.2.2
lodash: 4.17.21
ms: 2.1.3
semver: 7.3.8
semver: 7.4.0
/jsprim@1.4.2:
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}