diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index da534135fb132d..29ad00fbf4be45 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -29,7 +29,7 @@ module.exports = { In the `renovate.json` file, define the commands and files to be included in the final commit. -The command to install dependencies (`npm ci --ignore-scripts`) is needed because, by default, the installation of dependencies is skipped. +The command to install dependencies (`npm ci --ignore-scripts`) is needed because, by default, the installation of dependencies is skipped (see the `skipInstalls` global option). ```json { @@ -798,6 +798,12 @@ It could then be used in a repository config or preset like so: Secret names must start with an upper or lower case character and can have only characters, digits, or underscores. +## 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 ## unicodeEmoji diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 82bae582950210..eb314221a872df 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -729,6 +729,14 @@ const options: RenovateOptions[] = [ description: 'Set to `false` to disable lock file updating.', type: 'boolean', }, + { + name: 'skipInstalls', + description: + 'Skip installing modules/dependencies if lock file updating is possible without a full install.', + type: 'boolean', + default: null, + globalOnly: true, + }, { name: 'autodiscover', description: 'Autodiscover all repositories.', diff --git a/lib/config/types.ts b/lib/config/types.ts index ad00ee60404919..9fbabb40fa68ac 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -262,6 +262,7 @@ export interface RenovateConfig secrets?: Record; constraints?: Record; + skipInstalls?: boolean | null; constraintsFiltering?: ConstraintsFilter; diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 170f4ff30d151a..d43e4702551c73 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -9,7 +9,7 @@ const realFs = jest.requireActual( ); const defaultExtractConfig = { - skipInstalls: true, + skipInstalls: null, } satisfies ExtractConfig; const input01Content = Fixtures.get('inputs/01.json', '..'); diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 6db976a92e5316..467cdef6b849fc 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -473,15 +473,19 @@ export async function extractPackageFile( return null; } } - let skipInstalls = true; // skip installing modules by default - if ((hasFancyRefs && lockFiles.npmLock) || yarnZeroInstall) { - // https://github.com/npm/cli/issues/1432 - // Explanation: - // - npm install --package-lock-only is buggy for transitive deps in file: and npm: references - // - So we set skipInstalls to false if file: or npm: refs are found *and* the user hasn't explicitly set the value already - // - Also, do not skip install if Yarn zero-install is used - logger.debug('Automatically setting skipInstalls to false'); - skipInstalls = false; + let skipInstalls = config.skipInstalls; + if (skipInstalls === null) { + if ((hasFancyRefs && lockFiles.npmLock) || yarnZeroInstall) { + // https://github.com/npm/cli/issues/1432 + // Explanation: + // - npm install --package-lock-only is buggy for transitive deps in file: and npm: references + // - So we set skipInstalls to false if file: or npm: refs are found *and* the user hasn't explicitly set the value already + // - Also, do not skip install if Yarn zero-install is used + logger.debug('Automatically setting skipInstalls to false'); + skipInstalls = false; + } else { + skipInstalls = true; + } } return { diff --git a/lib/modules/manager/types.ts b/lib/modules/manager/types.ts index f037a97f1afaef..311288b6bf9369 100644 --- a/lib/modules/manager/types.ts +++ b/lib/modules/manager/types.ts @@ -20,7 +20,7 @@ export interface ExtractConfig extends CustomExtractConfig { registryAliases?: Record; npmrc?: string; npmrcMerge?: boolean; - skipInstalls?: boolean; + skipInstalls?: boolean | null; } export interface CustomExtractConfig extends RegexManagerTemplates { @@ -64,7 +64,7 @@ export interface PackageFileContent> lockFiles?: string[]; npmrc?: string; packageFileVersion?: string; - skipInstalls?: boolean; + skipInstalls?: boolean | null; matchStrings?: string[]; matchStringsStrategy?: MatchStringsStrategy; } @@ -274,7 +274,7 @@ export interface PostUpdateConfig> ManagerData { updatedPackageFiles?: FileChange[]; postUpdateOptions?: string[]; - skipInstalls?: boolean; + skipInstalls?: boolean | null; ignoreScripts?: boolean; packageFile?: string; diff --git a/lib/workers/repository/extract/extract-fingerprint-config.spec.ts b/lib/workers/repository/extract/extract-fingerprint-config.spec.ts index e3d252ba282147..c0ac40404f4e46 100644 --- a/lib/workers/repository/extract/extract-fingerprint-config.spec.ts +++ b/lib/workers/repository/extract/extract-fingerprint-config.spec.ts @@ -47,6 +47,7 @@ describe('workers/repository/extract/extract-fingerprint-config', () => { registryAliases: { notStable: 'http://some.link.2', }, + skipInstalls: null, }); expect( fingerprintConfig.managers.find((manager) => manager.manager === 'regex') @@ -65,6 +66,7 @@ describe('workers/repository/extract/extract-fingerprint-config', () => { registryAliases: { stable: 'http://some.link', }, + skipInstalls: null, }); }); @@ -88,6 +90,7 @@ describe('workers/repository/extract/extract-fingerprint-config', () => { npmrc: 'some-string', npmrcMerge: true, registryAliases: {}, + skipInstalls: null, }); expect( fingerprintConfig.managers.find( @@ -106,6 +109,7 @@ describe('workers/repository/extract/extract-fingerprint-config', () => { npmrc: 'some-string', npmrcMerge: true, registryAliases: {}, + skipInstalls: null, }); expect( fingerprintConfig.managers.find((manager) => manager.manager === 'regex')