diff --git a/lib/workers/branch/lock-files/__snapshots__/yarn.spec.ts.snap b/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap similarity index 72% rename from lib/workers/branch/lock-files/__snapshots__/yarn.spec.ts.snap rename to lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap index 34243ee43e4674850c8a5ec56bf3d94cdfd6ea53..54ef2abfc64f06111e31469c33894c0db673e4d0 100644 --- a/lib/workers/branch/lock-files/__snapshots__/yarn.spec.ts.snap +++ b/lib/manager/npm/post-update/__snapshots__/yarn.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`workers/branch/lock-files/yarn catches errors 1`] = ` +exports[`manager/npm/post-update/yarn catches errors 1`] = ` Array [ Object { "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", @@ -22,27 +22,10 @@ Array [ ] `; -exports[`workers/branch/lock-files/yarn docker using yarn v1.0.0 1`] = ` +exports[`manager/npm/post-update/yarn generates lock files using yarn v1.22.0 1`] = ` Array [ Object { - "cmd": "docker run --rm -v \\"some-dir\\":\\"some-dir\\" -v \\"dummy/dir\\":\\"dummy/dir\\" -e NPM_CONFIG_CACHE -e npm_config_store -w \\"some-dir\\" renovate/yarn 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": "docker run --rm -v \\"some-dir\\":\\"some-dir\\" -v \\"dummy/dir\\":\\"dummy/dir\\" -e NPM_CONFIG_CACHE -e npm_config_store -w \\"some-dir\\" renovate/yarn yarn upgrade some-dep --ignore-scripts --ignore-engines --ignore-platform", + "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", "options": Object { "cwd": "some-dir", "encoding": "utf-8", @@ -58,13 +41,8 @@ Array [ "timeout": 900000, }, }, -] -`; - -exports[`workers/branch/lock-files/yarn finds yarn v1.0.0 globally 1`] = ` -Array [ Object { - "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", + "cmd": "npx yarn-deduplicate --strategy fewer", "options": Object { "cwd": "some-dir", "encoding": "utf-8", @@ -80,11 +58,6 @@ Array [ "timeout": 900000, }, }, -] -`; - -exports[`workers/branch/lock-files/yarn generates lock files using yarn v1.0.0 1`] = ` -Array [ Object { "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", "options": Object { @@ -103,7 +76,7 @@ Array [ }, }, Object { - "cmd": "npx yarn-deduplicate --strategy fewer && yarn", + "cmd": "npx yarn-deduplicate --strategy highest", "options": Object { "cwd": "some-dir", "encoding": "utf-8", @@ -120,7 +93,7 @@ Array [ }, }, Object { - "cmd": "npx yarn-deduplicate --strategy highest && yarn", + "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", "options": Object { "cwd": "some-dir", "encoding": "utf-8", @@ -139,7 +112,7 @@ Array [ ] `; -exports[`workers/branch/lock-files/yarn performs lock file updates using yarn v1.0.0 1`] = ` +exports[`manager/npm/post-update/yarn performs lock file updates using yarn v1.22.0 1`] = ` Array [ Object { "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", diff --git a/lib/workers/branch/lock-files/yarn.spec.ts b/lib/manager/npm/post-update/yarn.spec.ts similarity index 64% rename from lib/workers/branch/lock-files/yarn.spec.ts rename to lib/manager/npm/post-update/yarn.spec.ts index 23055eb40de05f56cd281b0151077956e4dc5808..9cf127d643733140938068e0fb571a784659c3f8 100644 --- a/lib/workers/branch/lock-files/yarn.spec.ts +++ b/lib/manager/npm/post-update/yarn.spec.ts @@ -2,9 +2,8 @@ import { exec as _exec } from 'child_process'; import _fs from 'fs-extra'; import { ExecSnapshots, envMock, mockExecAll } from '../../../../test/execUtil'; import { getName, mocked } from '../../../../test/util'; -import * as _yarnHelper from '../../../manager/npm/post-update/yarn'; -import { BinarySource } from '../../../util/exec/common'; import * as _env from '../../../util/exec/env'; +import * as _yarnHelper from './yarn'; jest.mock('fs-extra'); jest.mock('child_process'); @@ -15,6 +14,8 @@ const env = mocked(_env); const fs = mocked(_fs); const yarnHelper = mocked(_yarnHelper); +delete process.env.NPM_CONFIG_CACHE; + // TODO: figure out snapshot similarity for each CI platform const fixSnapshots = (snapshots: ExecSnapshots): ExecSnapshots => snapshots.map((snapshot) => ({ @@ -27,7 +28,7 @@ describe(getName(__filename), () => { jest.resetAllMocks(); env.getChildProcessEnv.mockReturnValue(envMock.basic); }); - it.each([['1.0.0']])( + it.each([['1.22.0']])( 'generates lock files using yarn v%s', async (yarnVersion) => { const execSnapshots = mockExecAll(exec, { @@ -36,6 +37,7 @@ describe(getName(__filename), () => { }); fs.readFile = jest.fn(() => 'package-lock-contents') as never; const config = { + dockerMapDotfiles: true, postUpdateOptions: ['yarnDedupeFewer', 'yarnDedupeHighest'], }; const res = await yarnHelper.generateLockFile('some-dir', {}, config); @@ -44,7 +46,7 @@ describe(getName(__filename), () => { expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); } ); - it.each([['1.0.0']])( + it.each([['1.22.0']])( 'performs lock file updates using yarn v%s', async (yarnVersion) => { const execSnapshots = mockExecAll(exec, { @@ -54,30 +56,15 @@ describe(getName(__filename), () => { fs.readFile = jest.fn(() => 'package-lock-contents') as never; const res = await yarnHelper.generateLockFile('some-dir', {}, {}, [ - { depName: 'some-dep', isLockfileUpdate: true }, + { + depName: 'some-dep', + isLockfileUpdate: true, + }, ]); expect(res.lockFile).toEqual('package-lock-contents'); expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); } ); - it.each([['1.0.0']])('docker using yarn v%s', async (yarnVersion) => { - const execSnapshots = mockExecAll(exec, { - stdout: yarnVersion, - stderr: '', - }); - - fs.readFile = jest.fn(() => 'package-lock-contents') as never; - const config = { - upgrades: [{}], - binarySource: BinarySource.Docker, - cacheDir: 'dummy/dir', - }; - const res = await yarnHelper.generateLockFile('some-dir', {}, config, [ - { depName: 'some-dep', isLockfileUpdate: true }, - ]); - expect(res.lockFile).toEqual('package-lock-contents'); - expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); - }); it('catches errors', async () => { const execSnapshots = mockExecAll(exec, { stdout: '1.9.4', @@ -92,17 +79,4 @@ describe(getName(__filename), () => { expect(res.lockFile).not.toBeDefined(); expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); }); - - it.each([['1.0.0']])('finds yarn v%s globally', async (yarnVersion) => { - const execSnapshots = mockExecAll(exec, { - stdout: yarnVersion, - stderr: '', - }); - - fs.readFile = jest.fn(() => 'package-lock-contents') as never; - const res = await yarnHelper.generateLockFile('some-dir'); - expect(fs.readFile).toHaveBeenCalledTimes(1); - expect(res.lockFile).toEqual('package-lock-contents'); - expect(fixSnapshots(execSnapshots)).toMatchSnapshot(); - }); }); diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts index 036b8049ee0fade8098c2e84520493c7e9da35a8..04129551b419efe8ee6efb641ee6f29ac009c5ad 100644 --- a/lib/manager/npm/post-update/yarn.ts +++ b/lib/manager/npm/post-update/yarn.ts @@ -3,8 +3,7 @@ import { join } from 'upath'; import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../constants/error-messages'; import { DatasourceError } from '../../../datasource'; import { logger } from '../../../logger'; -import { exec } from '../../../util/exec'; -import { BinarySource } from '../../../util/exec/common'; +import { ExecOptions, exec } from '../../../util/exec'; import { PostUpdateConfig, Upgrade } from '../../common'; export interface GenerateLockFileResult { @@ -21,101 +20,68 @@ export async function generateLockFile( ): Promise<GenerateLockFileResult> { logger.debug(`Spawning yarn install to create ${cwd}/yarn.lock`); let lockFile = null; - let stdout = ''; - let stderr = ''; - let cmd = 'yarn'; try { - if (config.binarySource === BinarySource.Docker) { - logger.debug('Running yarn via docker'); - cmd = `docker run --rm `; - // istanbul ignore if - if (config.dockerUser) { - cmd += `--user=${config.dockerUser} `; - } - const volumes = [cwd]; - if (config.cacheDir) { - volumes.push(config.cacheDir); - } - cmd += volumes.map((v) => `-v "${v}":"${v}" `).join(''); - // istanbul ignore if - if (config.dockerMapDotfiles) { - const homeDir = - process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; - const homeNpmrc = join(homeDir, '.npmrc'); - cmd += `-v ${homeNpmrc}:/home/ubuntu/.npmrc `; - } - const envVars = ['NPM_CONFIG_CACHE', 'npm_config_store']; - cmd += envVars.map((e) => `-e ${e} `).join(''); - cmd += `-w "${cwd}" `; - cmd += `renovate/yarn yarn`; + const preCommands = ['npm i -g yarn']; + const commands = []; + let cmdOptions = ''; + if (global.trustLevel !== 'high' || config.ignoreScripts) { + cmdOptions = '--ignore-scripts --ignore-engines --ignore-platform'; } - - let cmdExtras = ''; - const cmdEnv = { ...env }; - cmdExtras += ' --ignore-scripts'; - cmdExtras += ' --ignore-engines'; - cmdExtras += ' --ignore-platform'; - const installCmd = cmd + ' install' + cmdExtras; - // TODO: Switch to native util.promisify once using only node 8 - await exec(installCmd, { + const execOptions: ExecOptions = { cwd, - env: cmdEnv, - }); + extraEnv: { + NPM_CONFIG_CACHE: env?.NPM_CONFIG_CACHE, + npm_config_store: env?.npm_config_store, + }, + docker: { + image: 'renovate/node', + preCommands, + }, + }; + if (config.dockerMapDotfiles) { + const homeDir = + process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; + const homeNpmrc = join(homeDir, '.npmrc'); + execOptions.docker.volumes = [[homeNpmrc, '/home/ubuntu/.npmrc']]; + } + + // This command updates the lock file based on package.json + commands.push(`yarn install ${cmdOptions}`.trim()); + + // rangeStrategy = update-lockfile const lockUpdates = upgrades .filter((upgrade) => upgrade.isLockfileUpdate) .map((upgrade) => upgrade.depName); if (lockUpdates.length) { logger.debug('Performing lockfileUpdate (yarn)'); - const updateCmd = - cmd + - ' upgrade' + - lockUpdates.map((depName) => ` ${depName}`).join('') + - cmdExtras; - const updateRes = await exec(updateCmd, { - cwd, - env, - }); - // istanbul ignore next - stdout += updateRes.stdout || ''; - stderr += updateRes.stderr || ''; + commands.push( + `yarn upgrade ${lockUpdates.join(' ')} ${cmdOptions}`.trim() + ); } - if ( - config.postUpdateOptions && - config.postUpdateOptions.includes('yarnDedupeFewer') - ) { + // postUpdateOptions + if (config.postUpdateOptions?.includes('yarnDedupeFewer')) { logger.debug('Performing yarn dedupe fewer'); - const dedupeCommand = 'npx yarn-deduplicate --strategy fewer && yarn'; - const dedupeRes = await exec(dedupeCommand, { - cwd, - env, - }); - // istanbul ignore next - stdout += dedupeRes.stdout || ''; - stderr += dedupeRes.stderr || ''; + commands.push('npx yarn-deduplicate --strategy fewer'); + // Run yarn again in case any changes are necessary + commands.push(`yarn install ${cmdOptions}`.trim()); } - if ( - config.postUpdateOptions && - config.postUpdateOptions.includes('yarnDedupeHighest') - ) { + if (config.postUpdateOptions?.includes('yarnDedupeHighest')) { logger.debug('Performing yarn dedupe highest'); - const dedupeCommand = 'npx yarn-deduplicate --strategy highest && yarn'; - const dedupeRes = await exec(dedupeCommand, { - cwd, - env, - }); - // istanbul ignore next - stdout += dedupeRes.stdout || ''; - stderr += dedupeRes.stderr || ''; + commands.push('npx yarn-deduplicate --strategy highest'); + // Run yarn again in case any changes are necessary + commands.push(`yarn install ${cmdOptions}`.trim()); } + + // Run the commands + await exec(commands, execOptions); + + // Read the result lockFile = await readFile(join(cwd, 'yarn.lock'), 'utf8'); } catch (err) /* istanbul ignore next */ { logger.debug( { - cmd, err, - stdout, - stderr, type: 'yarn', }, 'lock file error'