diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 5283cde1724deba8cff24c468734c65941e06bc3..53232f4c4b453f8f0f676e870adae2a6d9686b8e 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -2889,18 +2889,27 @@ It is also recommended to avoid `rebaseWhen=never` as it can result in conflicte Avoid setting `rebaseWhen=never` and then also setting `prCreation=not-pending` as this can prevent creation of PRs. -## recreateClosed +## recreateWhen -By default, Renovate will detect if it has proposed an update to a project before and not propose the same one again. -For example the Webpack 3.x case described above. -This field lets you customize this behavior down to a per-package level. -For example we override it to `true` in the following cases where branch names and PR titles need to be reused: +This feature used to be called `recreateClosed`. + +By default, Renovate detects if it proposed an update to a project before, and will not propose the same update again. +For example the Webpack 3.x case described in the [`separateMajorMinor`](#separatemajorminor) documentation. +You can use `recreateWhen` to customize this behavior down to a per-package level. +For example we override it to `always` in the following cases where branch names and PR titles must be reused: - Package groups - When pinning versions - Lock file maintenance -Typically you shouldn't need to modify this setting. +You can select which behavior you want from Renovate: + +- `always`: Recreates all closed or blocking PRs +- `auto`: The default option. Recreates only immortal PRs (default) +- `never`: No PR is recreated, doesn't matter if it is immortal or not + +We recommend that you stick with the default setting for this option. +Only change this setting if you really need to. ## regexManagers diff --git a/docs/usage/key-concepts/pull-requests.md b/docs/usage/key-concepts/pull-requests.md index 4f63045a5f7765a82822f22d0cbefb5519d0c62b..7e2af2e96a9bb1941d3ecdf2d3bda957e404b5e5 100644 --- a/docs/usage/key-concepts/pull-requests.md +++ b/docs/usage/key-concepts/pull-requests.md @@ -76,6 +76,7 @@ If you regularly wish to close immortal PRs, it's an indication that you may be ### How to fix immortal PRs Avoid grouping dependencies together which have different versions, or which you have a high chance of wanting to ignore. +If you have immortal PRs which you want to keep closed, then set `"recreateWhen": "never"`. #### Major updates require Dependency Dashboard approval diff --git a/lib/config/__snapshots__/index.spec.ts.snap b/lib/config/__snapshots__/index.spec.ts.snap index 83825d9ee31c87852e6ae2fd6372e6fb5f271bbe..e8b55b377a356191d74cc9cf6eb149f80e6b4be2 100644 --- a/lib/config/__snapshots__/index.spec.ts.snap +++ b/lib/config/__snapshots__/index.spec.ts.snap @@ -12,7 +12,7 @@ exports[`config/index mergeChildConfig(parentConfig, childConfig) merges 1`] = ` "Change": "All locks refreshed", }, "rebaseStalePrs": true, - "recreateClosed": true, + "recreateWhen": "always", "schedule": [ "on monday", ], diff --git a/lib/config/migrations/custom/recreate-closed-migration.spec.ts b/lib/config/migrations/custom/recreate-closed-migration.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..38628ca6c44f1f1890778fc71719178ead29a0ec --- /dev/null +++ b/lib/config/migrations/custom/recreate-closed-migration.spec.ts @@ -0,0 +1,25 @@ +import { RecreateClosedMigration } from './recreate-closed-migration'; + +describe('config/migrations/custom/recreate-closed-migration', () => { + it('should migrate true', () => { + expect(RecreateClosedMigration).toMigrate( + { + recreateClosed: true, + }, + { + recreateWhen: 'always', + } + ); + }); + + it('should migrate false', () => { + expect(RecreateClosedMigration).toMigrate( + { + recreateClosed: false, + }, + { + recreateWhen: 'auto', + } + ); + }); +}); diff --git a/lib/config/migrations/custom/recreate-closed-migration.ts b/lib/config/migrations/custom/recreate-closed-migration.ts new file mode 100644 index 0000000000000000000000000000000000000000..81cd106f4e4ee2d5dfaea13686a2a6befacebe67 --- /dev/null +++ b/lib/config/migrations/custom/recreate-closed-migration.ts @@ -0,0 +1,13 @@ +import is from '@sindresorhus/is'; +import { AbstractMigration } from '../base/abstract-migration'; + +export class RecreateClosedMigration extends AbstractMigration { + override readonly deprecated = true; + override readonly propertyName = 'recreateClosed'; + + override run(value: unknown): void { + if (is.boolean(value)) { + this.setSafely('recreateWhen', value ? 'always' : 'auto'); + } + } +} diff --git a/lib/config/migrations/migrations-service.ts b/lib/config/migrations/migrations-service.ts index 1b6c0d357d515fb6c6fd6337bbc5fa9a7e8738ec..e7e3b314bb51d1debc2d0d08d76ffddc9f51f289 100644 --- a/lib/config/migrations/migrations-service.ts +++ b/lib/config/migrations/migrations-service.ts @@ -39,6 +39,7 @@ import { PostUpdateOptionsMigration } from './custom/post-update-options-migrati import { RaiseDeprecationWarningsMigration } from './custom/raise-deprecation-warnings-migration'; import { RebaseConflictedPrs } from './custom/rebase-conflicted-prs-migration'; import { RebaseStalePrsMigration } from './custom/rebase-stale-prs-migration'; +import { RecreateClosedMigration } from './custom/recreate-closed-migration'; import { RenovateForkMigration } from './custom/renovate-fork-migration'; import { RequireConfigMigration } from './custom/require-config-migration'; import { RequiredStatusChecksMigration } from './custom/required-status-checks-migration'; @@ -145,6 +146,7 @@ export class MigrationsService { SemanticPrefixMigration, MatchDatasourcesMigration, DatasourceMigration, + RecreateClosedMigration, StabilityDaysMigration, ]; diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index f71066be46c06b4e363af7d43b73e5279e9a3e16..f539bbf96b839a4e0ad4ac6914b1fd78a9bf2c46 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1574,10 +1574,11 @@ const options: RenovateOptions[] = [ default: false, }, { - name: 'recreateClosed', + name: 'recreateWhen', description: 'Recreate PRs even if same ones were closed previously.', - type: 'boolean', - default: false, + type: 'string', + default: 'auto', + allowedValues: ['auto', 'always', 'never'], }, { name: 'rebaseWhen', @@ -1899,7 +1900,7 @@ const options: RenovateOptions[] = [ type: 'object', default: { enabled: false, - recreateClosed: true, + recreateWhen: 'always', rebaseStalePrs: true, branchTopic: 'lock-file-maintenance', commitMessageAction: 'Lock file maintenance', diff --git a/lib/config/types.ts b/lib/config/types.ts index 5c390b09c825a03ca10fd39511dbc218c8b4ba6b..ac915cd80304f56a9a80a6aa68928104f396fe8d 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -21,6 +21,7 @@ export interface GroupConfig extends Record<string, unknown> { branchTopic?: string; } +export type RecreateWhen = 'auto' | 'never' | 'always'; // TODO: Proper typings export interface RenovateSharedConfig { $schema?: string; @@ -70,6 +71,7 @@ export interface RenovateSharedConfig { respectLatest?: boolean; stopUpdatingLabel?: string; rebaseWhen?: string; + recreateWhen?: RecreateWhen; recreateClosed?: boolean; repository?: string; repositoryCache?: RepositoryCacheConfig; diff --git a/lib/workers/global/config/parse/cli.spec.ts b/lib/workers/global/config/parse/cli.spec.ts index a98b4f89390d20ba61556c7ab195a6b02a9ed93d..8cb7a3d7851a9d01dd2445843a7056105f681d03 100644 --- a/lib/workers/global/config/parse/cli.spec.ts +++ b/lib/workers/global/config/parse/cli.spec.ts @@ -33,19 +33,19 @@ describe('workers/global/config/parse/cli', () => { }); it('supports boolean no value', () => { - argv.push('--recreate-closed'); - expect(cli.getConfig(argv)).toEqual({ recreateClosed: true }); + argv.push('--config-migration'); + expect(cli.getConfig(argv)).toEqual({ configMigration: true }); argv = argv.slice(0, -1); }); it('supports boolean space true', () => { - argv.push('--recreate-closed'); + argv.push('--config-migration'); argv.push('true'); - expect(cli.getConfig(argv)).toEqual({ recreateClosed: true }); + expect(cli.getConfig(argv)).toEqual({ configMigration: true }); }); it('throws exception for invalid boolean value', () => { - argv.push('--recreate-closed'); + argv.push('--config-migration'); argv.push('badvalue'); expect(() => cli.getConfig(argv)).toThrow( Error( @@ -55,19 +55,19 @@ describe('workers/global/config/parse/cli', () => { }); it('supports boolean space false', () => { - argv.push('--recreate-closed'); + argv.push('--config-migration'); argv.push('false'); - expect(cli.getConfig(argv)).toEqual({ recreateClosed: false }); + expect(cli.getConfig(argv)).toEqual({ configMigration: false }); }); it('supports boolean equals true', () => { - argv.push('--recreate-closed=true'); - expect(cli.getConfig(argv)).toEqual({ recreateClosed: true }); + argv.push('--config-migration=true'); + expect(cli.getConfig(argv)).toEqual({ configMigration: true }); }); it('supports boolean equals false', () => { - argv.push('--recreate-closed=false'); - expect(cli.getConfig(argv)).toEqual({ recreateClosed: false }); + argv.push('--config-migration=false'); + expect(cli.getConfig(argv)).toEqual({ configMigration: false }); }); it('supports list single', () => { @@ -130,6 +130,12 @@ describe('workers/global/config/parse/cli', () => { ${'--git-lab-automerge=false'} | ${{ platformAutomerge: false }} ${'--git-lab-automerge=true'} | ${{ platformAutomerge: true }} ${'--git-lab-automerge'} | ${{ platformAutomerge: true }} + ${'--recreate-closed=false'} | ${{ recreateWhen: 'auto' }} + ${'--recreate-closed=true'} | ${{ recreateWhen: 'always' }} + ${'--recreate-closed'} | ${{ recreateWhen: 'always' }} + ${'--recreate-when=auto'} | ${{ recreateWhen: 'auto' }} + ${'--recreate-when=always'} | ${{ recreateWhen: 'always' }} + ${'--recreate-when=never'} | ${{ recreateWhen: 'never' }} `('"$arg" -> $config', ({ arg, config }) => { argv.push(arg); expect(cli.getConfig(argv)).toMatchObject(config); diff --git a/lib/workers/global/config/parse/cli.ts b/lib/workers/global/config/parse/cli.ts index f12305fa3e28ab6ae867a91fe843ae87ee6f72b0..37c8d37cac19c95c4b159acbfaf8b89f915f82bf 100644 --- a/lib/workers/global/config/parse/cli.ts +++ b/lib/workers/global/config/parse/cli.ts @@ -34,6 +34,9 @@ export function getConfig(input: string[]): AllConfig { .replace('--aliases', '--registry-aliases') .replace('--include-forks=true', '--fork-processing=enabled') .replace('--include-forks', '--fork-processing=enabled') + .replace('--recreate-closed=false', '--recreate-when=auto') + .replace('--recreate-closed=true', '--recreate-when=always') + .replace('--recreate-closed', '--recreate-when=always') ) .filter((a) => !a.startsWith('--git-fs')); const options = getOptions(); diff --git a/lib/workers/global/config/parse/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts index ff963b7658dadc5f9cad660e409bc5641af46c1b..672839f1c31a60c275ee509d77ce1a2d9c2ac69b 100644 --- a/lib/workers/global/config/parse/env.spec.ts +++ b/lib/workers/global/config/parse/env.spec.ts @@ -10,18 +10,20 @@ describe('workers/global/config/parse/env', () => { }); it('supports boolean true', () => { - const envParam: NodeJS.ProcessEnv = { RENOVATE_RECREATE_CLOSED: 'true' }; - expect(env.getConfig(envParam).recreateClosed).toBeTrue(); + const envParam: NodeJS.ProcessEnv = { RENOVATE_CONFIG_MIGRATION: 'true' }; + expect(env.getConfig(envParam).configMigration).toBeTrue(); }); it('supports boolean false', () => { - const envParam: NodeJS.ProcessEnv = { RENOVATE_RECREATE_CLOSED: 'false' }; - expect(env.getConfig(envParam).recreateClosed).toBeFalse(); + const envParam: NodeJS.ProcessEnv = { + RENOVATE_CONFIG_MIGRATION: 'false', + }; + expect(env.getConfig(envParam).configMigration).toBeFalse(); }); it('throws exception for invalid boolean value', () => { const envParam: NodeJS.ProcessEnv = { - RENOVATE_RECREATE_CLOSED: 'badvalue', + RENOVATE_CONFIG_MIGRATION: 'badvalue', }; expect(() => env.getConfig(envParam)).toThrow( Error( @@ -30,7 +32,7 @@ describe('workers/global/config/parse/env', () => { ); }); - delete process.env.RENOVATE_RECREATE_CLOSED; + delete process.env.RENOVATE_CONFIG_MIGRATION; it('supports list single', () => { const envParam: NodeJS.ProcessEnv = { RENOVATE_LABELS: 'a' }; @@ -83,6 +85,17 @@ describe('workers/global/config/parse/env', () => { expect(res).toMatchObject({ hostRules: [{ foo: 'bar' }] }); }); + test.each` + envArg | config + ${{ RENOVATE_RECREATE_CLOSED: 'true' }} | ${{ recreateWhen: 'always' }} + ${{ RENOVATE_RECREATE_CLOSED: 'false' }} | ${{ recreateWhen: 'auto' }} + ${{ RENOVATE_RECREATE_WHEN: 'auto' }} | ${{ recreateWhen: 'auto' }} + ${{ RENOVATE_RECREATE_WHEN: 'always' }} | ${{ recreateWhen: 'always' }} + ${{ RENOVATE_RECREATE_WHEN: 'never' }} | ${{ recreateWhen: 'never' }} + `('"$envArg" -> $config', ({ envArg, config }) => { + expect(env.getConfig(envArg)).toMatchObject(config); + }); + it('skips misconfigured arrays', () => { const envName = 'RENOVATE_HOST_RULES'; const val = JSON.stringify('foobar'); diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts index 4c1afc8b7ee446795b8b46093beb9d9825e7d590..f176043f0854b34e2cc446817c913272e4608321 100644 --- a/lib/workers/global/config/parse/env.ts +++ b/lib/workers/global/config/parse/env.ts @@ -53,10 +53,41 @@ function renameEnvKeys(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { return result; } +const migratedKeysWithValues = [ + { + oldName: 'recreateClosed', + newName: 'recreateWhen', + from: 'true', + to: 'always', + }, + { + oldName: 'recreateClosed', + newName: 'recreateWhen', + from: 'false', + to: 'auto', + }, +]; + +function massageEnvKeyValues(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { + const result = { ...env }; + for (const { oldName, newName, from, to } of migratedKeysWithValues) { + const key = getEnvName({ name: oldName }); + if (env[key] !== undefined) { + if (result[key] === from) { + delete result[key]; + result[getEnvName({ name: newName })] = to; + } + } + } + return result; +} + export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig { let env = inputEnv; env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX); env = renameEnvKeys(env); + // massage the values of migrated configuration keys + env = massageEnvKeyValues(env); const options = getOptions(); diff --git a/lib/workers/repository/update/branch/check-existing.spec.ts b/lib/workers/repository/update/branch/check-existing.spec.ts index ff7c545d85a5708398be0a269f4020edd5751315..c1f219f22aea3944162f7aab68a0ab35f5f656a5 100644 --- a/lib/workers/repository/update/branch/check-existing.spec.ts +++ b/lib/workers/repository/update/branch/check-existing.spec.ts @@ -20,13 +20,13 @@ describe('workers/repository/update/branch/check-existing', () => { }); it('returns false if recreating closed PRs', async () => { - config.recreateClosed = true; + config.recreateWhen = 'always'; expect(await prAlreadyExisted(config)).toBeNull(); expect(platform.findPr).toHaveBeenCalledTimes(0); }); it('returns false if check misses', async () => { - config.recreatedClosed = true; + config.recreateWhen = 'auto'; expect(await prAlreadyExisted(config)).toBeNull(); expect(platform.findPr).toHaveBeenCalledTimes(1); }); diff --git a/lib/workers/repository/update/branch/check-existing.ts b/lib/workers/repository/update/branch/check-existing.ts index 877d28fa0d378fb5e9a42a606d16a6b669de594f..c0fdba11548862995ff25531e86ca1d4ee313c9b 100644 --- a/lib/workers/repository/update/branch/check-existing.ts +++ b/lib/workers/repository/update/branch/check-existing.ts @@ -8,11 +8,13 @@ export async function prAlreadyExisted( config: BranchConfig ): Promise<Pr | null> { logger.trace({ config }, 'prAlreadyExisted'); - if (config.recreateClosed) { - logger.debug('recreateClosed is true'); + if (config.recreateWhen === 'always') { + logger.debug('recreateWhen is "always". No need to check for closed PR.'); return null; } - logger.debug('recreateClosed is false'); + logger.debug( + 'Check for closed PR because recreating closed PRs is disabled.' + ); // Return if same PR already existed let pr = await platform.findPr({ branchName: config.branchName, diff --git a/lib/workers/repository/update/branch/index.spec.ts b/lib/workers/repository/update/branch/index.spec.ts index 66e276a240c3afdedfac19565de47c019e03c20e..4a438e25442d8668a9313e6e252c1f470ff0c3eb 100644 --- a/lib/workers/repository/update/branch/index.spec.ts +++ b/lib/workers/repository/update/branch/index.spec.ts @@ -1026,7 +1026,7 @@ describe('workers/repository/update/branch/index', () => { artifactErrors: [partial<ArtifactError>()], updatedArtifacts: [partial<FileChange>()], }); - config.recreateClosed = true; + config.recreateWhen = 'always'; scm.branchExists.mockResolvedValue(true); automerge.tryBranchAutomerge.mockResolvedValueOnce('failed'); prWorker.ensurePr.mockResolvedValueOnce({ diff --git a/lib/workers/repository/update/pr/body/config-description.spec.ts b/lib/workers/repository/update/pr/body/config-description.spec.ts index 435c37258c3940c3fa40b87324c12d8e0b3734a8..37bba58b2cc95b504ea798da3f5af9fefb50da52 100644 --- a/lib/workers/repository/update/pr/body/config-description.spec.ts +++ b/lib/workers/repository/update/pr/body/config-description.spec.ts @@ -67,7 +67,7 @@ describe('workers/repository/update/pr/body/config-description', () => { expect(res).toContain(`At any time (no schedule defined).`); }); - it('renders recreateClosed', () => { + it('renders recreateClosed=true', () => { const res = getPrConfigDescription({ ...config, recreateClosed: true, @@ -75,6 +75,19 @@ describe('workers/repository/update/pr/body/config-description', () => { expect(res).toContain(`**Immortal**`); }); + it('does not render recreateClosed=false', () => { + const res = getPrConfigDescription({ + ...config, + recreateClosed: false, + }); + expect(res).not.toContain(`**Immortal**`); + }); + + it('does not render recreateClosed=undefined', () => { + const res = getPrConfigDescription(config); + expect(res).not.toContain(`**Immortal**`); + }); + it('renders singular', () => { const res = getPrConfigDescription({ ...config, diff --git a/lib/workers/repository/update/pr/pr-fingerprint.ts b/lib/workers/repository/update/pr/pr-fingerprint.ts index 955852278123a8d4a1002f2ddd3c3151890469d3..cd91d440690b5b89d6f725210e7df826b41958fc 100644 --- a/lib/workers/repository/update/pr/pr-fingerprint.ts +++ b/lib/workers/repository/update/pr/pr-fingerprint.ts @@ -1,7 +1,11 @@ // fingerprint config is based on the old skip pr update logic // https://github.com/renovatebot/renovate/blob/3d85b6048d6a8c57887b64ed4929e2e02ea41aa0/lib/workers/repository/update/pr/index.ts#L294-L306 -import type { UpdateType, ValidationMessage } from '../../../../config/types'; +import type { + RecreateWhen, + UpdateType, + ValidationMessage, +} from '../../../../config/types'; import { logger } from '../../../../logger'; import type { PrCache } from '../../../../util/cache/repository/types'; import { getElapsedHours } from '../../../../util/date'; @@ -28,7 +32,7 @@ export interface PrBodyFingerprintConfig { prHeader?: string; prTitle?: string; rebaseWhen?: string; - recreateClosed?: boolean; + recreateWhen?: RecreateWhen; schedule?: string[]; stopUpdating?: boolean; timezone?: string; @@ -67,7 +71,7 @@ export function generatePrBodyFingerprintConfig( prHeader: config.prHeader, prTitle: config.prTitle, rebaseWhen: config.rebaseWhen, - recreateClosed: config.recreateClosed, + recreateWhen: config.recreateWhen, schedule: config.schedule, stopUpdating: config.stopUpdating, timezone: config.timezone, diff --git a/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap b/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap index 9455b1b87aa6b6053fb86495b95fd4330b432367..693e02a74fbe5f514cb69aa2c743ffe741e8ac08 100644 --- a/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap +++ b/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap @@ -178,6 +178,7 @@ exports[`workers/repository/updates/generate generateBranchConfig() handles lock "prBodyColumns": [], "prTitle": "some-title", "prettyDepType": "dependency", + "recreateClosed": true, "releaseTimestamp": undefined, "reuseLockFiles": true, "upgrades": [ @@ -191,6 +192,7 @@ exports[`workers/repository/updates/generate generateBranchConfig() handles lock "manager": "some-manager", "prTitle": "some-title", "prettyDepType": "dependency", + "recreateClosed": true, }, ], } diff --git a/lib/workers/repository/updates/generate.spec.ts b/lib/workers/repository/updates/generate.spec.ts index c44d2e74ee6cf5263f22ae1ca6edb18900e82ab8..ca0b111abb5bd8bb89b10eb9578064769302cf8c 100644 --- a/lib/workers/repository/updates/generate.spec.ts +++ b/lib/workers/repository/updates/generate.spec.ts @@ -67,6 +67,7 @@ describe('workers/repository/updates/generate', () => { branchName: 'some-branch', prTitle: 'some-title', isLockFileMaintenance: true, + recreateClosed: true, }, ], }); @@ -216,7 +217,7 @@ describe('workers/repository/updates/generate', () => { }); }); - it('groups major updates with different versions but same newValue, no recreateClosed', () => { + it('groups major updates with different versions but same newValue, no recreateWhen', () => { const branch = [ { manager: 'some-manager', @@ -282,7 +283,7 @@ describe('workers/repository/updates/generate', () => { expect(res.recreateClosed).toBeTrue(); }); - it('Grouped pin & pinDigest can be recreated', () => { + it('recreates grouped pin & pinDigest', () => { const branch = [ { ...requiredDefaultOptions, @@ -304,7 +305,31 @@ describe('workers/repository/updates/generate', () => { expect(res.recreateClosed).toBeTrue(); }); - it('Grouped pin can be recreated', () => { + it('does not recreate grouped pin & pinDigest when closed if recreateWhen=never', () => { + const branch = [ + { + ...requiredDefaultOptions, + isPinDigest: true, + updateType: 'pinDigest', + newValue: 'v2', + newDigest: 'dc323e67f16fb5f7663d20ff7941f27f5809e9b6', + recreateWhen: 'never', + }, + { + ...requiredDefaultOptions, + updateType: 'pin', + isPin: true, + newValue: "'2.2.0'", + newVersion: '2.2.0', + newMajor: 2, + recreateWhen: 'never', + }, + ] as BranchUpgradeConfig[]; + const res = generateBranchConfig(branch); + expect(res.recreateClosed).toBeFalse(); + }); + + it('recreates grouped pin', () => { const branch = [ { ...requiredDefaultOptions, @@ -331,7 +356,7 @@ describe('workers/repository/updates/generate', () => { expect(res.recreateClosed).toBeTrue(); }); - it('grouped pinDigest can be recreated', () => { + it('recreates grouped pinDigest', () => { const branch = [ { ...requiredDefaultOptions, diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts index c56cb1434567d6f4b76fc4afe500e5fc44789d23..c044fb94d175753d441783f4a04bcdbb71c73aa3 100644 --- a/lib/workers/repository/updates/generate.ts +++ b/lib/workers/repository/updates/generate.ts @@ -103,6 +103,10 @@ export function generateBranchConfig( upg.displayFrom = upg.currentValue; upg.displayTo = upg.newValue; } + + if (upg.isLockFileMaintenance) { + upg.recreateClosed = upg.recreateWhen !== 'never'; + } upg.displayFrom ??= ''; upg.displayTo ??= ''; if (!depNames.includes(upg.depName!)) { @@ -178,14 +182,14 @@ export function generateBranchConfig( logger.trace({ toVersions }); logger.trace({ toValues }); delete upgrade.commitMessageExtra; - upgrade.recreateClosed = true; + upgrade.recreateClosed = upgrade.recreateWhen !== 'never'; } else if ( newValue.length > 1 && (upgrade.isDigest || upgrade.isPinDigest) ) { logger.trace({ newValue }); delete upgrade.commitMessageExtra; - upgrade.recreateClosed = true; + upgrade.recreateClosed = upgrade.recreateWhen !== 'never'; } else if (semver.valid(toVersions[0])) { upgrade.isRange = false; }