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 54ef2abfc64f06111e31469c33894c0db673e4d0..48c858717f4c7e1f1a46cd422991efc1e263dbf0 100644 --- a/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap +++ b/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap @@ -112,6 +112,45 @@ Array [ ] `; +exports[`manager/npm/post-update/yarn performs lock file updates and full install using yarn v1.22.0 1`] = ` +Array [ + Object { + "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", + "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", + }, + "timeout": 900000, + }, + }, + Object { + "cmd": "yarn upgrade some-dep --ignore-scripts --ignore-engines --ignore-platform", + "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", + }, + "timeout": 900000, + }, + }, +] +`; + exports[`manager/npm/post-update/yarn performs lock file updates 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 9cf127d643733140938068e0fb571a784659c3f8..05bfa1d34541406b1e39072f7c4c8bb39e6c04fb 100644 --- a/lib/manager/npm/post-update/yarn.spec.ts +++ b/lib/manager/npm/post-update/yarn.spec.ts @@ -35,13 +35,14 @@ describe(getName(__filename), () => { stdout: yarnVersion, stderr: '', }); - fs.readFile = jest.fn(() => 'package-lock-contents') as never; + fs.readFile.mockResolvedValue(null); // .yarnrc + fs.readFile.mockResolvedValue('package-lock-contents' as never); const config = { dockerMapDotfiles: true, postUpdateOptions: ['yarnDedupeFewer', 'yarnDedupeHighest'], }; const res = await yarnHelper.generateLockFile('some-dir', {}, config); - expect(fs.readFile).toHaveBeenCalledTimes(1); + expect(fs.readFile).toHaveBeenCalledTimes(2); expect(res.lockFile).toEqual('package-lock-contents'); expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); } @@ -54,7 +55,30 @@ describe(getName(__filename), () => { stderr: '', }); - fs.readFile = jest.fn(() => 'package-lock-contents') as never; + fs.readFile.mockResolvedValue(null); // .yarnrc + fs.readFile.mockResolvedValue('package-lock-contents' as never); + const res = await yarnHelper.generateLockFile('some-dir', {}, {}, [ + { + depName: 'some-dep', + isLockfileUpdate: true, + }, + ]); + expect(res.lockFile).toEqual('package-lock-contents'); + expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); + } + ); + it.each([['1.22.0']])( + 'performs lock file updates and full install using yarn v%s', + async (yarnVersion) => { + const execSnapshots = mockExecAll(exec, { + stdout: yarnVersion, + stderr: '', + }); + + fs.readFile.mockReturnValueOnce( + 'yarn-offline-mirror ./npm-packages-offline-cache' as never + ); + fs.readFile.mockReturnValueOnce('package-lock-contents' as never); const res = await yarnHelper.generateLockFile('some-dir', {}, {}, [ { depName: 'some-dep', @@ -70,11 +94,12 @@ describe(getName(__filename), () => { stdout: '1.9.4', stderr: 'some-error', }); - fs.readFile = jest.fn(() => { - throw new Error('not found'); - }) as never; + fs.readFile.mockImplementationOnce(() => null); + fs.readFile.mockImplementationOnce(() => { + throw new Error('not-found'); + }); const res = await yarnHelper.generateLockFile('some-dir'); - expect(fs.readFile).toHaveBeenCalledTimes(1); + expect(fs.readFile).toHaveBeenCalledTimes(2); expect(res.error).toBe(true); expect(res.lockFile).not.toBeDefined(); expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts index 04129551b419efe8ee6efb641ee6f29ac009c5ad..e74bc1e40e0dac731d140fae0c0065ccfd042230 100644 --- a/lib/manager/npm/post-update/yarn.ts +++ b/lib/manager/npm/post-update/yarn.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import { readFile } from 'fs-extra'; import { join } from 'upath'; import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages'; @@ -12,6 +13,19 @@ export interface GenerateLockFileResult { stderr?: string; } +export async function hasYarnOfflineMirror(cwd: string): Promise<boolean> { + const yarnrc = await readFile(`${cwd}/.yarnrc`, 'utf8'); + if (is.string(yarnrc)) { + const mirrorLine = yarnrc + .split('\n') + .find((line) => line.startsWith('yarn-offline-mirror ')); + if (mirrorLine) { + return true; + } + } + return false; +} + export async function generateLockFile( cwd: string, env?: NodeJS.ProcessEnv, @@ -22,6 +36,16 @@ export async function generateLockFile( let lockFile = null; try { const preCommands = ['npm i -g yarn']; + if ( + config.skipInstalls !== false && + (await hasYarnOfflineMirror(cwd)) === false + ) { + logger.debug('Updating yarn.lock only - skipping node_modules'); + // The following change causes Yarn 1.x to exit gracefully after updating the lock file but without installing node_modules + preCommands.push( + "sed -i 's/ steps,/ steps.slice(0,1),/' /home/ubuntu/.npm-global/lib/node_modules/yarn/lib/cli.js" + ); + } const commands = []; let cmdOptions = ''; if (global.trustLevel !== 'high' || config.ignoreScripts) {