From 1a681481ac09ec541ba6d8ab501b1d0aec82d2bb Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 27 Jan 2024 18:33:39 +0100 Subject: [PATCH 1/8] feat: add `corepack cleanup` command --- sources/commands/Cleanup.ts | 90 +++++++++++++++++++++++++++++++++++++ sources/main.ts | 2 + sources/specUtils.ts | 23 ++++++++++ 3 files changed, 115 insertions(+) create mode 100644 sources/commands/Cleanup.ts diff --git a/sources/commands/Cleanup.ts b/sources/commands/Cleanup.ts new file mode 100644 index 000000000..1d30a144c --- /dev/null +++ b/sources/commands/Cleanup.ts @@ -0,0 +1,90 @@ +import {Command} from 'clipanion'; +import fs from 'fs'; +import path from 'path'; +import semver from 'semver'; + +import * as debugUtils from '../debugUtils'; +import {getInstallFolder} from '../folderUtils'; +import type {Context} from '../main'; +import type {NodeError} from '../nodeUtils'; +import {parseSpec} from '../specUtils'; + +export class CleanupCommand extends Command { + static paths = [ + [`cleanup`], + ]; + + static usage = Command.Usage({ + description: `Cleans Corepack cache`, + details: ` + When run, this commmand will check what are the versions required by the package.json files it knows of, and remove from the cache all the versions that are in used. + `, + }); + + async execute() { + const installFolder = getInstallFolder(); + const listFile = await fs.promises.open(path.join(installFolder, `packageJsonList.json`), `r+`); + + const previousList = JSON.parse(await listFile.readFile(`utf8`)) as Array; + const listFilteredOffOfInvalidManifests = new Set(); + const inusedSpecs = []; + + for (const pkgPath of previousList) { + let pkgContent: string; + try { + pkgContent = await fs.promises.readFile(pkgPath, `utf8`); + } catch (err) { + if ((err as NodeError)?.code === `ENOENT`) + continue; + + throw err; + } + let packageManager: string; + try { + packageManager = JSON.parse(pkgContent).packageManager; + } catch { + continue; + } + + if (!packageManager) continue; + + try { + inusedSpecs.push(parseSpec(packageManager, pkgPath)); + listFilteredOffOfInvalidManifests.add(pkgPath); + } catch { + continue; + } + } + + await listFile.truncate(0); + await listFile.write(JSON.stringify(Array.from(listFilteredOffOfInvalidManifests)), 0); + await listFile.close(); + + const cacheDir = await fs.promises.opendir(path.join(installFolder)); + const deletionPromises = []; + for await (const dirent of cacheDir) { + if (!dirent.isDirectory() || dirent.name[0] === `.`) continue; + deletionPromises.push(this.cleanUpCacheFolder( + path.join(installFolder, dirent.name), + inusedSpecs.flatMap(spec => spec.name === dirent.name ? spec.range : []), + )); + } + await Promise.all(deletionPromises); + } + + async cleanUpCacheFolder(dirPath: string, ranges: Array) { + const dirIterator = await fs.promises.opendir(dirPath); + const deletionPromises = []; + for await (const dirent of dirIterator) { + if (!dirent.isDirectory() || dirent.name[0] === `.`) continue; + const p = path.join(dirPath, dirent.name); + if (ranges.every(range => !semver.satisfies(dirent.name, range))) { + debugUtils.log(`Removing ${p}`); + deletionPromises.push(fs.promises.rm(p)); + } else { + debugUtils.log(`Keeping ${p}`); + } + } + await Promise.all(deletionPromises); + } +} diff --git a/sources/main.ts b/sources/main.ts index 9b880ee05..55e33c030 100644 --- a/sources/main.ts +++ b/sources/main.ts @@ -3,6 +3,7 @@ import {BaseContext, Builtins, Cli, Command, Option, UsageError} from 'clipanion import {version as corepackVersion} from '../package.json'; import {Engine} from './Engine'; +import {CleanupCommand} from './commands/Cleanup'; import {DisableCommand} from './commands/Disable'; import {EnableCommand} from './commands/Enable'; import {InstallGlobalCommand} from './commands/InstallGlobal'; @@ -117,6 +118,7 @@ export async function runMain(argv: Array) { cli.register(Builtins.HelpCommand); cli.register(Builtins.VersionCommand); + cli.register(CleanupCommand); cli.register(DisableCommand); cli.register(EnableCommand); cli.register(InstallGlobalCommand); diff --git a/sources/specUtils.ts b/sources/specUtils.ts index dcf51f9b3..683697c4f 100644 --- a/sources/specUtils.ts +++ b/sources/specUtils.ts @@ -3,6 +3,8 @@ import fs from 'fs'; import path from 'path'; import semver from 'semver'; +import * as debugUtils from './debugUtils'; +import {getInstallFolder} from './folderUtils'; import {NodeError} from './nodeUtils'; import {Descriptor, Locator, isSupportedPackageManager} from './types'; @@ -123,6 +125,27 @@ export async function loadSpec(initialCwd: string): Promise { if (typeof rawPmSpec === `undefined`) return {type: `NoSpec`, target: selection.manifestPath}; + const pathToListFile = path.join(getInstallFolder(), `packageJsonList.json`); + try { + const file = await fs.promises.open(pathToListFile, `r+`); + const list = JSON.parse(await file.readFile(`utf8`)) as Array; + if (!list?.includes(selection.manifestPath)) { + list.push(selection.manifestPath); + await file.truncate(0); + await file.write(JSON.stringify(list), 0); + } + await file.close(); + } catch (err) { + if ((err as NodeError)?.code === `ENOENT`) { + // If the file doesn't exist yet, we can create it. + await fs.promises.writeFile(pathToListFile, JSON.stringify([selection.manifestPath])) + .catch(debugUtils.log); + } else { + // In case of another error, ignore it. + debugUtils.log(`Failed to update packageJsonList file because of the following error: ${err}`); + } + } + return { type: `Found`, target: selection.manifestPath, From 66b8e793827412e5b9fcd7cd6bd3a640bc904742 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 30 Jan 2024 01:52:27 +0100 Subject: [PATCH 2/8] Update sources/commands/Cleanup.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maël Nison --- sources/commands/Cleanup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/commands/Cleanup.ts b/sources/commands/Cleanup.ts index 1d30a144c..a832ab234 100644 --- a/sources/commands/Cleanup.ts +++ b/sources/commands/Cleanup.ts @@ -11,7 +11,7 @@ import {parseSpec} from '../specUtils'; export class CleanupCommand extends Command { static paths = [ - [`cleanup`], + [`cache`, `clean`], ]; static usage = Command.Usage({ From 761ac6421d5b566a4e8ebe5d764c8ef080698d97 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 30 Jan 2024 01:55:46 +0100 Subject: [PATCH 3/8] s/Cleanup/Cache/ --- sources/commands/{Cleanup.ts => Cache.ts} | 2 +- sources/main.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename sources/commands/{Cleanup.ts => Cache.ts} (98%) diff --git a/sources/commands/Cleanup.ts b/sources/commands/Cache.ts similarity index 98% rename from sources/commands/Cleanup.ts rename to sources/commands/Cache.ts index a832ab234..502060250 100644 --- a/sources/commands/Cleanup.ts +++ b/sources/commands/Cache.ts @@ -9,7 +9,7 @@ import type {Context} from '../main'; import type {NodeError} from '../nodeUtils'; import {parseSpec} from '../specUtils'; -export class CleanupCommand extends Command { +export class CacheCommand extends Command { static paths = [ [`cache`, `clean`], ]; diff --git a/sources/main.ts b/sources/main.ts index 55e33c030..c9a3b9456 100644 --- a/sources/main.ts +++ b/sources/main.ts @@ -3,7 +3,7 @@ import {BaseContext, Builtins, Cli, Command, Option, UsageError} from 'clipanion import {version as corepackVersion} from '../package.json'; import {Engine} from './Engine'; -import {CleanupCommand} from './commands/Cleanup'; +import {CacheCommand} from './commands/Cache'; import {DisableCommand} from './commands/Disable'; import {EnableCommand} from './commands/Enable'; import {InstallGlobalCommand} from './commands/InstallGlobal'; @@ -118,7 +118,7 @@ export async function runMain(argv: Array) { cli.register(Builtins.HelpCommand); cli.register(Builtins.VersionCommand); - cli.register(CleanupCommand); + cli.register(CacheCommand); cli.register(DisableCommand); cli.register(EnableCommand); cli.register(InstallGlobalCommand); From 54c213866954661347616f0ed3cc6412d93cece6 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 30 Jan 2024 01:58:05 +0100 Subject: [PATCH 4/8] simply remove the folder --- sources/commands/Cache.ts | 72 ++------------------------------------- 1 file changed, 2 insertions(+), 70 deletions(-) diff --git a/sources/commands/Cache.ts b/sources/commands/Cache.ts index 502060250..92f951030 100644 --- a/sources/commands/Cache.ts +++ b/sources/commands/Cache.ts @@ -1,13 +1,8 @@ import {Command} from 'clipanion'; import fs from 'fs'; -import path from 'path'; -import semver from 'semver'; -import * as debugUtils from '../debugUtils'; import {getInstallFolder} from '../folderUtils'; import type {Context} from '../main'; -import type {NodeError} from '../nodeUtils'; -import {parseSpec} from '../specUtils'; export class CacheCommand extends Command { static paths = [ @@ -17,74 +12,11 @@ export class CacheCommand extends Command { static usage = Command.Usage({ description: `Cleans Corepack cache`, details: ` - When run, this commmand will check what are the versions required by the package.json files it knows of, and remove from the cache all the versions that are in used. + Removes Corepack cache directory from your local disk. `, }); async execute() { - const installFolder = getInstallFolder(); - const listFile = await fs.promises.open(path.join(installFolder, `packageJsonList.json`), `r+`); - - const previousList = JSON.parse(await listFile.readFile(`utf8`)) as Array; - const listFilteredOffOfInvalidManifests = new Set(); - const inusedSpecs = []; - - for (const pkgPath of previousList) { - let pkgContent: string; - try { - pkgContent = await fs.promises.readFile(pkgPath, `utf8`); - } catch (err) { - if ((err as NodeError)?.code === `ENOENT`) - continue; - - throw err; - } - let packageManager: string; - try { - packageManager = JSON.parse(pkgContent).packageManager; - } catch { - continue; - } - - if (!packageManager) continue; - - try { - inusedSpecs.push(parseSpec(packageManager, pkgPath)); - listFilteredOffOfInvalidManifests.add(pkgPath); - } catch { - continue; - } - } - - await listFile.truncate(0); - await listFile.write(JSON.stringify(Array.from(listFilteredOffOfInvalidManifests)), 0); - await listFile.close(); - - const cacheDir = await fs.promises.opendir(path.join(installFolder)); - const deletionPromises = []; - for await (const dirent of cacheDir) { - if (!dirent.isDirectory() || dirent.name[0] === `.`) continue; - deletionPromises.push(this.cleanUpCacheFolder( - path.join(installFolder, dirent.name), - inusedSpecs.flatMap(spec => spec.name === dirent.name ? spec.range : []), - )); - } - await Promise.all(deletionPromises); - } - - async cleanUpCacheFolder(dirPath: string, ranges: Array) { - const dirIterator = await fs.promises.opendir(dirPath); - const deletionPromises = []; - for await (const dirent of dirIterator) { - if (!dirent.isDirectory() || dirent.name[0] === `.`) continue; - const p = path.join(dirPath, dirent.name); - if (ranges.every(range => !semver.satisfies(dirent.name, range))) { - debugUtils.log(`Removing ${p}`); - deletionPromises.push(fs.promises.rm(p)); - } else { - debugUtils.log(`Keeping ${p}`); - } - } - await Promise.all(deletionPromises); + await fs.promises.rm(getInstallFolder(), {recursive: true, force: true}); } } From fa957c97d7506fecf212c56c210dac2bdf86d456 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 12 Feb 2024 23:45:49 +0100 Subject: [PATCH 5/8] Update sources/specUtils.ts --- sources/specUtils.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/sources/specUtils.ts b/sources/specUtils.ts index 683697c4f..d3a3d85c4 100644 --- a/sources/specUtils.ts +++ b/sources/specUtils.ts @@ -125,27 +125,6 @@ export async function loadSpec(initialCwd: string): Promise { if (typeof rawPmSpec === `undefined`) return {type: `NoSpec`, target: selection.manifestPath}; - const pathToListFile = path.join(getInstallFolder(), `packageJsonList.json`); - try { - const file = await fs.promises.open(pathToListFile, `r+`); - const list = JSON.parse(await file.readFile(`utf8`)) as Array; - if (!list?.includes(selection.manifestPath)) { - list.push(selection.manifestPath); - await file.truncate(0); - await file.write(JSON.stringify(list), 0); - } - await file.close(); - } catch (err) { - if ((err as NodeError)?.code === `ENOENT`) { - // If the file doesn't exist yet, we can create it. - await fs.promises.writeFile(pathToListFile, JSON.stringify([selection.manifestPath])) - .catch(debugUtils.log); - } else { - // In case of another error, ignore it. - debugUtils.log(`Failed to update packageJsonList file because of the following error: ${err}`); - } - } - return { type: `Found`, target: selection.manifestPath, From 2493b0a6344d5f9e9727c16e1673c603d7fac7b4 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 12 Feb 2024 23:47:05 +0100 Subject: [PATCH 6/8] Update sources/specUtils.ts --- sources/specUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/sources/specUtils.ts b/sources/specUtils.ts index d3a3d85c4..dcf51f9b3 100644 --- a/sources/specUtils.ts +++ b/sources/specUtils.ts @@ -3,8 +3,6 @@ import fs from 'fs'; import path from 'path'; import semver from 'semver'; -import * as debugUtils from './debugUtils'; -import {getInstallFolder} from './folderUtils'; import {NodeError} from './nodeUtils'; import {Descriptor, Locator, isSupportedPackageManager} from './types'; From e5497e72880a2ea1abaa24c8c208570027d76442 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 13 Feb 2024 00:29:08 +0100 Subject: [PATCH 7/8] add docs --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 18bcf88e9..2ea1c06ca 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,14 @@ Note that those commands still check whether the local project is configured for the given package manager (ie you won't be able to run `corepack yarn install` on a project where the `packageManager` field references `pnpm`). +### `corepack cache clean` + +Clears the local `COREPACK_HOME` cache directory. + +### `corepack cache clear` + +Clears the local `COREPACK_HOME` cache directory. + ### `corepack enable [... name]` | Option | Description | From 38e638e54e099bedabb7940db47eec24c7f43caa Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 13 Feb 2024 00:30:00 +0100 Subject: [PATCH 8/8] Update sources/commands/Cache.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maël Nison --- sources/commands/Cache.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sources/commands/Cache.ts b/sources/commands/Cache.ts index 92f951030..9f32d7616 100644 --- a/sources/commands/Cache.ts +++ b/sources/commands/Cache.ts @@ -7,6 +7,7 @@ import type {Context} from '../main'; export class CacheCommand extends Command { static paths = [ [`cache`, `clean`], + [`cache`, `clear`], ]; static usage = Command.Usage({