mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-10 18:18:56 -04:00
fix: store prune should not crash on unexpected subdirs in the store (#4072)
This commit is contained in:
6
.changeset/neat-rocks-know.md
Normal file
6
.changeset/neat-rocks-know.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/package-store": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
`pnpm store prune` should not fail if there are unexpected subdirectories in the content-addressable store.
|
||||
@@ -1,7 +1,7 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import { PackageFilesIndex } from '@pnpm/cafs'
|
||||
import { globalInfo } from '@pnpm/logger'
|
||||
import { globalInfo, globalWarn } from '@pnpm/logger'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import loadJsonFile from 'load-json-file'
|
||||
import ssri from 'ssri'
|
||||
@@ -29,6 +29,10 @@ export default async function prune (storeDir: string) {
|
||||
continue
|
||||
}
|
||||
const stat = await fs.stat(filePath)
|
||||
if (stat.isDirectory()) {
|
||||
globalWarn(`An alien directory is present in the store: ${filePath}`)
|
||||
continue
|
||||
}
|
||||
if (stat.nlink === 1 || stat.nlink === BIG_ONE) {
|
||||
await fs.unlink(filePath)
|
||||
fileCounter++
|
||||
|
||||
@@ -39,13 +39,11 @@
|
||||
"@pnpm/prepare": "workspace:0.0.28",
|
||||
"@types/archy": "0.0.31",
|
||||
"@types/ramda": "0.27.39",
|
||||
"@types/sinon": "^9.0.11",
|
||||
"@types/ssri": "^7.1.0",
|
||||
"@zkochan/rimraf": "^2.1.1",
|
||||
"execa": "npm:safe-execa@^0.1.1",
|
||||
"load-json-file": "^6.2.0",
|
||||
"path-exists": "^4.0.0",
|
||||
"sinon": "^11.1.1",
|
||||
"ssri": "^8.0.1",
|
||||
"tempy": "^1.0.0"
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@ import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import execa from 'execa'
|
||||
import isEmpty from 'ramda/src/isEmpty'
|
||||
import sinon from 'sinon'
|
||||
import ssri from 'ssri'
|
||||
|
||||
const STORE_VERSION = 'v3'
|
||||
@@ -25,7 +24,7 @@ test('remove unreferenced packages', async () => {
|
||||
|
||||
await project.storeHas('is-negative', '2.1.0')
|
||||
|
||||
const reporter = sinon.spy()
|
||||
const reporter = jest.fn()
|
||||
await store.handler({
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
@@ -37,14 +36,16 @@ test('remove unreferenced packages', async () => {
|
||||
storeDir,
|
||||
}, ['prune'])
|
||||
|
||||
expect(reporter.calledWithMatch({
|
||||
level: 'info',
|
||||
message: 'Removed 1 package',
|
||||
})).toBeTruthy()
|
||||
expect(reporter).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
level: 'info',
|
||||
message: 'Removed 1 package',
|
||||
})
|
||||
)
|
||||
|
||||
await project.storeHasNot('is-negative', '2.1.0')
|
||||
|
||||
reporter.resetHistory()
|
||||
reporter.mockClear()
|
||||
await store.handler({
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
@@ -56,10 +57,12 @@ test('remove unreferenced packages', async () => {
|
||||
storeDir,
|
||||
}, ['prune'])
|
||||
|
||||
expect(reporter.calledWithMatch({
|
||||
level: 'info',
|
||||
message: 'Removed 1 package',
|
||||
})).toBeFalsy()
|
||||
expect(reporter).not.toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
level: 'info',
|
||||
message: 'Removed 1 package',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('remove packages that are used by project that no longer exist', async () => {
|
||||
@@ -74,7 +77,7 @@ test.skip('remove packages that are used by project that no longer exist', async
|
||||
|
||||
await cafsHas(ssri.fromHex('f0d86377aa15a64c34961f38ac2a9be2b40a1187', 'sha1').toString())
|
||||
|
||||
const reporter = sinon.spy()
|
||||
const reporter = jest.fn()
|
||||
await store.handler({
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
@@ -86,10 +89,12 @@ test.skip('remove packages that are used by project that no longer exist', async
|
||||
storeDir,
|
||||
}, ['prune'])
|
||||
|
||||
expect(reporter.calledWithMatch({
|
||||
level: 'info',
|
||||
message: `- localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0`,
|
||||
})).toBeTruthy()
|
||||
expect(reporter).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
level: 'info',
|
||||
message: `- localhost+${REGISTRY_MOCK_PORT}/is-negative/2.1.0`,
|
||||
})
|
||||
)
|
||||
|
||||
await cafsHasNot(ssri.fromHex('f0d86377aa15a64c34961f38ac2a9be2b40a1187', 'sha1').toString())
|
||||
})
|
||||
@@ -168,3 +173,34 @@ test('prune will skip scanning non-directory in storeDir', async () => {
|
||||
storeDir,
|
||||
}, ['prune'])
|
||||
})
|
||||
|
||||
test('prune does not fail if the store contains an unexpected directory', async () => {
|
||||
const project = prepare()
|
||||
const cacheDir = path.resolve('cache')
|
||||
const storeDir = path.resolve('store')
|
||||
|
||||
await execa('node', [pnpmBin, 'add', 'is-negative@2.1.0', '--store-dir', storeDir, '--registry', REGISTRY])
|
||||
|
||||
await project.storeHas('is-negative', '2.1.0')
|
||||
const alienDir = path.join(storeDir, 'v3/files/44/directory')
|
||||
fs.mkdirSync(alienDir)
|
||||
|
||||
const reporter = jest.fn()
|
||||
await store.handler({
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
rawConfig: {
|
||||
registry: REGISTRY,
|
||||
},
|
||||
registries: { default: REGISTRY },
|
||||
reporter,
|
||||
storeDir,
|
||||
}, ['prune'])
|
||||
|
||||
expect(reporter).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
level: 'warn',
|
||||
message: `An alien directory is present in the store: ${alienDir}`,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
4
pnpm-lock.yaml
generated
4
pnpm-lock.yaml
generated
@@ -2635,7 +2635,6 @@ importers:
|
||||
'@pnpm/types': workspace:7.6.0
|
||||
'@types/archy': 0.0.31
|
||||
'@types/ramda': 0.27.39
|
||||
'@types/sinon': ^9.0.11
|
||||
'@types/ssri': ^7.1.0
|
||||
'@zkochan/rimraf': ^2.1.1
|
||||
archy: ^1.0.0
|
||||
@@ -2647,7 +2646,6 @@ importers:
|
||||
path-exists: ^4.0.0
|
||||
ramda: ^0.27.1
|
||||
render-help: ^1.0.1
|
||||
sinon: ^11.1.1
|
||||
ssri: ^8.0.1
|
||||
tempy: ^1.0.0
|
||||
dependencies:
|
||||
@@ -2678,13 +2676,11 @@ importers:
|
||||
'@pnpm/prepare': link:../../privatePackages/prepare
|
||||
'@types/archy': 0.0.31
|
||||
'@types/ramda': 0.27.39
|
||||
'@types/sinon': 9.0.11
|
||||
'@types/ssri': 7.1.1
|
||||
'@zkochan/rimraf': 2.1.1
|
||||
execa: /safe-execa/0.1.1
|
||||
load-json-file: 6.2.0
|
||||
path-exists: 4.0.0
|
||||
sinon: 11.1.2
|
||||
ssri: 8.0.1
|
||||
tempy: 1.0.1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user