fix: concurrently hard linking a directory (#10163)

close #10160
This commit is contained in:
Zoltan Kochan
2025-11-09 12:07:11 +01:00
committed by GitHub
parent cbdc1067cc
commit b09722fc01
4 changed files with 38 additions and 11 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/fs.hard-link-dir": patch
"pnpm": patch
---
Don't crash when two processes of pnpm are hardlinking the contents of a directory to the same destination simultaneously [#10160](https://github.com/pnpm/pnpm/pull/10160).

View File

@@ -33,7 +33,9 @@
"compile": "tsc --build && pnpm run lint --fix"
},
"dependencies": {
"@pnpm/graceful-fs": "workspace:*"
"@pnpm/graceful-fs": "workspace:*",
"path-temp": "catalog:",
"rename-overwrite": "catalog:"
},
"peerDependencies": {
"@pnpm/logger": "catalog:"

View File

@@ -4,12 +4,25 @@ import util from 'util'
import fs from 'fs'
import { globalWarn } from '@pnpm/logger'
import gfs from '@pnpm/graceful-fs'
import { sync as renameOverwrite } from 'rename-overwrite'
import { fastPathTemp as pathTemp } from 'path-temp'
export function hardLinkDir (src: string, destDirs: string[]): void {
if (destDirs.length === 0) return
// Don't try to hard link the source directory to itself
destDirs = destDirs.filter((destDir) => path.relative(destDir, src) !== '')
_hardLinkDir(src, destDirs, true)
const filteredDestDirs: string[] = []
const tempDestDirs: string[] = []
for (const destDir of destDirs) {
if (path.relative(destDir, src) === '') {
// Don't try to hard link the source directory to itself
continue
}
filteredDestDirs.push(destDir)
tempDestDirs.push(pathTemp(destDir))
}
_hardLinkDir(src, tempDestDirs, true)
for (let i = 0; i < filteredDestDirs.length; i++) {
renameOverwrite(tempDestDirs[i], filteredDestDirs[i])
}
}
function _hardLinkDir (src: string, destDirs: string[], isRoot?: boolean) {

20
pnpm-lock.yaml generated
View File

@@ -3354,6 +3354,12 @@ importers:
'@pnpm/graceful-fs':
specifier: workspace:*
version: link:../graceful-fs
path-temp:
specifier: 'catalog:'
version: 2.1.0
rename-overwrite:
specifier: 'catalog:'
version: 6.0.2
devDependencies:
'@pnpm/fs.hard-link-dir':
specifier: workspace:*
@@ -18241,7 +18247,7 @@ snapshots:
'@pnpm/fs.packlist': 2.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)
'@zkochan/rimraf': 3.0.2
execa: safe-execa@0.1.2
transitivePeerDependencies:
@@ -18376,7 +18382,7 @@ snapshots:
'@pnpm/find-workspace-dir': 1000.1.0
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)
'@pnpm/workspace.find-packages': 1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30))(typanion@3.14.0)
'@pnpm/workspace.read-manifest': 1000.1.5
load-json-file: 7.0.1
@@ -18576,7 +18582,7 @@ snapshots:
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)
p-defer: 3.0.0
p-limit: 3.1.0
p-queue: 6.6.2
@@ -18595,7 +18601,7 @@ snapshots:
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)
'@zkochan/rimraf': 3.0.2
load-json-file: 6.2.0
ramda: '@pnpm/ramda@0.28.1'
@@ -18874,7 +18880,7 @@ snapshots:
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)
'@zkochan/retry': 0.2.0
lodash.throttle: 4.1.1
p-map-values: 1.0.0
@@ -18913,7 +18919,7 @@ snapshots:
dependencies:
isexe: 2.0.0
'@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)':
'@pnpm/worker@1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)':
dependencies:
'@pnpm/cafs-types': 1000.0.0
'@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0)
@@ -18922,7 +18928,7 @@ snapshots:
'@pnpm/exec.pkg-requires-build': 1000.0.8
'@pnpm/fs.hard-link-dir': 1000.0.1(@pnpm/logger@1001.0.0)
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/logger': link:packages/logger
'@pnpm/store.cafs': 1000.0.13
'@pnpm/symlink-dependency': 1000.0.9(@pnpm/logger@1001.0.0)
'@rushstack/worker-pool': 0.4.9(@types/node@22.15.30)