fix(store prune): will not fail if store dir does not exist (#8555)

* fix(store prune): will not fail if store dir does not exist

* fix(store prune): will not fail if store dir does not exist

* refactor: package-store

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Colin Casey
2024-09-26 06:15:59 -03:00
committed by GitHub
parent a5af433782
commit 25d37f5102
3 changed files with 84 additions and 4 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/package-store": patch
---
`pnpm store prune` should not fail if the store directory doesn't exist.

View File

@@ -1,4 +1,5 @@
import { promises as fs } from 'fs'
import { type Dirent, promises as fs } from 'fs'
import util from 'util'
import path from 'path'
import { type PackageFilesIndex } from '@pnpm/store.cafs'
import { globalInfo, globalWarn } from '@pnpm/logger'
@@ -24,9 +25,7 @@ export async function prune ({ cacheDir, storeDir }: PruneOptions, removeAlienFi
globalInfo('Removed all cached metadata files')
const pkgIndexFiles = [] as string[]
const removedHashes = new Set<string>()
const dirs = (await fs.readdir(cafsDir, { withFileTypes: true }))
.filter(entry => entry.isDirectory())
.map(dir => dir.name)
const dirs = await getSubdirsSafely(cafsDir)
let fileCounter = 0
await Promise.all(dirs.map(async (dir) => {
const subdir = path.join(cafsDir, dir)
@@ -67,3 +66,18 @@ export async function prune ({ cacheDir, storeDir }: PruneOptions, removeAlienFi
}))
globalInfo(`Removed ${pkgCounter} package${pkgCounter === 1 ? '' : 's'}`)
}
async function getSubdirsSafely (dir: string): Promise<string[]> {
let entries: Dirent[]
try {
entries = await fs.readdir(dir, { withFileTypes: true }) as Dirent[]
} catch (err: unknown) {
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
return []
}
throw err
}
return entries
.filter(entry => entry.isDirectory())
.map(dir => dir.name)
}

View File

@@ -281,6 +281,67 @@ test('prune removes alien files from the store if the --force flag is used', asy
expect(fs.existsSync(alienDir)).toBeFalsy()
})
describe('prune when store directory is not properly configured', () => {
test('prune will not fail if the store directory does not exist (ENOENT)', async () => {
prepareEmpty()
const nonExistentStoreDir = path.resolve('store')
const reporter = jest.fn()
await expect(
store.handler({
cacheDir: path.resolve('cache'),
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: REGISTRY,
},
registries: { default: REGISTRY },
reporter,
storeDir: nonExistentStoreDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
).resolves.toBeUndefined()
expect(reporter).toHaveBeenCalledWith(
expect.objectContaining({
level: 'info',
message: 'Removed 0 files',
})
)
expect(reporter).toHaveBeenCalledWith(
expect.objectContaining({
level: 'info',
message: 'Removed 0 packages',
})
)
})
test('prune will fail for other file-related errors (i.e.; not ENOENT)', async () => {
prepareEmpty()
const fileInPlaceOfStoreDir = path.resolve('store')
fs.writeFileSync(fileInPlaceOfStoreDir, '')
await expect(
store.handler({
cacheDir: path.resolve('cache'),
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: REGISTRY,
},
registries: { default: REGISTRY },
reporter: jest.fn(),
storeDir: fileInPlaceOfStoreDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
).rejects.toThrow(/^ENOTDIR/)
})
})
function createSampleDlxCacheLinkTarget (dirPath: string): void {
fs.mkdirSync(path.join(dirPath, 'node_modules', '.pnpm'), { recursive: true })
fs.mkdirSync(path.join(dirPath, 'node_modules', '.bin'), { recursive: true })