fix: store prune should not crash on unexpected subdirs in the store (#4072)

This commit is contained in:
Zoltan Kochan
2021-12-05 19:16:59 +02:00
committed by GitHub
parent 81ed156660
commit d00e1fc6af
5 changed files with 63 additions and 23 deletions

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

View File

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

View File

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

View File

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

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