diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index bb56cd6c44df80d7f1ff0f598801763ab9cbc150..3d5907b0f4e29c46e722b2defa152773add610bc 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -1102,7 +1102,7 @@ If enabled Renovate will pin docker images by means of their sha256 digest and n - `gomodTidy`: Run `go mod tidy` after Go module updates - `npmDedupe`: Run `npm dedupe` after `package-lock.json` updates - `yarnDedupeFewer`: Run `yarn-deduplicate --strategy fewer` after `yarn.lock` updates -- `yarnDedupeHighest`: Run `yarn-deduplicate --strategy highest` after `yarn.lock` updates +- `yarnDedupeHighest`: Run `yarn-deduplicate --strategy highest` (`yarn dedupe --strategy highest` for Yarn >=2.2.0) after `yarn.lock` updates ## postUpgradeTasks diff --git a/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap b/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap index 5351e93ad0117dd1adc44da722c36cdf8aae5f76..9d979942b9f9822b19025c3c7d84a2bb9ad599ce 100644 --- a/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap +++ b/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap @@ -136,6 +136,49 @@ Array [ ] `; +exports[`manager/npm/post-update/yarn generates lock files using yarn v2.2.0 1`] = ` +Array [ + Object { + "cmd": "yarn install", + "options": Object { + "cwd": "some-dir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + "YARN_ENABLE_SCRIPTS": "0", + "YARN_HTTP_TIMEOUT": "100000", + }, + "timeout": 900000, + }, + }, + Object { + "cmd": "yarn dedupe --strategy highest", + "options": Object { + "cwd": "some-dir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + "YARN_ENABLE_SCRIPTS": "0", + "YARN_HTTP_TIMEOUT": "100000", + }, + "timeout": 900000, + }, + }, +] +`; + exports[`manager/npm/post-update/yarn performs lock file maintenance using yarn v1.22.0 1`] = ` Array [ Object { @@ -250,6 +293,49 @@ Array [ ] `; +exports[`manager/npm/post-update/yarn performs lock file maintenance using yarn v2.2.0 1`] = ` +Array [ + Object { + "cmd": "yarn install", + "options": Object { + "cwd": "some-dir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + "YARN_ENABLE_SCRIPTS": "0", + "YARN_HTTP_TIMEOUT": "100000", + }, + "timeout": 900000, + }, + }, + Object { + "cmd": "yarn dedupe --strategy highest", + "options": Object { + "cwd": "some-dir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + "YARN_ENABLE_SCRIPTS": "0", + "YARN_HTTP_TIMEOUT": "100000", + }, + "timeout": 900000, + }, + }, +] +`; + exports[`manager/npm/post-update/yarn performs lock file updates and full install using yarn v1.22.0 1`] = ` Array [ Object { diff --git a/lib/manager/npm/post-update/yarn.spec.ts b/lib/manager/npm/post-update/yarn.spec.ts index ad726a0c985a83264f24299b81e479591525ba99..4d154a37dbc8338497ef157fb0c4ba37a5990701 100644 --- a/lib/manager/npm/post-update/yarn.spec.ts +++ b/lib/manager/npm/post-update/yarn.spec.ts @@ -29,11 +29,12 @@ describe(getName(__filename), () => { env.getChildProcessEnv.mockReturnValue(envMock.basic); }); it.each([ - ['1.22.0', 2], - ['2.1.0', 1], + ['1.22.0', '^1.10.0', 2], + ['2.1.0', '>= 2.0.0', 1], + ['2.2.0', '2.2.0', 1], ])( 'generates lock files using yarn v%s', - async (yarnVersion, expectedFsCalls) => { + async (yarnVersion, yarnCompatibility, expectedFsCalls) => { const execSnapshots = mockExecAll(exec, { stdout: yarnVersion, stderr: '', @@ -49,7 +50,7 @@ describe(getName(__filename), () => { const config = { dockerMapDotfiles: true, compatibility: { - yarn: yarnVersion === '1.22.0' ? '^1.10.0' : '>= 2.0.0', + yarn: yarnCompatibility, }, postUpdateOptions: ['yarnDedupeFewer', 'yarnDedupeHighest'], }; @@ -103,11 +104,12 @@ describe(getName(__filename), () => { } ); it.each([ - ['1.22.0', 2], - ['2.1.0', 1], + ['1.22.0', '^1.10.0', 2], + ['2.1.0', '>= 2.0.0', 1], + ['2.2.0', '2.2.0', 1], ])( 'performs lock file maintenance using yarn v%s', - async (yarnVersion, expectedFsCalls) => { + async (yarnVersion, yarnCompatibility, expectedFsCalls) => { const execSnapshots = mockExecAll(exec, { stdout: yarnVersion, stderr: '', @@ -123,7 +125,7 @@ describe(getName(__filename), () => { const config = { dockerMapDotfiles: true, compatibility: { - yarn: yarnVersion === '1.22.0' ? '^1.10.0' : '>= 2.0.0', + yarn: yarnCompatibility, }, postUpdateOptions: ['yarnDedupeFewer', 'yarnDedupeHighest'], }; diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts index 667ca710e6c137ee942fdcd358c1b03ed2c43e98..3b6adf166abb58dd5be94d0b6724458ea072212d 100644 --- a/lib/manager/npm/post-update/yarn.ts +++ b/lib/manager/npm/post-update/yarn.ts @@ -1,5 +1,5 @@ import is from '@sindresorhus/is'; -import { minVersion, validRange } from 'semver'; +import { gte, minVersion, validRange } from 'semver'; import { quote } from 'shlex'; import { join } from 'upath'; import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages'; @@ -48,12 +48,14 @@ export async function generateLockFile( let lockFile = null; try { const yarnCompatibility = config.compatibility?.yarn; - const isValidYarnRange = validRange(yarnCompatibility); - const isYarn1 = - !isValidYarnRange || minVersion(yarnCompatibility).major === 1; + const minYarnVersion = + validRange(yarnCompatibility) && minVersion(yarnCompatibility); + const isYarn1 = !minYarnVersion || minYarnVersion.major === 1; + const isYarnDedupeAvailable = + minYarnVersion && gte(minYarnVersion, '2.2.0'); let installYarn = 'npm i -g yarn'; - if (isYarn1 && isValidYarnRange) { + if (isYarn1 && minYarnVersion) { installYarn += `@${quote(yarnCompatibility)}`; } @@ -133,11 +135,18 @@ export async function generateLockFile( // Run yarn again in case any changes are necessary commands.push(`yarn install ${cmdOptions}`.trim()); } - if (isYarn1 && config.postUpdateOptions?.includes('yarnDedupeHighest')) { + if ( + (isYarn1 || isYarnDedupeAvailable) && + config.postUpdateOptions?.includes('yarnDedupeHighest') + ) { logger.debug('Performing yarn dedupe highest'); - commands.push('npx yarn-deduplicate --strategy highest'); - // Run yarn again in case any changes are necessary - commands.push(`yarn install ${cmdOptions}`.trim()); + if (isYarn1) { + commands.push('npx yarn-deduplicate --strategy highest'); + // Run yarn again in case any changes are necessary + commands.push(`yarn install ${cmdOptions}`.trim()); + } else { + commands.push('yarn dedupe --strategy highest'); + } } if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) {