From a4ab4523f8de62ffecec7c713aa82bfb7bf18bbe Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Sun, 5 Mar 2023 17:23:41 +0100 Subject: [PATCH] feat(config)!: forkProcessing (#20759) Removes "includeForks" option and replaces with "forkProcessing". New default behavior is to process forks if automerge=false. Closes #20752 BREAKING CHANGE: Forked repos will now be processed automatically if autodiscover=false. includeForks is removed and replaced by new option forkProcessing. --- docs/development/configuration.md | 2 +- docs/development/local-development.md | 11 ------ docs/usage/configuration-options.md | 18 +++++----- .../__snapshots__/migration.spec.ts.snap | 2 +- .../custom/include-forks-migration.spec.ts | 34 +++++++++++++++++++ .../custom/include-forks-migration.ts | 13 +++++++ .../custom/renovate-fork-migration.spec.ts | 4 +-- .../custom/renovate-fork-migration.ts | 2 +- lib/config/options/index.ts | 9 ++--- lib/config/types.ts | 2 +- lib/modules/platform/types.ts | 2 +- lib/workers/global/config/parse/cli.ts | 2 ++ lib/workers/global/config/parse/index.ts | 6 ++++ lib/workers/repository/configured.ts | 2 +- lib/workers/repository/init/apis.spec.ts | 33 ++++++++++++++---- lib/workers/repository/init/apis.ts | 9 +++-- .../repository/onboarding/branch/index.ts | 2 +- 17 files changed, 112 insertions(+), 41 deletions(-) create mode 100644 lib/config/migrations/custom/include-forks-migration.spec.ts create mode 100644 lib/config/migrations/custom/include-forks-migration.ts diff --git a/docs/development/configuration.md b/docs/development/configuration.md index b77fdfd6e3a993..54b007df01d2be 100644 --- a/docs/development/configuration.md +++ b/docs/development/configuration.md @@ -32,7 +32,7 @@ e.g. apply one set of labels for `backend/package.json` and a different set of l module.exports = { npmrc: '//registry.npmjs.org/:_authToken=abc123', baseDir: '/tmp/renovate', - includeForks: true, + forkProcessing: 'enabled', gradle: { enabled: false }, }; ``` diff --git a/docs/development/local-development.md b/docs/development/local-development.md index ef07cd0a5539af..a226fba39b3a66 100644 --- a/docs/development/local-development.md +++ b/docs/development/local-development.md @@ -171,17 +171,6 @@ To do this, see these GitHub guides: ## Tips and tricks -### Running Renovate against forked repositories - -Quite often, the quickest way for you to test or fix something is to fork an existing repository. -But by default Renovate skips over repositories that are forked. -To override this default, you need to specify the setting `includeForks` as `true`. - -Tell Renovate to run on your forked repository by doing one of the following: - -1. Add `"includeForks": true` to the `renovate.json` file in your forked repository -1. Run Renovate with the CLI flag `--renovate-fork=true` - ### Log files Usually, `debug` is good enough to troubleshoot most problems or verify functionality. diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index afa89177db7aab..6912e64938eb5f 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -923,6 +923,16 @@ If this option is enabled, reviewers will need to create a new PR if additional !!! note This option is only relevant if you set `forkToken`. +## forkProcessing + +By default, Renovate will skip over any repositories that are forked if Renovate is using `autodiscover` mode. +This includes if the forked repository has a Renovate config file, because Renovate can't tell if that file was added by the original repository or not. +If you wish to enable processing of a forked repository by Renovate when autodiscovering, you need to add `"forkProcessing": "enabled"` to your repository config or run the CLI command with `--fork-processing=enabled`. + +If you are running in non-autodiscover mode (e.g. supplying a list of repositories to Renovate) but wish to skip forked repositories, you need to configure `"forkProcessing": "disabled"` in your global config. + +If you are using the hosted Mend Renovate then this option will be configured to `"enabled"` automatically if you "Selected" repositories individually but `"disabled"` if you installed for "All" repositories. If you have installed Renovate into "All" repositories but have a fork you want to use, then add `"forkProcessing": "enabled"` to the repository's `renovate.json` file. + ## gitAuthor You can customize the Git author that's used whenever Renovate creates a commit. @@ -1417,14 +1427,6 @@ If you need to force permanent unstable updates for a package, you can add a pac Also check out the `followTag` configuration option above if you wish Renovate to keep you pinned to a particular release tag. -## includeForks - -By default, Renovate will skip over any repositories that are forked. -This includes if the forked repository has a Renovate config file, because Renovate can't tell if that file was added by the original repository or not. -If you wish to enable processing of a forked repository by Renovate, you need to add `"includeForks": true` to your repository config or run the CLI command with `--include-forks=true`. - -If you are using the hosted Mend Renovate then this option will be configured to `true` automatically if you "Selected" repositories individually but remain as `false` if you installed for "All" repositories. - ## includePaths If you wish for Renovate to process only select paths in the repository, use `includePaths`. diff --git a/lib/config/__snapshots__/migration.spec.ts.snap b/lib/config/__snapshots__/migration.spec.ts.snap index fdf32c597a036c..572cc3bbdb1787 100644 --- a/lib/config/__snapshots__/migration.spec.ts.snap +++ b/lib/config/__snapshots__/migration.spec.ts.snap @@ -131,6 +131,7 @@ exports[`config/migration migrateConfig(config, parentConfig) migrates config 1` "config:js-lib", ":dependencyDashboard", ], + "forkProcessing": "enabled", "hostRules": [ { "hostType": "docker", @@ -142,7 +143,6 @@ exports[`config/migration migrateConfig(config, parentConfig) migrates config 1` "ignorePaths": [ "node_modules/", ], - "includeForks": true, "lockFileMaintenance": { "automerge": true, "exposeAllEnv": false, diff --git a/lib/config/migrations/custom/include-forks-migration.spec.ts b/lib/config/migrations/custom/include-forks-migration.spec.ts new file mode 100644 index 00000000000000..ca4ce0b9b2a5b2 --- /dev/null +++ b/lib/config/migrations/custom/include-forks-migration.spec.ts @@ -0,0 +1,34 @@ +import { RenovateForkMigration } from './include-forks-migration'; + +describe('config/migrations/custom/include-forks-migration', () => { + it('should migrate true', () => { + expect(RenovateForkMigration).toMigrate( + { + includeForks: true, + }, + { + forkProcessing: 'enabled', + } + ); + }); + + it('should migrate false', () => { + expect(RenovateForkMigration).toMigrate( + { + includeForks: false, + }, + { + forkProcessing: 'disabled', + } + ); + }); + + it('should not migrate non boolean value', () => { + expect(RenovateForkMigration).toMigrate( + { + includeForks: 'test', + }, + {} + ); + }); +}); diff --git a/lib/config/migrations/custom/include-forks-migration.ts b/lib/config/migrations/custom/include-forks-migration.ts new file mode 100644 index 00000000000000..5afede3db6b588 --- /dev/null +++ b/lib/config/migrations/custom/include-forks-migration.ts @@ -0,0 +1,13 @@ +import is from '@sindresorhus/is'; +import { AbstractMigration } from '../base/abstract-migration'; + +export class RenovateForkMigration extends AbstractMigration { + override readonly deprecated = true; + override readonly propertyName = 'includeForks'; + + override run(value: unknown): void { + if (is.boolean(value)) { + this.setSafely('forkProcessing', value ? 'enabled' : 'disabled'); + } + } +} diff --git a/lib/config/migrations/custom/renovate-fork-migration.spec.ts b/lib/config/migrations/custom/renovate-fork-migration.spec.ts index 3e0841ebbce4ad..daa11f1885918c 100644 --- a/lib/config/migrations/custom/renovate-fork-migration.spec.ts +++ b/lib/config/migrations/custom/renovate-fork-migration.spec.ts @@ -7,7 +7,7 @@ describe('config/migrations/custom/renovate-fork-migration', () => { renovateFork: true, }, { - includeForks: true, + forkProcessing: 'enabled', } ); }); @@ -18,7 +18,7 @@ describe('config/migrations/custom/renovate-fork-migration', () => { renovateFork: false, }, { - includeForks: false, + forkProcessing: 'disabled', } ); }); diff --git a/lib/config/migrations/custom/renovate-fork-migration.ts b/lib/config/migrations/custom/renovate-fork-migration.ts index 4a2b0d61d9ddfd..071ffdac5f7ea7 100644 --- a/lib/config/migrations/custom/renovate-fork-migration.ts +++ b/lib/config/migrations/custom/renovate-fork-migration.ts @@ -7,7 +7,7 @@ export class RenovateForkMigration extends AbstractMigration { override run(value: unknown): void { if (is.boolean(value)) { - this.setSafely('includeForks', value); + this.setSafely('forkProcessing', value ? 'enabled' : 'disabled'); } } } diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 077276162487bb..1ed247e6610519 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -419,12 +419,13 @@ const options: RenovateOptions[] = [ experimentalIssues: [17633], }, { - name: 'includeForks', + name: 'forkProcessing', description: - 'Whether to process forked repositories. By default, all forked repositories are skipped.', + 'Whether to process forked repositories. By default, all forked repositories are skipped when in autodiscover mode.', stage: 'repository', - type: 'boolean', - default: false, + type: 'string', + allowedValues: ['auto', 'enabled', 'disabled'], + default: 'auto', }, { name: 'forkToken', diff --git a/lib/config/types.ts b/lib/config/types.ts index 9891748ea53216..0a48bb829bad2c 100644 --- a/lib/config/types.ts +++ b/lib/config/types.ts @@ -213,7 +213,7 @@ export interface RenovateConfig hostRules?: HostRule[]; ignorePresets?: string[]; - includeForks?: boolean; + forkProcessing?: 'auto' | 'enabled' | 'disabled'; isFork?: boolean; fileList?: string[]; diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts index f5f8a15285be67..dab52e1aa86c16 100644 --- a/lib/modules/platform/types.ts +++ b/lib/modules/platform/types.ts @@ -38,7 +38,7 @@ export interface RepoParams { endpoint?: string; gitUrl?: GitUrlOption; forkToken?: string; - includeForks?: boolean; + forkProcessing?: 'enabled' | 'disabled'; renovateUsername?: string; cloneSubmodules?: boolean; ignorePrAuthor?: boolean; diff --git a/lib/workers/global/config/parse/cli.ts b/lib/workers/global/config/parse/cli.ts index 019dc343e95728..f12305fa3e28ab 100644 --- a/lib/workers/global/config/parse/cli.ts +++ b/lib/workers/global/config/parse/cli.ts @@ -32,6 +32,8 @@ export function getConfig(input: string[]): AllConfig { .replace(/^--dry-run$/, '--dry-run=true') .replace(/^--require-config$/, '--require-config=true') .replace('--aliases', '--registry-aliases') + .replace('--include-forks=true', '--fork-processing=enabled') + .replace('--include-forks', '--fork-processing=enabled') ) .filter((a) => !a.startsWith('--git-fs')); const options = getOptions(); diff --git a/lib/workers/global/config/parse/index.ts b/lib/workers/global/config/parse/index.ts index 5bfed02cbf2060..0823d06c6ff5d9 100644 --- a/lib/workers/global/config/parse/index.ts +++ b/lib/workers/global/config/parse/index.ts @@ -99,6 +99,12 @@ export async function parseConfigs( config.endpoint = ensureTrailingSlash(config.endpoint); } + // Massage forkProcessing + if (!config.autodiscover && config.forkProcessing !== 'disabled') { + logger.debug('Enabling forkProcessing while in non-autodiscover mode'); + config.forkProcessing = 'enabled'; + } + // Remove log file entries delete config.logFile; delete config.logFileLevel; diff --git a/lib/workers/repository/configured.ts b/lib/workers/repository/configured.ts index 80e88016f5c9c5..d6b57ee7a33291 100644 --- a/lib/workers/repository/configured.ts +++ b/lib/workers/repository/configured.ts @@ -8,7 +8,7 @@ export function checkIfConfigured(config: RenovateConfig): void { if (config.enabled === false) { throw new Error(REPOSITORY_DISABLED_BY_CONFIG); } - if (config.isFork && !config.includeForks) { + if (config.isFork && config.forkProcessing !== 'enabled') { throw new Error(REPOSITORY_FORKED); } } diff --git a/lib/workers/repository/init/apis.spec.ts b/lib/workers/repository/init/apis.spec.ts index 2daa89b7d150fd..b87e59d4013abf 100644 --- a/lib/workers/repository/init/apis.spec.ts +++ b/lib/workers/repository/init/apis.spec.ts @@ -15,7 +15,7 @@ describe('workers/repository/init/apis', () => { config.warnings = []; config.token = 'some-token'; delete config.optimizeForDisabled; - delete config.includeForks; + delete config.forkProcessing; }); afterEach(() => { @@ -53,15 +53,30 @@ describe('workers/repository/init/apis', () => { isFork: true, repoFingerprint: '123', }); - platform.getJsonFile.mockResolvedValueOnce({ includeForks: false }); + platform.getJsonFile.mockResolvedValueOnce({ + forkProcessing: 'disabled', + }); await expect( initApis({ ...config, - includeForks: false, + forkProcessing: 'disabled', }) ).rejects.toThrow(REPOSITORY_FORKED); }); + it('does not throw for includeForks=true', async () => { + platform.initRepo.mockResolvedValueOnce({ + defaultBranch: 'master', + isFork: true, + repoFingerprint: '123', + }); + platform.getJsonFile.mockResolvedValueOnce({ + includeForks: true, + }); + const workerPlatformConfig = await initApis(config); + expect(workerPlatformConfig).toBeTruthy(); + }); + it('ignores platform.getJsonFile() failures', async () => { platform.initRepo.mockResolvedValueOnce({ defaultBranch: 'master', @@ -73,7 +88,7 @@ describe('workers/repository/init/apis', () => { initApis({ ...config, optimizeForDisabled: true, - includeForks: false, + forkProcessing: 'disabled', isFork: true, }) ).resolves.not.toThrow(); @@ -85,7 +100,9 @@ describe('workers/repository/init/apis', () => { isFork: false, repoFingerprint: '123', }); - platform.getJsonFile.mockResolvedValueOnce({ includeForks: false }); + platform.getJsonFile.mockResolvedValueOnce({ + forkProcessing: 'disabled', + }); const workerPlatformConfig = await initApis({ ...config, optimizeForDisabled: true, @@ -107,7 +124,9 @@ describe('workers/repository/init/apis', () => { isFork: false, repoFingerprint: '123', }); - platform.getJsonFile.mockResolvedValueOnce({ includeForks: false }); + platform.getJsonFile.mockResolvedValueOnce({ + forkProcessing: 'disabled', + }); const workerPlatformConfig = await initApis({ ...config, optimizeForDisabled: true, @@ -124,7 +143,7 @@ describe('workers/repository/init/apis', () => { isFork: false, repoFingerprint: '123', }); - platform.getJsonFile.mockResolvedValueOnce({ includeForks: false }); + platform.getJsonFile.mockResolvedValueOnce({ forkProcessing: false }); const workerPlatformConfig = await initApis({ ...config, optimizeForDisabled: true, diff --git a/lib/workers/repository/init/apis.ts b/lib/workers/repository/init/apis.ts index d1d425ad335ed4..1eec1212a139db 100644 --- a/lib/workers/repository/init/apis.ts +++ b/lib/workers/repository/init/apis.ts @@ -4,6 +4,7 @@ import { REPOSITORY_DISABLED_BY_CONFIG, REPOSITORY_FORKED, } from '../../../constants/error-messages'; +import { logger } from '../../../logger'; import { RepoParams, RepoResult, platform } from '../../../modules/platform'; // TODO: fix types (#7154) @@ -37,11 +38,15 @@ async function validateOptimizeForDisabled( } async function validateIncludeForks(config: RenovateConfig): Promise { - if (!config.includeForks && config.isFork) { + if (config.forkProcessing !== 'enabled' && config.isFork) { const renovateConfig = await getJsonFile(defaultConfigFile(config)); - if (!renovateConfig?.includeForks) { + if ( + renovateConfig?.includeForks !== true && + renovateConfig?.forkProcessing !== 'enabled' + ) { throw new Error(REPOSITORY_FORKED); } + logger.debug('Repository config enables forks - continuing'); } } diff --git a/lib/workers/repository/onboarding/branch/index.ts b/lib/workers/repository/onboarding/branch/index.ts index e2b07ce4bebaaa..2add4def7375e7 100644 --- a/lib/workers/repository/onboarding/branch/index.ts +++ b/lib/workers/repository/onboarding/branch/index.ts @@ -28,7 +28,7 @@ export async function checkOnboardingBranch( logger.debug('Repo is onboarded'); return { ...config, repoIsOnboarded }; } - if (config.isFork && !config.includeForks) { + if (config.isFork && config.forkProcessing !== 'enabled') { throw new Error(REPOSITORY_FORKED); } logger.debug('Repo is not onboarded');