From 0149e76f5d573a4a62ed11943d310605e952214c Mon Sep 17 00:00:00 2001 From: Michael Kriese <michael.kriese@visualon.de> Date: Mon, 15 Mar 2021 08:49:56 +0100 Subject: [PATCH] feat(mix): migrate to modern docker handling (#9132) --- .../mix/__snapshots__/artifacts.spec.ts.snap | 33 ++++++-- lib/manager/mix/artifacts.spec.ts | 82 ++++++++++--------- lib/manager/mix/artifacts.ts | 34 ++------ 3 files changed, 82 insertions(+), 67 deletions(-) diff --git a/lib/manager/mix/__snapshots__/artifacts.spec.ts.snap b/lib/manager/mix/__snapshots__/artifacts.spec.ts.snap index d4cade50af..8606c3a66f 100644 --- a/lib/manager/mix/__snapshots__/artifacts.spec.ts.snap +++ b/lib/manager/mix/__snapshots__/artifacts.spec.ts.snap @@ -1,6 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`.updateArtifacts() catches errors 1`] = ` +exports[`manager/mix/artifacts catches exec errors 1`] = ` +Array [ + Object { + "artifactError": Object { + "lockFile": "mix.lock", + "stderr": "exec-error", + }, + }, +] +`; + +exports[`manager/mix/artifacts catches write errors 1`] = ` Array [ Object { "artifactError": Object { @@ -11,7 +22,7 @@ Array [ ] `; -exports[`.updateArtifacts() returns null if unchanged 1`] = ` +exports[`manager/mix/artifacts returns null if unchanged 1`] = ` Array [ Object { "cmd": "mix deps.update plug", @@ -34,7 +45,7 @@ Array [ ] `; -exports[`.updateArtifacts() returns updated mix.lock 1`] = ` +exports[`manager/mix/artifacts returns updated mix.lock 1`] = ` Array [ Object { "file": Object { @@ -45,10 +56,22 @@ Array [ ] `; -exports[`.updateArtifacts() returns updated mix.lock 2`] = ` +exports[`manager/mix/artifacts returns updated mix.lock 2`] = ` Array [ Object { - "cmd": "docker run --rm -v /tmp/github/some/repo:/tmp/github/some/repo -w /tmp/github/some/repo renovate/elixir mix deps.update plug", + "cmd": "docker pull renovate/elixir", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker ps --filter name=renovate_elixir -aq", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker run --rm --name=renovate_elixir --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -w \\"/tmp/github/some/repo\\" renovate/elixir bash -l -c \\"mix deps.update plug\\"", "options": Object { "cwd": "/tmp/github/some/repo", "encoding": "utf-8", diff --git a/lib/manager/mix/artifacts.spec.ts b/lib/manager/mix/artifacts.spec.ts index fed8cefc60..e1056f0938 100644 --- a/lib/manager/mix/artifacts.spec.ts +++ b/lib/manager/mix/artifacts.spec.ts @@ -1,30 +1,29 @@ -import { exec as _exec } from 'child_process'; -import _fs from 'fs-extra'; -import { envMock, mockExecAll } from '../../../test/exec-util'; -import { mocked } from '../../../test/util'; +import { join } from 'upath'; +import { envMock, exec, mockExecAll } from '../../../test/exec-util'; +import { env, fs, getName } from '../../../test/util'; +import { setExecConfig } from '../../util/exec'; import { BinarySource } from '../../util/exec/common'; -import * as _env from '../../util/exec/env'; +import * as docker from '../../util/exec/docker'; import { updateArtifacts } from '.'; -const fs: jest.Mocked<typeof _fs> = _fs as any; -const exec: jest.Mock<typeof _exec> = _exec as any; -const env = mocked(_env); - -jest.mock('fs-extra'); jest.mock('child_process'); jest.mock('../../util/exec/env'); +jest.mock('../../util/fs'); const config = { - localDir: '/tmp/github/some/repo', + // `join` fixes Windows CI + localDir: join('/tmp/github/some/repo'), }; -describe('.updateArtifacts()', () => { - beforeEach(() => { +describe(getName(__filename), () => { + beforeEach(async () => { jest.resetAllMocks(); jest.resetModules(); env.getChildProcessEnv.mockReturnValue(envMock.basic); + await setExecConfig(config); }); + it('returns null if no mix.lock found', async () => { expect( await updateArtifacts({ @@ -35,6 +34,7 @@ describe('.updateArtifacts()', () => { }) ).toBeNull(); }); + it('returns null if no updatedDeps were provided', async () => { expect( await updateArtifacts({ @@ -45,19 +45,7 @@ describe('.updateArtifacts()', () => { }) ).toBeNull(); }); - it('returns null if no local directory found', async () => { - const noLocalDirConfig = { - localDir: null, - }; - expect( - await updateArtifacts({ - packageFileName: 'mix.exs', - updatedDeps: ['plug'], - newPackageFileContent: '', - config: noLocalDirConfig, - }) - ).toBeNull(); - }); + it('returns null if updatedDeps is empty', async () => { expect( await updateArtifacts({ @@ -68,10 +56,11 @@ describe('.updateArtifacts()', () => { }) ).toBeNull(); }); + it('returns null if unchanged', async () => { - fs.readFile.mockResolvedValueOnce('Current mix.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('Current mix.lock'); const execSnapshots = mockExecAll(exec); - fs.readFile.mockResolvedValueOnce('Current mix.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('Current mix.lock'); expect( await updateArtifacts({ packageFileName: 'mix.exs', @@ -82,26 +71,30 @@ describe('.updateArtifacts()', () => { ).toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('returns updated mix.lock', async () => { - fs.readFile.mockResolvedValueOnce('Old mix.lock' as any); + jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce(); + await setExecConfig({ + ...config, + binarySource: BinarySource.Docker, + }); + fs.readLocalFile.mockResolvedValueOnce('Old mix.lock'); const execSnapshots = mockExecAll(exec); - fs.readFile.mockResolvedValueOnce('New mix.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('New mix.lock'); expect( await updateArtifacts({ packageFileName: 'mix.exs', updatedDeps: ['plug'], newPackageFileContent: '{}', - config: { - ...config, - binarySource: BinarySource.Docker, - }, + config, }) ).toMatchSnapshot(); expect(execSnapshots).toMatchSnapshot(); }); - it('catches errors', async () => { - fs.readFile.mockResolvedValueOnce('Current mix.lock' as any); - fs.outputFile.mockImplementationOnce(() => { + + it('catches write errors', async () => { + fs.readLocalFile.mockResolvedValueOnce('Current mix.lock'); + fs.writeLocalFile.mockImplementationOnce(() => { throw new Error('not found'); }); expect( @@ -113,4 +106,19 @@ describe('.updateArtifacts()', () => { }) ).toMatchSnapshot(); }); + + it('catches exec errors', async () => { + fs.readLocalFile.mockResolvedValueOnce('Current mix.lock'); + exec.mockImplementationOnce(() => { + throw new Error('exec-error'); + }); + expect( + await updateArtifacts({ + packageFileName: 'mix.exs', + updatedDeps: ['plug'], + newPackageFileContent: '{}', + config, + }) + ).toMatchSnapshot(); + }); }); diff --git a/lib/manager/mix/artifacts.ts b/lib/manager/mix/artifacts.ts index 06bcc56ca2..118730bec3 100644 --- a/lib/manager/mix/artifacts.ts +++ b/lib/manager/mix/artifacts.ts @@ -1,8 +1,7 @@ import { quote } from 'shlex'; import { TEMPORARY_ERROR } from '../../constants/error-messages'; import { logger } from '../../logger'; -import { exec } from '../../util/exec'; -import { BinarySource } from '../../util/exec/common'; +import { ExecOptions, exec } from '../../util/exec'; import { readLocalFile, writeLocalFile } from '../../util/fs'; import type { UpdateArtifact, UpdateArtifactsResult } from '../types'; @@ -10,7 +9,6 @@ export async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, - config, }: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> { logger.debug(`mix.getArtifacts(${packageFileName})`); if (updatedDeps.length < 1) { @@ -18,12 +16,6 @@ export async function updateArtifacts({ return null; } - const cwd = config.localDir; - if (!cwd) { - logger.debug('No local dir specified'); - return null; - } - const lockFileName = 'mix.lock'; try { await writeLocalFile(packageFileName, newPackageFileContent); @@ -45,30 +37,22 @@ export async function updateArtifacts({ return null; } - const cmdParts = - config.binarySource === BinarySource.Docker - ? [ - 'docker', - 'run', - '--rm', - `-v ${cwd}:${cwd}`, - `-w ${cwd}`, - 'renovate/elixir mix', - ] - : ['mix']; - cmdParts.push('deps.update'); + const execOptions: ExecOptions = { + cwdFile: packageFileName, + docker: { image: 'renovate/elixir' }, + }; + const command = ['mix', 'deps.update', ...updatedDeps.map(quote)].join(' '); - /* istanbul ignore next */ try { - const command = [...cmdParts, ...updatedDeps.map(quote)].join(' '); - await exec(command, { cwd }); + await exec(command, execOptions); } catch (err) { // istanbul ignore if if (err.message === TEMPORARY_ERROR) { throw err; } + logger.warn( - { err, message: err.message }, + { err, message: err.message, command }, 'Failed to update Mix lock file' ); -- GitLab