From ed6b9b99c4a83ec9ab8a113892ba900365316b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 2 Mar 2017 09:38:14 -0800 Subject: [PATCH] Adds versioning to the cache path (#2768) * Adds versioning to the cache path * Adds a test to check that the cache path is correctly updated * Renames "cacheFolder" into "versionedCacheFolder" * Adds a test to check that the cache is correctly busted when bumping the cache version * Fixes tests on Windows * Changes variable names --- __tests__/commands/install/integration.js | 75 +++- __tests__/package-resolver.js | 13 +- flow-typed/npm/jest_v14.0.x.js | 73 ---- flow-typed/npm/jest_v18.x.x.js | 441 ++++++++++++++++++++++ src/cli/commands/cache.js | 14 +- src/cli/commands/global.js | 2 +- src/cli/index.js | 2 +- src/config.js | 11 +- src/constants.js | 3 + 9 files changed, 545 insertions(+), 89 deletions(-) delete mode 100644 flow-typed/npm/jest_v14.0.x.js create mode 100644 flow-typed/npm/jest_v18.x.x.js diff --git a/__tests__/commands/install/integration.js b/__tests__/commands/install/integration.js index 327822c08c..bc0547f8d0 100644 --- a/__tests__/commands/install/integration.js +++ b/__tests__/commands/install/integration.js @@ -1,5 +1,8 @@ /* @flow */ +import type Config from '../../../src/config'; +import PackageResolver from '../../../src/package-resolver.js'; +import {run as cache} from '../../../src/cli/commands/cache.js'; import {run as check} from '../../../src/cli/commands/check.js'; import * as constants from '../../../src/constants.js'; import * as reporters from '../../../src/reporters/index.js'; @@ -11,15 +14,41 @@ import {promisify} from '../../../src/util/promise'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 150000; -const request = require('request'); +let request = require('request'); const assert = require('assert'); const semver = require('semver'); const fsNode = require('fs'); const path = require('path'); +const stream = require('stream'); const os = require('os'); +async function mockConstants(base: Config, mocks: Object, cb: (config: Config) => Promise): Promise { + // We cannot put this function inside _helpers, because we need to change the "request" variable + // after resetting the modules. Updating this variable is required because some tests check what + // happened during the Yarn execution, and they need to use the same instance of "request" than + // the Yarn environment. + + const opts = {}; + + opts.binLinks = base.binLinks; + opts.cwd = base.cwd; + opts.globalFolder = base.globalFolder; + opts.linkFolder = base.linkFolder; + opts.production = base.production; + opts.cacheFolder = base._cacheRootFolder; + + const automock = jest.genMockFromModule('../../../src/constants'); + jest.setMock('../../../src/constants', Object.assign(automock, mocks)); + + jest.resetModules(); + request = require('request'); + + jest.mock('../../../src/constants'); + await cb(await require('../../../src/config.js').default.create(opts, base.reporter)); + jest.unmock('../../../src/constants'); +} + beforeEach(request.__resetAuthedRequests); -// $FlowFixMe afterEach(request.__resetAuthedRequests); test.concurrent('properly find and save build artifacts', async () => { @@ -37,6 +66,48 @@ test.concurrent('properly find and save build artifacts', async () => { }); }); +test('changes the cache path when bumping the cache version', async () => { + await runInstall({}, 'install-github', async (config): Promise => { + const inOut = new stream.PassThrough(); + const reporter = new reporters.JSONReporter({stdout: inOut}); + + await cache(config, reporter, {}, ['dir']); + assert.ok(!!(JSON.parse(String(inOut.read())) : any).data.match(/[\\\/]v1[\\\/]?$/)); + + await mockConstants(config, {CACHE_VERSION: 42}, async (config): Promise => { + await cache(config, reporter, {}, ['dir']); + assert.ok(!!(JSON.parse(String(inOut.read())) : any).data.match(/[\\\/]v42[\\\/]?$/)); + }); + }); +}); + +test('changes the cache directory when bumping the cache version', async () => { + await runInstall({}, 'install-production', async (config, reporter): Promise => { + const lockfile = await Lockfile.fromDirectory(config.cwd); + + const resolver = new PackageResolver(config, lockfile); + await resolver.init([{pattern: 'is-array', registry: 'npm'}]); + + const ref = resolver.getPackageReferences()[0]; + const cachePath = config.generateHardModulePath(ref, true); + + await fs.writeFile(path.join(cachePath, 'yarn.test'), 'YARN TEST'); + await fs.unlink(path.join(config.cwd, 'node_modules')); + + const firstReinstall = new Install({skipIntegrityCheck: true}, config, reporter, lockfile); + await firstReinstall.init(); + + assert.ok(await fs.exists(path.join(config.cwd, 'node_modules', 'is-array', 'yarn.test'))); + + await mockConstants(config, {CACHE_VERSION: 42}, async (config): Promise => { + const secondReinstall = new Install({skipIntegrityCheck: true}, config, reporter, lockfile); + await secondReinstall.init(); + + assert.ok(!await fs.exists(path.join(config.cwd, 'node_modules', 'is-array', 'yarn.test'))); + }); + }); +}); + test.concurrent("removes extraneous files that aren't in module or artifacts", async () => { async function check(cwd: string): Promise { // retains artifact diff --git a/__tests__/package-resolver.js b/__tests__/package-resolver.js index b624bfd9b2..1a993f2a7b 100644 --- a/__tests__/package-resolver.js +++ b/__tests__/package-resolver.js @@ -30,18 +30,21 @@ function addTest(pattern, registry = 'npm', init: ?(cacheFolder: string) => Prom const reporter = new reporters.NoopReporter({}); const loc = await makeTemp(); - await fs.mkdirp(path.join(loc, 'node_modules')); const cacheFolder = path.join(loc, 'cache'); - await fs.mkdirp(cacheFolder); - if (init) { - await init(cacheFolder); - } const config = await Config.create({ cwd: loc, offline, cacheFolder, }, reporter); + + await fs.mkdirp(path.join(loc, 'node_modules')); + await fs.mkdirp(config.cacheFolder); + + if (init) { + await init(config.cacheFolder); + } + const resolver = new PackageResolver(config, lockfile); await resolver.init([{pattern, registry}]); diff --git a/flow-typed/npm/jest_v14.0.x.js b/flow-typed/npm/jest_v14.0.x.js deleted file mode 100644 index 5fe8cb3917..0000000000 --- a/flow-typed/npm/jest_v14.0.x.js +++ /dev/null @@ -1,73 +0,0 @@ -// flow-typed signature: e2130120dcdc34bf09ff82449b0d508c -// flow-typed version: 230d7577ce/jest_v12.0.x/flow_>=v0.23.x - -type JestMockFn = { - (...args: Array): any; - mock: { - calls: Array>; - instances: any; - }; - mockClear(): Function; - mockImplementation(fn: Function): JestMockFn; - mockImplementationOnce(fn: Function): JestMockFn; - mockReturnThis(): any; - mockReturnValue(value: any): JestMockFn; - mockReturnValueOne(value: any): JestMockFn; -} - -declare function beforeEach(fn: Function): void; -declare function describe(name: string, fn: Function): void; -declare function fdescribe(name: string, fn: Function): void; -declare function fit(name: string, fn: Function): ?Promise; -declare function it(name: string, fn: Function): ?Promise; -declare function pit(name: string, fn: Function): Promise; -declare function test(name: string, fn: Function): ?Promise; -declare function xdescribe(name: string, fn: Function): void; -declare function xit(name: string, fn: Function): ?Promise; - -type JestExpectType = { - not: JestExpectType; - lastCalledWith(...args: Array): void; - toBe(value: any): void; - toBeCalled(): void; - toBeCalledWith(...args: Array): void; - toBeCloseTo(num: number, delta: any): void; - toBeDefined(): void; - toBeFalsy(): void; - toBeGreaterThan(number: number): void; - toBeLessThan(number: number): void; - toBeNull(): void; - toBeTruthy(): void; - toBeUndefined(): void; - toContain(str: string): void; - toEqual(value: any): void; - toMatch(regexp: RegExp): void; - toMatchSnapshot(): void; - toThrow(message?: string | Error): void; - toThrowError(message?: string): void; - toContainPackage(packageName: string, path: string): void; -} - -declare function expect(value: any): JestExpectType; - -declare var jest: { - autoMockOff(): void; - autoMockOn(): void; - clearAllTimers(): void; - currentTestPath(): void; - disableAutomock(): void; - enableAutomock(): void; - fn(implementation?: Function): JestMockFn; - genMockFromModule(moduleName: string): any; - mock(moduleName: string, moduleFactory?: any): void; - runAllTicks(): void; - runAllTimers(): void; - runOnlyPendingTimers(): void; - setMock(moduleName: string, moduleExports: any): void; - unmock(moduleName: string): void; -} - -declare var jasmine: { - DEFAULT_TIMEOUT_INTERVAL: number; - addMatchers(matcher: Object): void; -} diff --git a/flow-typed/npm/jest_v18.x.x.js b/flow-typed/npm/jest_v18.x.x.js new file mode 100644 index 0000000000..83a52324a2 --- /dev/null +++ b/flow-typed/npm/jest_v18.x.x.js @@ -0,0 +1,441 @@ +type JestMockFn = { + (...args: Array): any, + /** + * An object for introspecting mock calls + */ + mock: { + /** + * An array that represents all calls that have been made into this mock + * function. Each call is represented by an array of arguments that were + * passed during the call. + */ + calls: Array>, + /** + * An array that contains all the object instances that have been + * instantiated from this mock function. + */ + instances: mixed, + }, + /** + * Resets all information stored in the mockFn.mock.calls and + * mockFn.mock.instances arrays. Often this is useful when you want to clean + * up a mock's usage data between two assertions. + */ + mockClear(): Function, + /** + * Resets all information stored in the mock. This is useful when you want to + * completely restore a mock back to its initial state. + */ + mockReset(): Function, + /** + * Accepts a function that should be used as the implementation of the mock. + * The mock itself will still record all calls that go into and instances + * that come from itself -- the only difference is that the implementation + * will also be executed when the mock is called. + */ + mockImplementation(fn: Function): JestMockFn, + /** + * Accepts a function that will be used as an implementation of the mock for + * one call to the mocked function. Can be chained so that multiple function + * calls produce different results. + */ + mockImplementationOnce(fn: Function): JestMockFn, + /** + * Just a simple sugar function for returning `this` + */ + mockReturnThis(): void, + /** + * Deprecated: use jest.fn(() => value) instead + */ + mockReturnValue(value: any): JestMockFn, + /** + * Sugar for only returning a value once inside your mock + */ + mockReturnValueOnce(value: any): JestMockFn, +} + +type JestAsymmetricEqualityType = { + /** + * A custom Jasmine equality tester + */ + asymmetricMatch(value: mixed): boolean, +} + +type JestCallsType = { + allArgs(): mixed, + all(): mixed, + any(): boolean, + count(): number, + first(): mixed, + mostRecent(): mixed, + reset(): void, +} + +type JestClockType = { + install(): void, + mockDate(date: Date): void, + tick(): void, + uninstall(): void, +} + +type JestMatcherResult = { + message?: string | ()=>string, + pass: boolean, +} + +type JestMatcher = (actual: any, expected: any) => JestMatcherResult; + +type JestExpectType = { + not: JestExpectType, + /** + * If you have a mock function, you can use .lastCalledWith to test what + * arguments it was last called with. + */ + lastCalledWith(...args: Array): void, + /** + * toBe just checks that a value is what you expect. It uses === to check + * strict equality. + */ + toBe(value: any): void, + /** + * Use .toHaveBeenCalled to ensure that a mock function got called. + */ + toBeCalled(): void, + /** + * Use .toBeCalledWith to ensure that a mock function was called with + * specific arguments. + */ + toBeCalledWith(...args: Array): void, + /** + * Using exact equality with floating point numbers is a bad idea. Rounding + * means that intuitive things fail. + */ + toBeCloseTo(num: number, delta: any): void, + /** + * Use .toBeDefined to check that a variable is not undefined. + */ + toBeDefined(): void, + /** + * Use .toBeFalsy when you don't care what a value is, you just want to + * ensure a value is false in a boolean context. + */ + toBeFalsy(): void, + /** + * To compare floating point numbers, you can use toBeGreaterThan. + */ + toBeGreaterThan(number: number): void, + /** + * To compare floating point numbers, you can use toBeGreaterThanOrEqual. + */ + toBeGreaterThanOrEqual(number: number): void, + /** + * To compare floating point numbers, you can use toBeLessThan. + */ + toBeLessThan(number: number): void, + /** + * To compare floating point numbers, you can use toBeLessThanOrEqual. + */ + toBeLessThanOrEqual(number: number): void, + /** + * Use .toBeInstanceOf(Class) to check that an object is an instance of a + * class. + */ + toBeInstanceOf(cls: Class<*>): void, + /** + * .toBeNull() is the same as .toBe(null) but the error messages are a bit + * nicer. + */ + toBeNull(): void, + /** + * Use .toBeTruthy when you don't care what a value is, you just want to + * ensure a value is true in a boolean context. + */ + toBeTruthy(): void, + /** + * Use .toBeUndefined to check that a variable is undefined. + */ + toBeUndefined(): void, + /** + * Use .toContain when you want to check that an item is in a list. For + * testing the items in the list, this uses ===, a strict equality check. + */ + toContain(item: any): void, + /** + * Use .toContainEqual when you want to check that an item is in a list. For + * testing the items in the list, this matcher recursively checks the + * equality of all fields, rather than checking for object identity. + */ + toContainEqual(item: any): void, + /** + * Use .toEqual when you want to check that two objects have the same value. + * This matcher recursively checks the equality of all fields, rather than + * checking for object identity. + */ + toEqual(value: any): void, + /** + * Use .toHaveBeenCalled to ensure that a mock function got called. + */ + toHaveBeenCalled(): void, + /** + * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact + * number of times. + */ + toHaveBeenCalledTimes(number: number): void, + /** + * Use .toHaveBeenCalledWith to ensure that a mock function was called with + * specific arguments. + */ + toHaveBeenCalledWith(...args: Array): void, + /** + * Check that an object has a .length property and it is set to a certain + * numeric value. + */ + toHaveLength(number: number): void, + /** + * + */ + toHaveProperty(propPath: string, value?: any): void, + /** + * Use .toMatch to check that a string matches a regular expression. + */ + toMatch(regexp: RegExp): void, + /** + * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. + */ + toMatchObject(object: Object): void, + /** + * This ensures that a React component matches the most recent snapshot. + */ + toMatchSnapshot(name?: string): void, + /** + * Use .toThrow to test that a function throws when it is called. + */ + toThrow(message?: string | Error): void, + /** + * Use .toThrowError to test that a function throws a specific error when it + * is called. The argument can be a string for the error message, a class for + * the error, or a regex that should match the error. + */ + toThrowError(message?: string | Error | RegExp): void, + /** + * Use .toThrowErrorMatchingSnapshot to test that a function throws a error + * matching the most recent snapshot when it is called. + */ + toThrowErrorMatchingSnapshot(): void, + /** + * Custom function; need to be backported when updating this file + */ + toContainPackage(packageName: string, path: string): void; +} + +type JestObjectType = { + /** + * Disables automatic mocking in the module loader. + * + * After this method is called, all `require()`s will return the real + * versions of each module (rather than a mocked version). + */ + disableAutomock(): JestObjectType, + /** + * An un-hoisted version of disableAutomock + */ + autoMockOff(): JestObjectType, + /** + * Enables automatic mocking in the module loader. + */ + enableAutomock(): JestObjectType, + /** + * An un-hoisted version of enableAutomock + */ + autoMockOn(): JestObjectType, + /** + * Resets the state of all mocks. Equivalent to calling .mockReset() on every + * mocked function. + */ + resetAllMocks(): JestObjectType, + /** + * Removes any pending timers from the timer system. + */ + clearAllTimers(): void, + /** + * The same as `mock` but not moved to the top of the expectation by + * babel-jest. + */ + doMock(moduleName: string, moduleFactory?: any): JestObjectType, + /** + * The same as `unmock` but not moved to the top of the expectation by + * babel-jest. + */ + dontMock(moduleName: string): JestObjectType, + /** + * Returns a new, unused mock function. Optionally takes a mock + * implementation. + */ + fn(implementation?: Function): JestMockFn, + /** + * Determines if the given function is a mocked function. + */ + isMockFunction(fn: Function): boolean, + /** + * Given the name of a module, use the automatic mocking system to generate a + * mocked version of the module for you. + */ + genMockFromModule(moduleName: string): any, + /** + * Mocks a module with an auto-mocked version when it is being required. + * + * The second argument can be used to specify an explicit module factory that + * is being run instead of using Jest's automocking feature. + * + * The third argument can be used to create virtual mocks -- mocks of modules + * that don't exist anywhere in the system. + */ + mock(moduleName: string, moduleFactory?: any): JestObjectType, + /** + * Resets the module registry - the cache of all required modules. This is + * useful to isolate modules where local state might conflict between tests. + */ + resetModules(): JestObjectType, + /** + * Exhausts the micro-task queue (usually interfaced in node via + * process.nextTick). + */ + runAllTicks(): void, + /** + * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), + * setInterval(), and setImmediate()). + */ + runAllTimers(): void, + /** + * Exhausts all tasks queued by setImmediate(). + */ + runAllImmediates(): void, + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + */ + runTimersToTime(msToRun: number): void, + /** + * Executes only the macro-tasks that are currently pending (i.e., only the + * tasks that have been queued by setTimeout() or setInterval() up to this + * point) + */ + runOnlyPendingTimers(): void, + /** + * Explicitly supplies the mock object that the module system should return + * for the specified module. Note: It is recommended to use jest.mock() + * instead. + */ + setMock(moduleName: string, moduleExports: any): JestObjectType, + /** + * Indicates that the module system should never return a mocked version of + * the specified module from require() (e.g. that it should always return the + * real module). + */ + unmock(moduleName: string): JestObjectType, + /** + * Instructs Jest to use fake versions of the standard timer functions + * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, + * setImmediate and clearImmediate). + */ + useFakeTimers(): JestObjectType, + /** + * Instructs Jest to use the real versions of the standard timer functions. + */ + useRealTimers(): JestObjectType, +} + +type JestSpyType = { + calls: JestCallsType, +} + +/** Runs this function after every test inside this context */ +declare function afterEach(fn: Function): void; +/** Runs this function before every test inside this context */ +declare function beforeEach(fn: Function): void; +/** Runs this function after all tests have finished inside this context */ +declare function afterAll(fn: Function): void; +/** Runs this function before any tests have started inside this context */ +declare function beforeAll(fn: Function): void; +/** A context for grouping tests together */ +declare function describe(name: string, fn: Function): void; + +/** An individual test unit */ +declare var it: { + /** + * An individual test unit + * + * @param {string} Name of Test + * @param {Function} Test + */ + (name: string, fn?: Function): ?Promise, + /** + * Only run this test + * + * @param {string} Name of Test + * @param {Function} Test + */ + only(name: string, fn?: Function): ?Promise, + /** + * Skip running this test + * + * @param {string} Name of Test + * @param {Function} Test + */ + skip(name: string, fn?: Function): ?Promise, + /** + * Run the test concurrently + * + * @param {string} Name of Test + * @param {Function} Test + */ + concurrent(name: string, fn?: Function): ?Promise, +}; +declare function fit(name: string, fn: Function): ?Promise; +/** An individual test unit */ +declare var test: typeof it; +/** A disabled group of tests */ +declare var xdescribe: typeof describe; +/** A focused group of tests */ +declare var fdescribe: typeof describe; +/** A disabled individual test */ +declare var xit: typeof it; +/** A disabled individual test */ +declare var xtest: typeof it; + +/** The expect function is used every time you want to test a value */ +declare var expect: { + /** The object that you want to make assertions against */ + (value: any): JestExpectType, + /** Add additional Jasmine matchers to Jest's roster */ + extend(matchers: {[name:string]: JestMatcher}): void, + assertions(expectedAssertions: number): void, + any(value: mixed): JestAsymmetricEqualityType, + anything(): void, + arrayContaining(value: Array): void, + objectContaining(value: Object): void, + stringMatching(value: string): void, +}; + +// TODO handle return type +// http://jasmine.github.io/2.4/introduction.html#section-Spies +declare function spyOn(value: mixed, method: string): Object; + +/** Holds all functions related to manipulating test runner */ +declare var jest: JestObjectType + +/** + * The global Jamine object, this is generally not exposed as the public API, + * using features inside here could break in later versions of Jest. + */ +declare var jasmine: { + DEFAULT_TIMEOUT_INTERVAL: number, + any(value: mixed): JestAsymmetricEqualityType, + anything(): void, + arrayContaining(value: Array): void, + clock(): JestClockType, + createSpy(name: string): JestSpyType, + objectContaining(value: Object): void, + stringMatching(value: string): void, + addMatchers(matcher: Object): void, +} diff --git a/src/cli/commands/cache.js b/src/cli/commands/cache.js index fe5a964520..539fb11868 100644 --- a/src/cli/commands/cache.js +++ b/src/cli/commands/cache.js @@ -50,8 +50,11 @@ export const {run, setFlags} = buildSubCommands('cache', { reporter.table(['Name', 'Version', 'Registry', 'Resolved'], body); }, - dir(config: Config) { - console.log(config.cacheFolder); + dir( + config: Config, + reporter: Reporter, + ) { + reporter.log(config.cacheFolder); }, async clean( @@ -60,10 +63,9 @@ export const {run, setFlags} = buildSubCommands('cache', { flags: Object, args: Array, ): Promise { - const cacheFolder = config.cacheFolder; - if (cacheFolder) { - await fs.unlink(cacheFolder); - await fs.mkdirp(cacheFolder); + if (config.cacheFolder) { + await fs.unlink(config._cacheRootFolder); + await fs.mkdirp(config.cacheFolder); reporter.success(reporter.lang('clearedCache')); } }, diff --git a/src/cli/commands/global.js b/src/cli/commands/global.js index 8fbecc3fd6..53cdb207d9 100644 --- a/src/cli/commands/global.js +++ b/src/cli/commands/global.js @@ -41,7 +41,7 @@ async function updateCwd(config: Config): Promise { cwd: config.globalFolder, binLinks: true, globalFolder: config.globalFolder, - cacheFolder: config.cacheFolder, + cacheFolder: config._cacheRootFolder, linkFolder: config.linkFolder, }); } diff --git a/src/cli/index.js b/src/cli/index.js index d6f67e3ad5..52d14f95db 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -362,7 +362,7 @@ config.init({ binLinks: commander.binLinks, modulesFolder: commander.modulesFolder, globalFolder: commander.globalFolder, - cacheFolder: commander.cacheFolder, + cacheRootFolder: commander.cacheFolder, preferOffline: commander.preferOffline, captureHar: commander.har, ignorePlatform: commander.ignorePlatform, diff --git a/src/config.js b/src/config.js index bbb49ca18e..339488fa5c 100644 --- a/src/config.js +++ b/src/config.js @@ -21,6 +21,7 @@ const path = require('path'); export type ConfigOptions = { cwd?: ?string, + _cacheRootFolder?: ?string, cacheFolder?: ?string, tempFolder?: ?string, modulesFolder?: ?string, @@ -110,6 +111,9 @@ export default class Config { // modulesFolder: ?string; + // + _cacheRootFolder: string; + // cacheFolder: string; @@ -231,9 +235,14 @@ export default class Config { key: String(opts.key || this.getOption('key') || ''), networkConcurrency: this.networkConcurrency, }); + this._cacheRootFolder = String( + opts.cacheFolder || + this.getOption('cache-folder') || + constants.MODULE_CACHE_DIRECTORY, + ); //init & create cacheFolder, tempFolder - this.cacheFolder = String(opts.cacheFolder || this.getOption('cache-folder') || constants.MODULE_CACHE_DIRECTORY); + this.cacheFolder = path.join(this._cacheRootFolder, 'v' + String(constants.CACHE_VERSION)); this.tempFolder = opts.tempFolder || path.join(this.cacheFolder, '.tmp'); await fs.mkdirp(this.cacheFolder); await fs.mkdirp(this.tempFolder); diff --git a/src/constants.js b/src/constants.js index 48d7854b35..116430109b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -24,6 +24,9 @@ export const SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; export const SELF_UPDATE_TARBALL_URL = 'https://yarnpkg.com/latest.tar.gz'; export const SELF_UPDATE_DOWNLOAD_FOLDER = 'updates'; +// cache version, bump whenever we make backwards incompatible changes +export const CACHE_VERSION = 1; + // lockfile version, bump whenever we make backwards incompatible changes export const LOCKFILE_VERSION = 1;