diff --git a/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap b/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap index 52534d46a332fbe9ccc13aa5931cf60af70f9e47..749d49e4010a6055d4fff2b7b288adf0c62b23fc 100644 --- a/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap +++ b/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap @@ -181,6 +181,28 @@ Array [ ] `; +exports[`bundler.updateArtifacts() performs lockFileMaintenance 1`] = ` +Array [ + Object { + "cmd": "bundle lock", + "options": Object { + "cwd": "/tmp/github/some/repo", + "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[`bundler.updateArtifacts() returns null if Gemfile.lock was not changed 1`] = `null`; exports[`bundler.updateArtifacts() returns null if Gemfile.lock was not changed 2`] = ` @@ -205,6 +227,28 @@ Array [ ] `; +exports[`bundler.updateArtifacts() throws BUNDLER_UNKNOWN_ERROR when failing in lockFileMaintenance true 1`] = ` +Array [ + Object { + "cmd": "bundle lock", + "options": Object { + "cwd": "/tmp/github/some/repo", + "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[`bundler.updateArtifacts() works explicit global binarySource 1`] = ` Array [ Object { diff --git a/lib/manager/bundler/artifacts.spec.ts b/lib/manager/bundler/artifacts.spec.ts index e7043503e0cb81b97923785245c870157894d3cb..b3be4cfd30603afb99c8688677959f16199f7b54 100644 --- a/lib/manager/bundler/artifacts.spec.ts +++ b/lib/manager/bundler/artifacts.spec.ts @@ -12,6 +12,7 @@ import * as _bundlerHostRules from './host-rules'; import { BinarySource } from '../../util/exec/common'; import { setUtilConfig } from '../../util'; import { resetPrefetchedImages } from '../../util/exec/docker'; +import { BUNDLER_UNKNOWN_ERROR } from '../../constants/error-messages'; const fs: jest.Mocked<typeof _fs> = _fs as any; const exec: jest.Mock<typeof _exec> = _exec as any; @@ -49,6 +50,7 @@ describe('bundler.updateArtifacts()', () => { env.getChildProcessEnv.mockReturnValue(envMock.basic); bundlerHostRules.findAllAuthenticatable.mockReturnValue([]); resetPrefetchedImages(); + global.repoCache = {}; await setUtilConfig(config); }); it('returns null by default', async () => { @@ -260,4 +262,48 @@ describe('bundler.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); }); + it('throws BUNDLER_UNKNOWN_ERROR when failing in lockFileMaintenance true', async () => { + const execError = new Error(); + (execError as any).stdout = ' foo was resolved to'; + (execError as any).stderr = ''; + platform.getFile.mockResolvedValueOnce('Current Gemfile.lock'); + fs.outputFile.mockResolvedValueOnce(null as never); + const execSnapshots = mockExecAll(exec, execError); + platform.getRepoStatus.mockResolvedValueOnce({ + modified: ['Gemfile.lock'], + } as Git.StatusResult); + await expect( + updateArtifacts({ + packageFileName: 'Gemfile', + updatedDeps: [], + newPackageFileContent: '{}', + config: { + ...config, + isLockFileMaintenance: true, + }, + }) + ).rejects.toThrowError(BUNDLER_UNKNOWN_ERROR); + expect(execSnapshots).toMatchSnapshot(); + }); + it('performs lockFileMaintenance', async () => { + platform.getFile.mockResolvedValueOnce('Current Gemfile.lock'); + fs.outputFile.mockResolvedValueOnce(null as never); + const execSnapshots = mockExecAll(exec); + platform.getRepoStatus.mockResolvedValueOnce({ + modified: ['Gemfile.lock'], + } as Git.StatusResult); + fs.readFile.mockResolvedValueOnce('Updated Gemfile.lock' as any); + expect( + await updateArtifacts({ + packageFileName: 'Gemfile', + updatedDeps: [], + newPackageFileContent: '{}', + config: { + ...config, + isLockFileMaintenance: true, + }, + }) + ).not.toBeNull(); + expect(execSnapshots).toMatchSnapshot(); + }); }); diff --git a/lib/manager/bundler/artifacts.ts b/lib/manager/bundler/artifacts.ts index 3e271f52da1fb8b83e8892b81dbc5eaeda1eea25..b2b7c07a52dbc699bee9330433059b167bceafa4 100644 --- a/lib/manager/bundler/artifacts.ts +++ b/lib/manager/bundler/artifacts.ts @@ -2,6 +2,7 @@ import { getSiblingFileName, readLocalFile, writeLocalFile, + deleteLocalFile, } from '../../util/fs'; import { exec, ExecOptions } from '../../util/exec'; import { logger } from '../../logger'; @@ -85,10 +86,21 @@ export async function updateArtifacts( logger.debug('No Gemfile.lock found'); return null; } + + if (config.isLockFileMaintenance) { + await deleteLocalFile(lockFileName); + } + try { await writeLocalFile(packageFileName, newPackageFileContent); - const cmd = `bundle lock --update ${updatedDeps.join(' ')}`; + let cmd; + + if (config.isLockFileMaintenance) { + cmd = 'bundle lock'; + } else { + cmd = `bundle lock --update ${updatedDeps.join(' ')}`; + } let bundlerVersion = ''; const { bundler } = compatibility; @@ -171,7 +183,7 @@ export async function updateArtifacts( throw new Error(BUNDLER_INVALID_CREDENTIALS); } const resolveMatchRe = new RegExp('\\s+(.*) was resolved to', 'g'); - if (output.match(resolveMatchRe)) { + if (output.match(resolveMatchRe) && !config.isLockFileMaintenance) { logger.debug({ err }, 'Bundler has a resolve error'); const resolveMatches = []; let resolveMatch; diff --git a/lib/manager/bundler/index.ts b/lib/manager/bundler/index.ts index d94aecf09e6806c94b215ef2de30ac6043f43659..bcb44731b29ad993664e80bd7326cb557412f9d3 100644 --- a/lib/manager/bundler/index.ts +++ b/lib/manager/bundler/index.ts @@ -6,6 +6,7 @@ import { LANGUAGE_RUBY } from '../../constants/languages'; import * as rubyVersioning from '../../versioning/ruby'; const language = LANGUAGE_RUBY; +export const supportsLockFileMaintenance = true; /* * Each of the below functions contain some explanations within their own files. diff --git a/lib/util/fs.ts b/lib/util/fs.ts index 3532ac9be75996bcf6a0f6a0402d4624baabe924..d71d088496acc635b6b74c0ea5c9811f6b911570 100644 --- a/lib/util/fs.ts +++ b/lib/util/fs.ts @@ -1,5 +1,5 @@ import { parse, join } from 'upath'; -import { outputFile, readFile } from 'fs-extra'; +import { outputFile, readFile, remove } from 'fs-extra'; import { logger } from '../logger'; import { RenovateConfig } from '../config/common'; @@ -47,3 +47,7 @@ export async function writeLocalFile( const localFileName = join(localDir, fileName); await outputFile(localFileName, fileContent); } + +export async function deleteLocalFile(fileName: string): Promise<void> { + await remove(fileName); +}