From d0ec341e15d5beafcf962599e0f88ee3b17460cf Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Wed, 15 Aug 2018 17:13:07 +0200 Subject: [PATCH] feat: skipInstalls (#2390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds new admin option “skipInstalls†that is applicable for npm-only for now (including lerna-npm). If set to false, Renovate will perform a full install of modules rather than `—package-lock-only`. This is necessary in some cases to work around bugs in npm. Self-hosted bot users can set this option themselves on the bot’s config, but app users will require it to be enabled per-repository by the app admin. --- lib/config/definitions.js | 7 ++++++ lib/manager/npm/post-update/index.js | 6 +++-- lib/manager/npm/post-update/lerna.js | 17 +++++++++----- lib/manager/npm/post-update/npm.js | 9 ++++++-- test/workers/branch/lock-files/lerna.spec.js | 22 ++++++++++++++++++- test/workers/branch/lock-files/npm.spec.js | 22 ++++++++++++++++++- .../__snapshots__/flatten.spec.js.snap | 4 ++++ website/docs/self-hosted-configuration.md | 4 ++++ 8 files changed, 80 insertions(+), 11 deletions(-) diff --git a/lib/config/definitions.js b/lib/config/definitions.js index c88fa4d196..eff277f3eb 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -237,6 +237,13 @@ const options = [ description: 'Set to false to disable lock file updating', type: 'boolean', }, + { + name: 'skipInstalls', + description: + 'Skip installing modules/dependencies if lock file updating is possible alone', + type: 'boolean', + admin: true, + }, { name: 'ignoreNpmrcFile', description: 'Whether to ignore any .npmrc file found in repository', diff --git a/lib/manager/npm/post-update/index.js b/lib/manager/npm/post-update/index.js index 856e45e160..9c17294e0e 100644 --- a/lib/manager/npm/post-update/index.js +++ b/lib/manager/npm/post-update/index.js @@ -306,7 +306,8 @@ async function getAdditionalFiles(config, packageFiles) { const res = await npm.generateLockFile( upath.join(config.tmpDir.path, lockFileDir), env, - fileName + fileName, + config.skipInstalls ); if (res.error) { // istanbul ignore if @@ -472,7 +473,8 @@ async function getAdditionalFiles(config, packageFiles) { const res = await lerna.generateLockFiles( lernaPackageFile.lernaClient, upath.join(config.tmpDir.path, lernaDir), - env + env, + config.skipInstalls ); // istanbul ignore else if (res.error) { diff --git a/lib/manager/npm/post-update/lerna.js b/lib/manager/npm/post-update/lerna.js index 4b4da66dca..04a17b616a 100644 --- a/lib/manager/npm/post-update/lerna.js +++ b/lib/manager/npm/post-update/lerna.js @@ -4,7 +4,7 @@ module.exports = { generateLockFiles, }; -async function generateLockFiles(lernaClient, tmpDir, env) { +async function generateLockFiles(lernaClient, tmpDir, env, skipInstalls) { if (!lernaClient) { logger.warn('No lernaClient specified - returning'); return { error: false }; @@ -26,10 +26,17 @@ async function generateLockFiles(lernaClient, tmpDir, env) { } lernaVersion = lernaVersion || 'latest'; logger.debug('Using lerna version ' + lernaVersion); - const params = - lernaClient === 'npm' - ? '--package-lock-only --no-audit' - : '--ignore-scripts --ignore-engines --ignore-platform --mutex network:31879'; + let params; + if (lernaClient === 'npm') { + if (skipInstalls) { + params = '--package-lock-only --no-audit'; + } else { + params = '--no-audit'; + } + } else { + params = + '--ignore-scripts --ignore-engines --ignore-platform --mutex network:31879'; + } cmd = `npm i -g -C ~/.npm/lerna@${lernaVersion} lerna@${lernaVersion} && ${lernaClient} install ${params} && ~/.npm/lerna@${lernaVersion}/bin/lerna bootstrap -- ${params}`; logger.debug({ cmd }); // TODO: Switch to native util.promisify once using only node 8 diff --git a/lib/manager/npm/post-update/npm.js b/lib/manager/npm/post-update/npm.js index 62f7d9f58e..6a90bd0c7f 100644 --- a/lib/manager/npm/post-update/npm.js +++ b/lib/manager/npm/post-update/npm.js @@ -7,7 +7,7 @@ module.exports = { generateLockFile, }; -async function generateLockFile(tmpDir, env, filename) { +async function generateLockFile(tmpDir, env, filename, skipInstalls) { logger.debug(`Spawning npm install to create ${tmpDir}/${filename}`); let lockFile = null; let stdout; @@ -52,7 +52,12 @@ async function generateLockFile(tmpDir, env, filename) { } } } - cmd = `${cmd} --version && ${cmd} install --package-lock-only --no-audit`; + cmd = `${cmd} --version && ${cmd} install`; + if (skipInstalls) { + cmd += ' --package-lock-only --no-audit'; + } else { + cmd += ' --no-audit'; + } logger.debug(`Using npm: ${cmd}`); // TODO: Switch to native util.promisify once using only node 8 ({ stdout, stderr } = await exec(cmd, { diff --git a/test/workers/branch/lock-files/lerna.spec.js b/test/workers/branch/lock-files/lerna.spec.js index 0d00f5f89f..25f6700a25 100644 --- a/test/workers/branch/lock-files/lerna.spec.js +++ b/test/workers/branch/lock-files/lerna.spec.js @@ -13,7 +13,27 @@ describe('generateLockFiles()', () => { JSON.stringify({ dependencies: { lerna: '2.0.0' } }) ); exec.mockReturnValueOnce({}); - const res = await lernaHelper.generateLockFiles('npm', 'some-dir', {}); + const skipInstalls = true; + const res = await lernaHelper.generateLockFiles( + 'npm', + 'some-dir', + {}, + skipInstalls + ); + expect(res.error).toBe(false); + }); + it('performs full npm install', async () => { + platform.getFile.mockReturnValueOnce( + JSON.stringify({ dependencies: { lerna: '2.0.0' } }) + ); + exec.mockReturnValueOnce({}); + const skipInstalls = false; + const res = await lernaHelper.generateLockFiles( + 'npm', + 'some-dir', + {}, + skipInstalls + ); expect(res.error).toBe(false); }); it('generates yarn.lock files', async () => { diff --git a/test/workers/branch/lock-files/npm.spec.js b/test/workers/branch/lock-files/npm.spec.js index 3b405bf144..3e6a131342 100644 --- a/test/workers/branch/lock-files/npm.spec.js +++ b/test/workers/branch/lock-files/npm.spec.js @@ -18,10 +18,30 @@ describe('generateLockFile', () => { stderror: '', }); fs.readFile = jest.fn(() => 'package-lock-contents'); + const skipInstalls = true; const res = await npmHelper.generateLockFile( 'some-dir', {}, - 'package-lock.json' + 'package-lock.json', + skipInstalls + ); + expect(fs.readFile.mock.calls.length).toEqual(1); + expect(res.error).not.toBeDefined(); + expect(res.lockFile).toEqual('package-lock-contents'); + }); + it('performs full install', async () => { + getInstalledPath.mockReturnValueOnce('node_modules/npm'); + exec.mockReturnValueOnce({ + stdout: '', + stderror: '', + }); + fs.readFile = jest.fn(() => 'package-lock-contents'); + const skipInstalls = false; + const res = await npmHelper.generateLockFile( + 'some-dir', + {}, + 'package-lock.json', + skipInstalls ); expect(fs.readFile.mock.calls.length).toEqual(1); expect(res.error).not.toBeDefined(); diff --git a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap index 6efd5cdd19..ecfaecaac9 100644 --- a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap +++ b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap @@ -56,6 +56,7 @@ Array [ "semanticCommitScope": "deps", "semanticCommitType": "chore", "semanticCommits": null, + "skipInstalls": true, "statusCheckVerify": false, "timezone": null, "unpublishSafe": false, @@ -123,6 +124,7 @@ Array [ "semanticCommitScope": "deps", "semanticCommitType": "chore", "semanticCommits": null, + "skipInstalls": true, "statusCheckVerify": false, "timezone": null, "unpublishSafe": false, @@ -203,6 +205,7 @@ Array [ "semanticCommitScope": "deps", "semanticCommitType": "chore", "semanticCommits": null, + "skipInstalls": true, "statusCheckVerify": false, "timezone": null, "unpublishSafe": false, @@ -272,6 +275,7 @@ Array [ "semanticCommitScope": "deps", "semanticCommitType": "chore", "semanticCommits": null, + "skipInstalls": true, "statusCheckVerify": false, "timezone": null, "unpublishSafe": false, diff --git a/website/docs/self-hosted-configuration.md b/website/docs/self-hosted-configuration.md index 263bb33251..8a49f03ba5 100644 --- a/website/docs/self-hosted-configuration.md +++ b/website/docs/self-hosted-configuration.md @@ -61,4 +61,8 @@ Set this to `false` if (a) you configure Renovate entirely on the bot side (i.e. ## requireConfig +## skipInstalls + +By default, Renovate will use the most efficient approach to updating package files and lock files, which in most cases skips the need to perform a full module install by the bot. If this is set to false, then a full install of modules will be done. This is currently applicable to `npm` and `lerna`/`npm` only, and only used in cases where bugs in `npm` result in incorrect lock files being updated. + ## token -- GitLab