diff --git a/__tests__/commands/remove.js b/__tests__/commands/remove.js index c23fba74bd..9edea77ffc 100644 --- a/__tests__/commands/remove.js +++ b/__tests__/commands/remove.js @@ -140,3 +140,13 @@ test.concurrent('removes subdependencies', (): Promise => { assert.equal(lockFileLines[0], 'dep-c@^1.0.0:'); }); }); + +test.concurrent('can prune the offline mirror', (): Promise => { + return runRemove(['dep-a'], {}, 'prune-offline-mirror', async (config, reporter) => { + const mirrorPath = 'mirror-for-offline'; + assert(!await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-a-1.0.0.tgz`))); + // dep-a depends on dep-b, so dep-b should also be pruned + assert(!await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-b-1.0.0.tgz`))); + assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-c-1.0.0.tgz`))); + }); +}); diff --git a/__tests__/commands/upgrade.js b/__tests__/commands/upgrade.js index 937008f180..912d7da1fa 100644 --- a/__tests__/commands/upgrade.js +++ b/__tests__/commands/upgrade.js @@ -165,3 +165,16 @@ test.concurrent('doesn\'t warn when peer dependency is still met after upgrade', 'peer-dependency-no-warn', ); }); + +test.concurrent('can prune the offline mirror', (): Promise => { + return runUpgrade(['dep-a@1.1.0'], {}, 'prune-offline-mirror', async (config): ?Promise => { + const lockfile = explodeLockfile(await fs.readFile(path.join(config.cwd, 'yarn.lock'))); + assert(lockfile.indexOf('dep-a@1.1.0:') === 0); + + const mirrorPath = 'mirror-for-offline'; + assert(await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-a-1.1.0.tgz`))); + assert(!await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-a-1.0.0.tgz`))); + // In 1.1.0, dep-a doesn't depend on dep-b anymore, so dep-b should be pruned + assert(!await fs.exists(path.join(config.cwd, `${mirrorPath}/dep-b-1.0.0.tgz`))); + }); +}); diff --git a/__tests__/fixtures/remove/prune-offline-mirror/.yarnrc b/__tests__/fixtures/remove/prune-offline-mirror/.yarnrc new file mode 100644 index 0000000000..444ff9631b --- /dev/null +++ b/__tests__/fixtures/remove/prune-offline-mirror/.yarnrc @@ -0,0 +1,6 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +yarn-offline-mirror "./mirror-for-offline" +yarn-offline-mirror-pruning true diff --git a/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz new file mode 100644 index 0000000000..89a694e066 Binary files /dev/null and b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz differ diff --git a/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz new file mode 100644 index 0000000000..4a4afe531c Binary files /dev/null and b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz differ diff --git a/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-c-1.0.0.tgz b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-c-1.0.0.tgz new file mode 100644 index 0000000000..9d362e6a6a Binary files /dev/null and b/__tests__/fixtures/remove/prune-offline-mirror/mirror-for-offline/dep-c-1.0.0.tgz differ diff --git a/__tests__/fixtures/remove/prune-offline-mirror/package.json b/__tests__/fixtures/remove/prune-offline-mirror/package.json new file mode 100755 index 0000000000..b2e17006ec --- /dev/null +++ b/__tests__/fixtures/remove/prune-offline-mirror/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "dep-a": "1.0.0", + "dep-c": "1.0.0" + } +} diff --git a/__tests__/fixtures/remove/prune-offline-mirror/yarn.lock b/__tests__/fixtures/remove/prune-offline-mirror/yarn.lock new file mode 100755 index 0000000000..db5fdd9a02 --- /dev/null +++ b/__tests__/fixtures/remove/prune-offline-mirror/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +dep-a@1.0.0: + version "1.0.0" + resolved dep-a-1.0.0.tgz#d170a960654001b74bdee4747e71f744ecf0a24f + dependencies: + dep-b "1.0.0" + +dep-b@1.0.0: + version "1.0.0" + resolved dep-b-1.0.0.tgz#fa3fab4e36d8eb93ac74790748a30547e9cb0f3f + +dep-c@1.0.0: + version "1.0.0" + resolved dep-c-1.0.0.tgz#67156f3be73744a1d4b7c959c4d0d3575e54f442 diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/.yarnrc b/__tests__/fixtures/upgrade/prune-offline-mirror/.yarnrc new file mode 100644 index 0000000000..444ff9631b --- /dev/null +++ b/__tests__/fixtures/upgrade/prune-offline-mirror/.yarnrc @@ -0,0 +1,6 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +yarn-offline-mirror "./mirror-for-offline" +yarn-offline-mirror-pruning true diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz new file mode 100644 index 0000000000..89a694e066 Binary files /dev/null and b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.0.0.tgz differ diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.1.0.tgz b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.1.0.tgz new file mode 100644 index 0000000000..33db3dbc39 Binary files /dev/null and b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-a-1.1.0.tgz differ diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz new file mode 100644 index 0000000000..4a4afe531c Binary files /dev/null and b/__tests__/fixtures/upgrade/prune-offline-mirror/mirror-for-offline/dep-b-1.0.0.tgz differ diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/package.json b/__tests__/fixtures/upgrade/prune-offline-mirror/package.json new file mode 100755 index 0000000000..c0d4b7cf22 --- /dev/null +++ b/__tests__/fixtures/upgrade/prune-offline-mirror/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "dep-a": "1.0.0" + } +} diff --git a/__tests__/fixtures/upgrade/prune-offline-mirror/yarn.lock b/__tests__/fixtures/upgrade/prune-offline-mirror/yarn.lock new file mode 100755 index 0000000000..d55f1dae06 --- /dev/null +++ b/__tests__/fixtures/upgrade/prune-offline-mirror/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +dep-a@1.0.0: + version "1.0.0" + resolved dep-a-1.0.0.tgz#d170a960654001b74bdee4747e71f744ecf0a24f + dependencies: + dep-b "1.0.0" + +dep-a@1.1.0: + version "1.1.0" + resolved dep-a-1.1.0.tgz#a347d002113da949bd89b6b71195ec3bca09db7a + +dep-b@1.0.0: + version "1.0.0" + resolved dep-b-1.0.0.tgz#fa3fab4e36d8eb93ac74790748a30547e9cb0f3f diff --git a/src/cli/commands/install.js b/src/cli/commands/install.js index 85fca40abd..d6c148d05a 100644 --- a/src/cli/commands/install.js +++ b/src/cli/commands/install.js @@ -549,6 +549,49 @@ export class Install { return flattenedPatterns; } + /** +<<<<<<< HEAD +======= + * Check if the loaded lockfile has all the included patterns + */ + + lockFileInSync(patterns: Array): boolean { + let inSync = true; + for (const pattern of patterns) { + if (!this.lockfile.getLocked(pattern)) { + inSync = false; + break; + } + } + return inSync; + } + + /** + * Remove offline tarballs that are no longer required + */ + + async pruneOfflineMirror(lockfile: Object): Promise { + const mirror = this.config.getOfflineMirrorPath(); + if (!mirror) { + return; + } + + const requiredTarballs = new Set(); + for (const dependency in lockfile) { + const resolved = lockfile[dependency].resolved; + if (resolved) { + requiredTarballs.add(resolved.split('#')[0]); + } + } + + const mirrorTarballs = await fs.walk(mirror); + for (const tarball of mirrorTarballs) { + if (!requiredTarballs.has(tarball.basename)) { + await fs.unlink(tarball.absolute); + } + } + } + /** * Save updated integrity and lockfiles. */ @@ -559,8 +602,13 @@ export class Install { return; } - // stringify current lockfile - const lockSource = lockStringify(this.lockfile.getLockfile(this.resolver.patterns)); + const lockfile = this.lockfile.getLockfile(this.resolver.patterns); + + if (this.config.pruneOfflineMirror) { + await this.pruneOfflineMirror(lockfile); + } + + const lockSource = lockStringify(lockfile); // write integrity hash await this.integrityChecker.save(patterns, lockSource, this.flags, this.resolver.usedRegistries); diff --git a/src/config.js b/src/config.js index 339488fa5c..8a636d057e 100644 --- a/src/config.js +++ b/src/config.js @@ -29,6 +29,7 @@ export type ConfigOptions = { linkFolder?: ?string, offline?: boolean, preferOffline?: boolean, + pruneOfflineMirror?: boolean, captureHar?: boolean, ignoreScripts?: boolean, ignorePlatform?: boolean, @@ -85,6 +86,7 @@ export default class Config { looseSemver: boolean; offline: boolean; preferOffline: boolean; + pruneOfflineMirror: boolean; ignorePlatform: boolean; binLinks: boolean; @@ -241,6 +243,8 @@ export default class Config { constants.MODULE_CACHE_DIRECTORY, ); + this.pruneOfflineMirror = Boolean(this.getOption('yarn-offline-mirror-pruning')); + //init & create cacheFolder, tempFolder this.cacheFolder = path.join(this._cacheRootFolder, 'v' + String(constants.CACHE_VERSION)); this.tempFolder = opts.tempFolder || path.join(this.cacheFolder, '.tmp');