diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index 57e7e33536c9e025ba6e22193b3bd5d418bfc3a7..b38bfcd446a7a35b04ffbecb8081dfc55612ae17 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -555,6 +555,12 @@ In practice, it is implemented by converting the `force` configuration into a `p This is set to `true` by default, meaning that any settings (such as `schedule`) take maximum priority even against custom settings existing inside individual repositories. It will also override any settings in `packageRules`. +## forkCreation + +This configuration lets you disable the runtime forking of repositories when running in "fork mode". + +Usually you will need to keep this as the default `true`, and only set to `false` if you have some out of band process to handle the creation of forks. + ## forkOrg This configuration option lets you choose an organization you want repositories forked into when "fork mode" is enabled. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 00459b4fa29be1bc1e22331ec526ed8f2e92f186..cdf1f8cb186767280c5908f007cbc1cf617ae07e 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -530,6 +530,17 @@ const options: RenovateOptions[] = [ supportedPlatforms: ['gitlab'], globalOnly: true, }, + { + name: 'forkCreation', + description: + 'Whether to create forks as needed at runtime when running in "fork mode".', + stage: 'repository', + type: 'boolean', + globalOnly: true, + supportedPlatforms: ['github'], + experimental: true, + default: true, + }, { name: 'forkToken', description: 'Set a personal access token here to enable "fork mode".', diff --git a/lib/constants/error-messages.ts b/lib/constants/error-messages.ts index 490e96373341932b6c3cff5c0ee162224f19ff5b..aa7da4b79e500e6c5e83752496f31d3b0e103cc7 100644 --- a/lib/constants/error-messages.ts +++ b/lib/constants/error-messages.ts @@ -28,6 +28,7 @@ export const REPOSITORY_CLOSED_ONBOARDING = 'disabled-closed-onboarding'; export const REPOSITORY_DISABLED_BY_CONFIG = 'disabled-by-config'; export const REPOSITORY_NO_CONFIG = 'disabled-no-config'; export const REPOSITORY_EMPTY = 'empty'; +export const REPOSITORY_FORK_MISSING = 'fork-missing'; export const REPOSITORY_FORK_MODE_FORKED = 'fork-mode-forked'; export const REPOSITORY_FORKED = 'fork'; export const REPOSITORY_MIRRORED = 'mirror'; diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts index 1f7e15ef8f6fd83e041ad0f30aaac7ceb303dfd0..d887628eb921d0b23249e874e6edf65cb7c137b4 100644 --- a/lib/modules/platform/github/index.spec.ts +++ b/lib/modules/platform/github/index.spec.ts @@ -8,6 +8,7 @@ import { PLATFORM_UNKNOWN_ERROR, REPOSITORY_CANNOT_FORK, REPOSITORY_FORKED, + REPOSITORY_FORK_MISSING, REPOSITORY_NOT_FOUND, REPOSITORY_RENAMED, } from '../../../constants/error-messages'; @@ -546,10 +547,26 @@ describe('modules/platform/github/index', () => { const config = await github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }); expect(config).toMatchSnapshot(); }); + it('should throw if fork needed but forkCreation=false', async () => { + const scope = httpMock.scope(githubApiHost); + forkInitRepoMock(scope, 'some/repo', false); + scope.get('/user').reply(200, { + login: 'forked', + }); + await expect( + github.initRepo({ + repository: 'some/repo', + forkToken: 'true', + forkCreation: false, + }), + ).rejects.toThrow(REPOSITORY_FORK_MISSING); + }); + it('throws if the repo is a fork', async () => { const repo = 'some/repo'; const branch = 'master'; @@ -559,6 +576,7 @@ describe('modules/platform/github/index', () => { github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }), ).rejects.toThrow(REPOSITORY_FORKED); }); @@ -573,6 +591,7 @@ describe('modules/platform/github/index', () => { github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }), ).rejects.toThrow(REPOSITORY_CANNOT_FORK); }); @@ -585,6 +604,7 @@ describe('modules/platform/github/index', () => { github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }), ).rejects.toThrow(REPOSITORY_CANNOT_FORK); }); @@ -597,6 +617,7 @@ describe('modules/platform/github/index', () => { github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }), ).rejects.toThrow(REPOSITORY_CANNOT_FORK); }); @@ -613,6 +634,7 @@ describe('modules/platform/github/index', () => { github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, forkOrg: 'forked', }), ).rejects.toThrow(REPOSITORY_CANNOT_FORK); @@ -625,6 +647,7 @@ describe('modules/platform/github/index', () => { const config = await github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, forkOrg: 'forked', }); expect(config).toMatchSnapshot(); @@ -642,6 +665,7 @@ describe('modules/platform/github/index', () => { const config = await github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }); expect(config).toMatchSnapshot(); }); @@ -2568,6 +2592,7 @@ describe('modules/platform/github/index', () => { await github.initRepo({ repository: 'some/repo', forkToken: 'true', + forkCreation: true, }); }); diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts index a5894572cd52db8669d843c646c8569facb80802..b5c6948fe4430c08fc4b9cd5953ec77b4ccacb71 100644 --- a/lib/modules/platform/github/index.ts +++ b/lib/modules/platform/github/index.ts @@ -16,6 +16,7 @@ import { REPOSITORY_DISABLED, REPOSITORY_EMPTY, REPOSITORY_FORKED, + REPOSITORY_FORK_MISSING, REPOSITORY_FORK_MODE_FORKED, REPOSITORY_NOT_FOUND, REPOSITORY_RENAMED, @@ -434,6 +435,7 @@ export async function createFork( export async function initRepo({ endpoint, repository, + forkCreation, forkOrg, forkToken, renovateUsername, @@ -682,10 +684,13 @@ export async function initRepo({ } throw new ExternalHostError(err); } - } else { + } else if (forkCreation) { logger.debug('Forked repo is not found - attempting to create it'); forkedRepo = await createFork(forkToken, repository, forkOrg); config.repository = forkedRepo.full_name; + } else { + logger.debug('Forked repo is not found and forkCreation is disabled'); + throw new Error(REPOSITORY_FORK_MISSING); } } diff --git a/lib/modules/platform/github/types.ts b/lib/modules/platform/github/types.ts index f3c0e066619fada8221cb2dfb7b4bd381c596670..48c41ef007a1cec8997a16882f5f0e092e366113 100644 --- a/lib/modules/platform/github/types.ts +++ b/lib/modules/platform/github/types.ts @@ -95,6 +95,7 @@ export interface LocalRepoConfig { parentRepo: string | null; forkOrg?: string; forkToken?: string; + forkCreation?: boolean; prList: GhPr[] | null; issueList: any[] | null; mergeMethod: 'rebase' | 'squash' | 'merge'; diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts index 8a27564666d99795231d57469f9ef89112b0822c..eff914fb7d4d0c76c659cc8bab40339cc546032d 100644 --- a/lib/modules/platform/types.ts +++ b/lib/modules/platform/types.ts @@ -41,6 +41,7 @@ export interface RepoParams { repository: string; endpoint?: string; gitUrl?: GitUrlOption; + forkCreation?: boolean; forkOrg?: string; forkToken?: string; forkProcessing?: 'enabled' | 'disabled'; diff --git a/lib/workers/repository/error.spec.ts b/lib/workers/repository/error.spec.ts index 1c2a662370da8aac4582aae17e70e694950cf96e..021836c21c783668bfca9b3ec2c4779da0fe9a1a 100644 --- a/lib/workers/repository/error.spec.ts +++ b/lib/workers/repository/error.spec.ts @@ -18,6 +18,7 @@ import { REPOSITORY_DISABLED, REPOSITORY_EMPTY, REPOSITORY_FORKED, + REPOSITORY_FORK_MISSING, REPOSITORY_FORK_MODE_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, @@ -48,6 +49,7 @@ describe('workers/repository/error', () => { REPOSITORY_DISABLED, REPOSITORY_CHANGED, REPOSITORY_FORKED, + REPOSITORY_FORK_MISSING, REPOSITORY_FORK_MODE_FORKED, REPOSITORY_NO_PACKAGE_FILES, CONFIG_SECRETS_EXPOSED, diff --git a/lib/workers/repository/error.ts b/lib/workers/repository/error.ts index 6e02b093158d78b0293f35fd25a5d311b98e868b..04a75159504a4c55ee3decc28760d11a333425c6 100644 --- a/lib/workers/repository/error.ts +++ b/lib/workers/repository/error.ts @@ -21,6 +21,7 @@ import { REPOSITORY_DISABLED_BY_CONFIG, REPOSITORY_EMPTY, REPOSITORY_FORKED, + REPOSITORY_FORK_MISSING, REPOSITORY_FORK_MODE_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, @@ -110,6 +111,10 @@ export default async function handleError( logger.info('Cannot fork repository - skipping'); return err.message; } + if (err.message === REPOSITORY_FORK_MISSING) { + logger.info('Cannot find fork required for fork mode - skipping'); + return err.message; + } if (err.message === REPOSITORY_NO_PACKAGE_FILES) { logger.info('Repository has no package files - skipping'); return err.message;