From 2caf0b304bc9cd9b9ff2dd913e08558c4071efe6 Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Sat, 2 Mar 2024 08:16:07 +0100 Subject: [PATCH] fix(github): skip forked repos when in fork mode (#27684) --- docs/usage/self-hosted-configuration.md | 4 ++++ lib/constants/error-messages.ts | 1 + lib/modules/platform/github/graphql.ts | 3 +++ lib/modules/platform/github/index.spec.ts | 26 ++++++++++++++++++----- lib/modules/platform/github/index.ts | 13 ++++++++++++ lib/modules/platform/github/types.ts | 3 +++ lib/util/git/index.ts | 2 +- lib/workers/repository/error.spec.ts | 2 ++ lib/workers/repository/error.ts | 7 ++++++ lib/workers/repository/result.ts | 2 ++ 10 files changed, 57 insertions(+), 6 deletions(-) diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index f4e388e6f0..57e7e33536 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -577,6 +577,10 @@ If this value is configured then Renovate: Renovate will then create branches on the fork and opens Pull Requests on the parent repository. +<!-- prettier-ignore --> +!!! note + Forked repositories will always be skipped when `forkToken` is set, even if `includeForks` is true. + ## gitNoVerify Controls when Renovate passes the `--no-verify` flag to `git`. diff --git a/lib/constants/error-messages.ts b/lib/constants/error-messages.ts index 95daa453ee..490e963733 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_MODE_FORKED = 'fork-mode-forked'; export const REPOSITORY_FORKED = 'fork'; export const REPOSITORY_MIRRORED = 'mirror'; export const REPOSITORY_NOT_FOUND = 'not-found'; diff --git a/lib/modules/platform/github/graphql.ts b/lib/modules/platform/github/graphql.ts index 09f0addcc0..8f96b4f4fb 100644 --- a/lib/modules/platform/github/graphql.ts +++ b/lib/modules/platform/github/graphql.ts @@ -3,6 +3,9 @@ query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { id isFork + parent { + nameWithOwner + } isArchived nameWithOwner hasIssuesEnabled diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts index 11b8efe42f..1f7e15ef8f 100644 --- a/lib/modules/platform/github/index.spec.ts +++ b/lib/modules/platform/github/index.spec.ts @@ -7,6 +7,7 @@ import { PLATFORM_RATE_LIMIT_EXCEEDED, PLATFORM_UNKNOWN_ERROR, REPOSITORY_CANNOT_FORK, + REPOSITORY_FORKED, REPOSITORY_NOT_FOUND, REPOSITORY_RENAMED, } from '../../../constants/error-messages'; @@ -483,6 +484,7 @@ describe('modules/platform/github/index', () => { forkExisted: boolean, forkResult = 200, forkDefaultBranch = 'master', + isFork = false, ): void { scope // repo info @@ -490,7 +492,7 @@ describe('modules/platform/github/index', () => { .reply(200, { data: { repository: { - isFork: false, + isFork, isArchived: false, nameWithOwner: repository, hasIssuesEnabled: true, @@ -505,10 +507,10 @@ describe('modules/platform/github/index', () => { }, }, }, - }) - // getForks - .get(`/repos/${repository}/forks?per_page=100`) - .reply( + }); + + if (!isFork) { + scope.get(`/repos/${repository}/forks?per_page=100`).reply( forkResult, forkExisted ? [ @@ -520,6 +522,7 @@ describe('modules/platform/github/index', () => { ] : [], ); + } } describe('initRepo', () => { @@ -547,6 +550,19 @@ describe('modules/platform/github/index', () => { expect(config).toMatchSnapshot(); }); + it('throws if the repo is a fork', async () => { + const repo = 'some/repo'; + const branch = 'master'; + const scope = httpMock.scope(githubApiHost); + forkInitRepoMock(scope, repo, false, 200, branch, true); + await expect( + github.initRepo({ + repository: 'some/repo', + forkToken: 'true', + }), + ).rejects.toThrow(REPOSITORY_FORKED); + }); + it('throws when cannot fork due to username error', async () => { const repo = 'some/repo'; const branch = 'master'; diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts index c34abf4abc..a5894572cd 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_MODE_FORKED, REPOSITORY_NOT_FOUND, REPOSITORY_RENAMED, } from '../../../constants/error-messages'; @@ -570,6 +571,9 @@ export async function initRepo({ if (err.message.startsWith('Repository access blocked')) { throw new Error(REPOSITORY_BLOCKED); } + if (err.message === REPOSITORY_FORK_MODE_FORKED) { + throw err; + } if (err.message === REPOSITORY_FORKED) { throw err; } @@ -588,6 +592,15 @@ export async function initRepo({ if (forkToken) { logger.debug('Bot is in fork mode'); + if (repo.isFork) { + logger.debug( + `Forked repos cannot be processed when running with a forkToken, so this repo will be skipped`, + ); + logger.debug( + `Parent repo for this forked repo is ${repo.parent?.nameWithOwner}`, + ); + throw new Error(REPOSITORY_FORKED); + } config.forkOrg = forkOrg; config.forkToken = forkToken; // save parent name then delete diff --git a/lib/modules/platform/github/types.ts b/lib/modules/platform/github/types.ts index cd6feee577..f3c0e06661 100644 --- a/lib/modules/platform/github/types.ts +++ b/lib/modules/platform/github/types.ts @@ -115,6 +115,9 @@ export type BranchProtection = any; export interface GhRepo { id: string; isFork: boolean; + parent?: { + nameWithOwner: string; + }; isArchived: boolean; nameWithOwner: string; autoMergeAllowed: boolean; diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 0cad95457e..5900222c07 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -585,7 +585,7 @@ export async function getFileList(): Promise<string[]> { } export function getBranchList(): string[] { - return Object.keys(config.branchCommits); + return Object.keys(config.branchCommits ?? /* istanbul ignore next */ {}); } export async function isBranchBehindBase( diff --git a/lib/workers/repository/error.spec.ts b/lib/workers/repository/error.spec.ts index 6b2ed7a118..1c2a662370 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_MODE_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, REPOSITORY_NO_PACKAGE_FILES, @@ -47,6 +48,7 @@ describe('workers/repository/error', () => { REPOSITORY_DISABLED, REPOSITORY_CHANGED, REPOSITORY_FORKED, + REPOSITORY_FORK_MODE_FORKED, REPOSITORY_NO_PACKAGE_FILES, CONFIG_SECRETS_EXPOSED, CONFIG_VALIDATION, diff --git a/lib/workers/repository/error.ts b/lib/workers/repository/error.ts index 9433458deb..6e02b09315 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_MODE_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, REPOSITORY_NO_CONFIG, @@ -93,6 +94,12 @@ export default async function handleError( logger.error('Repository is not found'); return err.message; } + if (err.message === REPOSITORY_FORK_MODE_FORKED) { + logger.info( + 'Repository is a fork and cannot be processed when Renovate is running in fork mode itself', + ); + return err.message; + } if (err.message === REPOSITORY_FORKED) { logger.info( 'Repository is a fork and not manually configured - skipping - did you want to run with --fork-processing=enabled?', diff --git a/lib/workers/repository/result.ts b/lib/workers/repository/result.ts index 8382967188..70ba5f0c57 100644 --- a/lib/workers/repository/result.ts +++ b/lib/workers/repository/result.ts @@ -12,6 +12,7 @@ import { REPOSITORY_DISABLED_BY_CONFIG, REPOSITORY_EMPTY, REPOSITORY_FORKED, + REPOSITORY_FORK_MODE_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, REPOSITORY_NO_CONFIG, @@ -47,6 +48,7 @@ export function processResult( REPOSITORY_DISABLED, REPOSITORY_DISABLED_BY_CONFIG, REPOSITORY_EMPTY, + REPOSITORY_FORK_MODE_FORKED, REPOSITORY_FORKED, REPOSITORY_MIRRORED, REPOSITORY_NOT_FOUND, -- GitLab