From e90cdd6d30977ec76e50f90913019b8a238c0066 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Thu, 4 Aug 2022 16:16:01 +0200 Subject: [PATCH] feat(manager/npm): support buildpack --- .../manager/npm/post-update/lerna.spec.ts | 100 +++++++++++++++++- lib/modules/manager/npm/post-update/lerna.ts | 8 +- .../manager/npm/post-update/npm.spec.ts | 3 + lib/modules/manager/npm/post-update/npm.ts | 13 ++- .../manager/npm/post-update/pnpm.spec.ts | 27 +++-- lib/modules/manager/npm/post-update/pnpm.ts | 14 +-- .../manager/npm/post-update/yarn.spec.ts | 17 ++- lib/modules/manager/npm/post-update/yarn.ts | 13 ++- 8 files changed, 155 insertions(+), 40 deletions(-) diff --git a/lib/modules/manager/npm/post-update/lerna.spec.ts b/lib/modules/manager/npm/post-update/lerna.spec.ts index 8b2277a32265801..64a8711e7398955 100644 --- a/lib/modules/manager/npm/post-update/lerna.spec.ts +++ b/lib/modules/manager/npm/post-update/lerna.spec.ts @@ -1,13 +1,16 @@ import { envMock, mockExecAll } from '../../../../../test/exec-util'; -import { env, partial } from '../../../../../test/util'; +import { env, logger, mockedFunction, partial } from '../../../../../test/util'; import { GlobalConfig } from '../../../../config/global'; import type { RepoGlobalConfig } from '../../../../config/types'; import type { PackageFile, PostUpdateConfig } from '../../types'; import * as lernaHelper from './lerna'; +import { getNodeToolConstraint } from './node-version'; jest.mock('../../../../util/exec/env'); jest.mock('../../npm/post-update/node-version'); +process.env.BUILDPACK = 'true'; + function lernaPkgFile(lernaClient: string): Partial { return { lernaClient, @@ -23,10 +26,18 @@ function lernaPkgFileWithoutLernaDep( }; } +mockedFunction(logger.logger.debug).mockImplementation((...args) => + // eslint-disable-next-line no-console + console.log(...args) +); + const config = partial({}); describe('modules/manager/npm/post-update/lerna', () => { - const globalConfig: RepoGlobalConfig = { localDir: '' }; + const globalConfig: RepoGlobalConfig = { + localDir: '', + cacheDir: '/tmp/cache', + }; describe('generateLockFiles()', () => { beforeEach(() => { @@ -34,6 +45,10 @@ describe('modules/manager/npm/post-update/lerna', () => { jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); GlobalConfig.set(globalConfig); + mockedFunction(getNodeToolConstraint).mockResolvedValueOnce({ + toolName: 'node', + constraint: '16.16.0', + }); }); it('returns if no lernaClient', async () => { @@ -120,6 +135,87 @@ describe('modules/manager/npm/post-update/lerna', () => { expect(res.error).toBeFalse(); expect(execSnapshots).toMatchSnapshot(); }); + + it('suppports docker', async () => { + const execSnapshots = mockExecAll(); + GlobalConfig.set({ ...globalConfig, binarySource: 'docker' }); + const res = await lernaHelper.generateLockFiles( + lernaPkgFile('npm'), + 'some-dir', + { ...config, constraints: { npm: '^6.0.0' } }, + {} + ); + expect(execSnapshots).toMatchObject([ + { + cmd: 'docker pull renovate/sidecar', + }, + { + cmd: 'docker ps --filter name=renovate_sidecar -aq', + }, + { + cmd: + 'docker run --rm --name=renovate_sidecar --label=renovate_child ' + + '-v "/tmp/cache":"/tmp/cache" ' + + '-e BUILDPACK_CACHE_DIR ' + + '-w "some-dir" renovate/sidecar ' + + 'bash -l -c "' + + 'install-tool node 16.16.0 ' + + '&& ' + + "npm i -g npm@'^6.0.0' || true " + + '&& ' + + 'hash -d npm 2>/dev/null || true ' + + '&& npm i -g lerna@2.0.0 ' + + '&& lerna info || echo \\"Ignoring lerna info failure\\" ' + + '&& ' + + 'npm install --ignore-scripts --no-audit --package-lock-only ' + + '&& ' + + 'lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --no-audit --package-lock-only' + + '"', + options: { + cwd: 'some-dir', + }, + }, + ]); + expect(res.error).toBeFalse(); + }); + + it('suppports binarySource=install', async () => { + const execSnapshots = mockExecAll(); + GlobalConfig.set({ ...globalConfig, binarySource: 'install' }); + const res = await lernaHelper.generateLockFiles( + lernaPkgFile('npm'), + 'some-dir', + { ...config, constraints: { npm: '^6.0.0' } }, + {} + ); + expect(res.error).toBeFalse(); + expect(execSnapshots).toMatchObject([ + { + cmd: 'install-tool node 16.16.0', + options: { + cwd: 'some-dir', + }, + }, + { + cmd: 'lerna info || echo "Ignoring lerna info failure"', + options: { + cwd: 'some-dir', + }, + }, + { + cmd: 'npm install --ignore-scripts --no-audit --package-lock-only', + options: { + cwd: 'some-dir', + }, + }, + { + cmd: 'lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --no-audit --package-lock-only', + options: { + cwd: 'some-dir', + }, + }, + ]); + }); }); describe('getLernaVersion()', () => { diff --git a/lib/modules/manager/npm/post-update/lerna.ts b/lib/modules/manager/npm/post-update/lerna.ts index 22d9a8717fb7651..bfe95cba8dbfd83 100644 --- a/lib/modules/manager/npm/post-update/lerna.ts +++ b/lib/modules/manager/npm/post-update/lerna.ts @@ -7,7 +7,7 @@ import { logger } from '../../../../logger'; import { exec } from '../../../../util/exec'; import type { ExecOptions, ExtraEnv } from '../../../../util/exec/types'; import type { PackageFile, PostUpdateConfig } from '../../types'; -import { getNodeConstraint } from './node-version'; +import { getNodeToolConstraint } from './node-version'; import type { GenerateLockFileResult } from './types'; import { getOptimizeCommand } from './yarn'; @@ -77,7 +77,6 @@ export async function generateLockFiles( lernaCommand = lernaCommand.replace('--ignore-scripts ', ''); } lernaCommand += cmdOptions; - const tagConstraint = await getNodeConstraint(config); const extraEnv: ExtraEnv = { NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE, npm_config_store: env.npm_config_store, @@ -86,11 +85,10 @@ export async function generateLockFiles( cwdFile: upath.join(lockFileDir, 'package.json'), extraEnv, docker: { - image: 'node', - tagScheme: 'node', - tagConstraint, + image: 'sidecar', }, preCommands, + toolConstraints: [await getNodeToolConstraint(config, [])], }; // istanbul ignore if if (GlobalConfig.get('exposeAllEnv')) { diff --git a/lib/modules/manager/npm/post-update/npm.spec.ts b/lib/modules/manager/npm/post-update/npm.spec.ts index 6bdb012a7b115f2..796566d2d08ad8e 100644 --- a/lib/modules/manager/npm/post-update/npm.spec.ts +++ b/lib/modules/manager/npm/post-update/npm.spec.ts @@ -268,6 +268,8 @@ describe('modules/manager/npm/post-update/npm', () => { '-w "some-dir" ' + 'renovate/node ' + 'bash -l -c "' + + 'install-tool node 16.16.0 ' + + '&& ' + 'install-tool npm 6.0.0 ' + '&& ' + 'hash -d npm 2>/dev/null || true ' + @@ -296,6 +298,7 @@ describe('modules/manager/npm/post-update/npm', () => { expect(fs.readLocalFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchObject([ + { cmd: 'install-tool node 16.16.0' }, { cmd: 'install-tool npm 6.0.0' }, { cmd: 'hash -d npm 2>/dev/null || true' }, { diff --git a/lib/modules/manager/npm/post-update/npm.ts b/lib/modules/manager/npm/post-update/npm.ts index b757111cdec4ea6..7cf39a57ea309c7 100644 --- a/lib/modules/manager/npm/post-update/npm.ts +++ b/lib/modules/manager/npm/post-update/npm.ts @@ -19,7 +19,7 @@ import { } from '../../../../util/fs'; import type { PostUpdateConfig, Upgrade } from '../../types'; import { composeLockFile, parseLockFile } from '../utils'; -import { getNodeConstraint, getNodeUpdate } from './node-version'; +import { getNodeToolConstraint } from './node-version'; import type { GenerateLockFileResult } from './types'; export async function generateLockFile( @@ -55,8 +55,6 @@ export async function generateLockFile( cmdOptions += ' --ignore-scripts'; } - const tagConstraint = - getNodeUpdate(upgrades) ?? (await getNodeConstraint(config)); const extraEnv: ExtraEnv = { NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE, npm_config_store: env.npm_config_store, @@ -64,11 +62,12 @@ export async function generateLockFile( const execOptions: ExecOptions = { cwdFile: lockFileName, extraEnv, - toolConstraints: [npmToolConstraint], + toolConstraints: [ + await getNodeToolConstraint(config, upgrades), + npmToolConstraint, + ], docker: { - image: 'node', - tagScheme: 'node', - tagConstraint, + image: 'sidecar', }, }; // istanbul ignore if diff --git a/lib/modules/manager/npm/post-update/pnpm.spec.ts b/lib/modules/manager/npm/post-update/pnpm.spec.ts index c0ee55d699d708a..2752998b36d6e4b 100644 --- a/lib/modules/manager/npm/post-update/pnpm.spec.ts +++ b/lib/modules/manager/npm/post-update/pnpm.spec.ts @@ -1,8 +1,14 @@ import { envMock, mockExecAll } from '../../../../../test/exec-util'; import { Fixtures } from '../../../../../test/fixtures'; -import { env, fs, partial } from '../../../../../test/util'; +import { + env, + fs, + mockedFunction, + partial, +} from '../../../../../test/util'; import { GlobalConfig } from '../../../../config/global'; import type { PostUpdateConfig } from '../../types'; +import { getNodeToolConstraint } from './node-version'; import * as pnpmHelper from './pnpm'; jest.mock('../../../../util/exec/env'); @@ -20,6 +26,10 @@ describe('modules/manager/npm/post-update/pnpm', () => { config = partial({ constraints: { pnpm: '^2.0.0' } }); env.getChildProcessEnv.mockReturnValue(envMock.basic); GlobalConfig.set({ localDir: '' }); + mockedFunction(getNodeToolConstraint).mockResolvedValueOnce({ + toolName: 'node', + constraint: '16.16.0', + }); }); it('generates lock files', async () => { @@ -197,19 +207,19 @@ describe('modules/manager/npm/post-update/pnpm', () => { expect(fs.readLocalFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchObject([ - { cmd: 'docker pull renovate/node' }, - { cmd: 'docker ps --filter name=renovate_node -aq' }, + { cmd: 'docker pull renovate/sidecar' }, + { cmd: 'docker ps --filter name=renovate_sidecar -aq' }, { cmd: - 'docker run --rm --name=renovate_node --label=renovate_child ' + + 'docker run --rm --name=renovate_sidecar --label=renovate_child ' + '-v "/tmp":"/tmp" ' + '-e BUILDPACK_CACHE_DIR ' + '-w "some-dir" ' + - 'renovate/node ' + + 'renovate/sidecar ' + 'bash -l -c "' + - 'install-tool pnpm 6.0.0 ' + - '&& ' + - 'pnpm install --recursive --lockfile-only' + + 'install-tool node 16.16.0 ' + + '&& install-tool pnpm 6.0.0 ' + + '&& pnpm install --recursive --lockfile-only' + '"', }, ]); @@ -231,6 +241,7 @@ describe('modules/manager/npm/post-update/pnpm', () => { expect(fs.readLocalFile).toHaveBeenCalledTimes(1); expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchObject([ + { cmd: 'install-tool node 16.16.0' }, { cmd: 'install-tool pnpm 6.0.0' }, { cmd: 'pnpm install --recursive --lockfile-only --ignore-scripts --ignore-pnpmfile', diff --git a/lib/modules/manager/npm/post-update/pnpm.ts b/lib/modules/manager/npm/post-update/pnpm.ts index ae23037cb5618ca..ac29a8e505f61d6 100644 --- a/lib/modules/manager/npm/post-update/pnpm.ts +++ b/lib/modules/manager/npm/post-update/pnpm.ts @@ -13,7 +13,7 @@ import type { import { deleteLocalFile, readLocalFile } from '../../../../util/fs'; import type { PostUpdateConfig, Upgrade } from '../../types'; import type { NpmPackage } from '../extract/types'; -import { getNodeConstraint, getNodeUpdate } from './node-version'; +import { getNodeToolConstraint } from './node-version'; import type { GenerateLockFileResult, PnpmLockFile } from './types'; export async function generateLockFile( @@ -34,8 +34,7 @@ export async function generateLockFile( constraint: config.constraints?.pnpm ?? (await getPnpmConstraint(lockFileDir)), }; - const tagConstraint = - getNodeUpdate(upgrades) ?? (await getNodeConstraint(config)); + const extraEnv: ExtraEnv = { NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE, npm_config_store: env.npm_config_store, @@ -44,11 +43,12 @@ export async function generateLockFile( cwdFile: lockFileName, extraEnv, docker: { - image: 'node', - tagScheme: 'node', - tagConstraint, + image: 'sidecar', }, - toolConstraints: [pnpmToolConstraint], + toolConstraints: [ + await getNodeToolConstraint(config, upgrades), + pnpmToolConstraint, + ], }; // istanbul ignore if if (GlobalConfig.get('exposeAllEnv')) { diff --git a/lib/modules/manager/npm/post-update/yarn.spec.ts b/lib/modules/manager/npm/post-update/yarn.spec.ts index 3798b4e2531a9cd..69638a7ac306adc 100644 --- a/lib/modules/manager/npm/post-update/yarn.spec.ts +++ b/lib/modules/manager/npm/post-update/yarn.spec.ts @@ -12,6 +12,7 @@ import * as docker from '../../../../util/exec/docker'; import { getPkgReleases } from '../../../datasource'; import type { PostUpdateConfig } from '../../types'; import type { NpmManagerData } from '../types'; +import { getNodeToolContraint } from './node-version'; import * as yarnHelper from './yarn'; jest.mock('fs-extra', () => @@ -41,8 +42,12 @@ describe('modules/manager/npm/post-update/yarn', () => { delete process.env.BUILDPACK; jest.clearAllMocks(); Fixtures.reset(); - docker.resetPrefetchedImages(); GlobalConfig.set({ localDir: '.', cacheDir: '/tmp/cache' }); + docker.resetPrefetchedImages(); + mockedFunction(getNodeToolContraint).mockResolvedValueOnce({ + toolName: 'node', + constraint: '16.16.0', + }); }); it.each([ @@ -380,8 +385,8 @@ describe('modules/manager/npm/post-update/yarn', () => { }, }); const res = await yarnHelper.generateLockFile('some-dir', {}, config); - expect(res.lockFile).toBe('package-lock-contents'); expect(execSnapshots).toMatchObject([ + { cmd: 'install-tool node 16.16.0', options: { cwd: 'some-dir' } }, { cmd: 'install-tool corepack 0.10.0', options: { cwd: 'some-dir' } }, { cmd: 'yarn install --mode=update-lockfile', @@ -395,6 +400,7 @@ describe('modules/manager/npm/post-update/yarn', () => { }, }, ]); + expect(res.lockFile).toBe('package-lock-contents'); }); it('uses slim yarn instead of corepack', async () => { @@ -427,6 +433,7 @@ describe('modules/manager/npm/post-update/yarn', () => { const res = await yarnHelper.generateLockFile('some-dir', {}, config); expect(res.lockFile).toBe(plocktest1YarnLockV1); expect(execSnapshots).toMatchObject([ + { cmd: 'install-tool node 16.16.0', options: { cwd: 'some-dir' } }, { cmd: 'install-tool yarn-slim 1.22.18', options: { cwd: 'some-dir' } }, { cmd: 'yarn install --ignore-engines --ignore-platform --network-timeout 100000 --ignore-scripts', @@ -500,11 +507,13 @@ describe('modules/manager/npm/post-update/yarn', () => { expect(res.lockFile).toBe(plocktest1YarnLockV1); const options = { encoding: 'utf-8' }; expect(execSnapshots).toMatchObject([ - { cmd: 'docker pull renovate/node', options }, + { cmd: 'docker pull renovate/sidecar', options }, { cmd: - `docker run --rm --name=renovate_node --label=renovate_child -v ".":"." -v "/tmp/cache":"/tmp/cache" -e CI -e BUILDPACK_CACHE_DIR -w "some-dir" renovate/node ` + + `docker run --rm --name=renovate_sidecar --label=renovate_child -v ".":"." -v "/tmp/cache":"/tmp/cache" -e CI -e BUILDPACK_CACHE_DIR -w "some-dir" renovate/sidecar ` + `bash -l -c "` + + `install-tool node 16.16.0` + + ` && ` + `install-tool yarn-slim 1.22.18` + ` && ` + `sed -i 's/ steps,/ steps.slice(0,1),/' some-dir/.yarn/cli.js || true` + diff --git a/lib/modules/manager/npm/post-update/yarn.ts b/lib/modules/manager/npm/post-update/yarn.ts index d1036e9109f298d..097c09ef8c73c30 100644 --- a/lib/modules/manager/npm/post-update/yarn.ts +++ b/lib/modules/manager/npm/post-update/yarn.ts @@ -25,7 +25,7 @@ import { uniqueStrings } from '../../../../util/string'; import { NpmDatasource } from '../../../datasource/npm'; import type { PostUpdateConfig, Upgrade } from '../../types'; import type { NpmManagerData } from '../types'; -import { getNodeConstraint, getNodeUpdate } from './node-version'; +import { getNodeToolConstraint } from './node-version'; import type { GenerateLockFileResult } from './types'; export async function checkYarnrc( @@ -94,7 +94,9 @@ export async function generateLockFile( logger.debug(`Spawning yarn install to create ${lockFileName}`); let lockFile: string | null = null; try { - const toolConstraints: ToolConstraint[] = []; + const toolConstraints: ToolConstraint[] = [ + await getNodeToolConstraint(config, upgrades), + ]; const yarnUpdate = upgrades.find(isYarnUpdate); const yarnCompatibility = yarnUpdate ? yarnUpdate.newValue @@ -174,15 +176,12 @@ export async function generateLockFile( extraEnv.YARN_ENABLE_SCRIPTS = '0'; } } - const tagConstraint = - getNodeUpdate(upgrades) ?? (await getNodeConstraint(config)); + const execOptions: ExecOptions = { cwdFile: lockFileName, extraEnv, docker: { - image: 'node', - tagScheme: 'node', - tagConstraint, + image: 'sidecar', }, preCommands, toolConstraints,