Skip to content

Commit

Permalink
feat: prune --force removes alien directories (#7272)
Browse files Browse the repository at this point in the history
add the ability to use --force to remove alien directory from the store
  • Loading branch information
tris203 committed Nov 7, 2023
1 parent abdf1f2 commit 291607c
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 6 deletions.
8 changes: 8 additions & 0 deletions .changeset/rich-radios-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@pnpm/store-controller-types": patch
"@pnpm/plugin-commands-store": patch
"@pnpm/package-store": patch
"pnpm": patch
---

When using `pnpm store prune --force` alien directories are removed from the store [#7272](https://github.com/pnpm/pnpm/pull/7272).
13 changes: 10 additions & 3 deletions store/package-store/src/storeController/prune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface PruneOptions {
storeDir: string
}

export async function prune ({ cacheDir, storeDir }: PruneOptions) {
export async function prune ({ cacheDir, storeDir }: PruneOptions, removeAlienFiles?: boolean) {
const cafsDir = path.join(storeDir, 'files')
await Promise.all([
rimraf(path.join(cacheDir, 'metadata')),
Expand All @@ -38,8 +38,15 @@ export async function prune ({ cacheDir, storeDir }: PruneOptions) {
}
const stat = await fs.stat(filePath)
if (stat.isDirectory()) {
globalWarn(`An alien directory is present in the store: ${filePath}`)
return
if (removeAlienFiles) {
await rimraf(filePath)
globalWarn(`An alien directory has been removed from the store: ${filePath}`)
fileCounter++
return
} else {
globalWarn(`An alien directory is present in the store: ${filePath}`)
return
}
}
if (stat.nlink === 1 || stat.nlink === BIG_ONE) {
await fs.unlink(filePath)
Expand Down
9 changes: 8 additions & 1 deletion store/plugin-commands-store/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function cliOptionsTypes () {
'registry',
'store',
'store-dir',
'force',
], allTypes)
}

Expand Down Expand Up @@ -47,6 +48,11 @@ Pruning the store is not harmful, but might slow down future installations. \
Visit the documentation for more information on unreferenced packages and why they occur',
name: 'prune',
},
{
description: 'If there are alien directories in the store, this command removes them. \
Alien directories are directories/files that were not created by the package manager.',
name: 'prune --force',
},
{
description: 'Returns the path to the active store directory.',
name: 'path',
Expand All @@ -67,7 +73,7 @@ class StoreStatusError extends PnpmError {
}
}

export type StoreCommandOptions = Pick<Config, 'dir' | 'registries' | 'tag' | 'storeDir'> & CreateStoreControllerOptions & {
export type StoreCommandOptions = Pick<Config, 'dir' | 'registries' | 'tag' | 'storeDir' | 'force'> & CreateStoreControllerOptions & {
reporter?: (logObj: LogBase) => void
}

Expand All @@ -87,6 +93,7 @@ export async function handler (opts: StoreCommandOptions, params: string[]) {
const storePruneOptions = Object.assign(opts, {
storeController: store.ctrl,
storeDir: store.dir,
removeAlienFiles: opts.force,
})
return storePrune(storePruneOptions)
}
Expand Down
3 changes: 2 additions & 1 deletion store/plugin-commands-store/src/storePrune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ export async function storePrune (
opts: {
reporter?: ReporterFunction
storeController: StoreController
removeAlienFiles?: boolean
}
) {
const reporter = opts?.reporter
if ((reporter != null) && typeof reporter === 'function') {
streamParser.on('data', reporter)
}
await opts.storeController.prune()
await opts.storeController.prune(opts.removeAlienFiles)
await opts.storeController.close()

if ((reporter != null) && typeof reporter === 'function') {
Expand Down
37 changes: 37 additions & 0 deletions store/plugin-commands-store/test/storePrune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,41 @@ test('prune does not fail if the store contains an unexpected directory', async
message: `An alien directory is present in the store: ${alienDir}`,
})
)

// as force is not used, the alien directory is not removed
expect(fs.existsSync(alienDir)).toBeTruthy()
})

test('prune removes alien files from the store if the --force flag is used', 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(),
pnpmHomeDir: '',
rawConfig: {
registry: REGISTRY,
},
registries: { default: REGISTRY },
reporter,
storeDir,
userConfig: {},
force: true,
}, ['prune'])
expect(reporter).toBeCalledWith(
expect.objectContaining({
level: 'warn',
message: `An alien directory has been removed from the store: ${alienDir}`,
})
)
expect(fs.existsSync(alienDir)).toBeFalsy()
})
2 changes: 1 addition & 1 deletion store/store-controller-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface StoreController {
getFilesIndexFilePath: GetFilesIndexFilePath
importPackage: ImportPackageFunctionAsync
close: () => Promise<void>
prune: () => Promise<void>
prune: (removeAlienFiles?: boolean) => Promise<void>
upload: UploadPkgToStore
}

Expand Down

0 comments on commit 291607c

Please sign in to comment.