From 54c8f2e588ed55376e0801e5cb048608328e255e Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Wed, 2 Dec 2020 15:01:48 +0100 Subject: [PATCH] feat: ignorePrAuthor (#7853) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- docs/usage/configuration-options.md | 7 ++++ lib/config/definitions.ts | 7 ++++ lib/platform/bitbucket-server/index.ts | 13 ++++-- lib/platform/bitbucket-server/types.ts | 2 +- lib/platform/bitbucket/index.ts | 4 +- lib/platform/bitbucket/utils.ts | 1 + lib/platform/common.ts | 1 + lib/platform/github/index.ts | 4 +- lib/platform/github/types.ts | 1 + .../gitlab/__snapshots__/index.spec.ts.snap | 20 +++++----- lib/platform/gitlab/index.spec.ts | 40 +++++-------------- lib/platform/gitlab/index.ts | 15 +++++-- 12 files changed, 65 insertions(+), 50 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 1300c46b43..e7e5d06277 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -820,6 +820,13 @@ Renovate will try to configure this to `true` also if you have configured any `n Using this setting, you can selectively ignore package files that you don't want Renovate autodiscovering. For instance if your repository has an "examples" directory of many package.json files that you don't want to be kept up to date. +## ignorePrAuthor + +This is usually needed if someone needs to migrate bot accounts, including from hosted app to self-hosted. +If `ignorePrAuthor` is configured to true, it means Renovate will fetch the entire list of repository PRs instead of optimizing to fetch only those PRs which it created itself. +You should only want to enable this if you are changing the bot account (e.g. from `@old-bot` to `@new-bot`) and want `@new-bot` to find and update any existing PRs created by `@old-bot`. +It's recommended to revert this setting once that transition period is over and all old PRs are resolved. + ## ignorePresets Use this if you are extending a complex preset but don't want to use every "sub preset" that it includes. diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts index f7cfa954f8..e8c22bb2a1 100644 --- a/lib/config/definitions.ts +++ b/lib/config/definitions.ts @@ -1867,6 +1867,13 @@ const options: RenovateOptions[] = [ type: 'boolean', default: true, }, + { + name: 'ignorePrAuthor', + description: + 'Set to true to fetch the entire list of PRs instead of only those authored by the Renovate user', + type: 'boolean', + default: false, + }, ]; export function getOptions(): RenovateOptions[] { diff --git a/lib/platform/bitbucket-server/index.ts b/lib/platform/bitbucket-server/index.ts index 23a2d6cf38..5066ac9f04 100644 --- a/lib/platform/bitbucket-server/index.ts +++ b/lib/platform/bitbucket-server/index.ts @@ -133,6 +133,7 @@ export async function initRepo({ repository, localDir, cloneSubmodules, + ignorePrAuthor, }: RepoParams): Promise<RepoResult> { logger.debug( `initRepo("${JSON.stringify({ repository, localDir }, null, 2)}")` @@ -150,6 +151,7 @@ export async function initRepo({ repository, prVersions: new Map<number, number>(), username: opts.username, + ignorePrAuthor, } as any; const { host, pathname } = url.parse(defaults.endpoint); @@ -294,11 +296,14 @@ export async function getPrList(refreshCache?: boolean): Promise<Pr[]> { logger.debug(`getPrList()`); // istanbul ignore next if (!config.prList || refreshCache) { - const query = new URLSearchParams({ + const searchParams = { state: 'ALL', - 'role.1': 'AUTHOR', - 'username.1': config.username, - }).toString(); + }; + if (!config.ignorePrAuthor) { + searchParams['role.1'] = 'AUTHOR'; + searchParams['username.1'] = config.username; + } + const query = new URLSearchParams(searchParams).toString(); const values = await utils.accumulateValues( `./rest/api/1.0/projects/${config.projectKey}/repos/${config.repositorySlug}/pull-requests?${query}` ); diff --git a/lib/platform/bitbucket-server/types.ts b/lib/platform/bitbucket-server/types.ts index bae607b8e9..d3e64ae3b3 100644 --- a/lib/platform/bitbucket-server/types.ts +++ b/lib/platform/bitbucket-server/types.ts @@ -11,7 +11,7 @@ export interface BbsConfig { repositorySlug: string; prVersions: Map<number, number>; - + ignorePrAuthor: boolean; username: string; } diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts index 7b6d8656c5..bf09e2ca7b 100644 --- a/lib/platform/bitbucket/index.ts +++ b/lib/platform/bitbucket/index.ts @@ -117,6 +117,7 @@ export async function initRepo({ repository, localDir, cloneSubmodules, + ignorePrAuthor, }: RepoParams): Promise<RepoResult> { logger.debug(`initRepo("${repository}")`); const opts = hostRules.find({ @@ -126,6 +127,7 @@ export async function initRepo({ config = { repository, username: opts.username, + ignorePrAuthor, } as utils.Config; let info: utils.RepoInfo; try { @@ -209,7 +211,7 @@ export async function getPrList(): Promise<Pr[]> { config.prList = prs .filter((pr) => { const prAuthorId = pr?.author?.uuid; - return renovateUserUuid && prAuthorId + return renovateUserUuid && prAuthorId && !config.ignorePrAuthor ? renovateUserUuid === prAuthorId : true; }) diff --git a/lib/platform/bitbucket/utils.ts b/lib/platform/bitbucket/utils.ts index 9b0cb72850..f0c9858f3e 100644 --- a/lib/platform/bitbucket/utils.ts +++ b/lib/platform/bitbucket/utils.ts @@ -15,6 +15,7 @@ export interface Config { repository: string; username: string; userUuid: string; + ignorePrAuthor: boolean; } export interface PagedResult<T = any> { diff --git a/lib/platform/common.ts b/lib/platform/common.ts index 56e23c77e3..077253973b 100644 --- a/lib/platform/common.ts +++ b/lib/platform/common.ts @@ -41,6 +41,7 @@ export interface RepoParams { includeForks?: boolean; renovateUsername?: string; cloneSubmodules?: boolean; + ignorePrAuthor?: boolean; } /** diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts index 6ea1da157f..caef151f68 100644 --- a/lib/platform/github/index.ts +++ b/lib/platform/github/index.ts @@ -169,10 +169,11 @@ export async function initRepo({ localDir, renovateUsername, cloneSubmodules, + ignorePrAuthor, }: RepoParams): Promise<RepoResult> { logger.debug(`initRepo("${repository}")`); // config is used by the platform api itself, not necessary for the app layer to know - config = { localDir, repository, cloneSubmodules } as any; + config = { localDir, repository, cloneSubmodules, ignorePrAuthor } as any; // istanbul ignore if if (endpoint) { // Necessary for Renovate Pro - do not remove @@ -687,6 +688,7 @@ export async function getPrList(): Promise<Pr[]> { .filter((pr) => { return ( config.forkMode || + config.ignorePrAuthor || (pr?.user?.login && config?.renovateUsername ? pr.user.login === config.renovateUsername : true) diff --git a/lib/platform/github/types.ts b/lib/platform/github/types.ts index 20dc963b40..ac2211bdee 100644 --- a/lib/platform/github/types.ts +++ b/lib/platform/github/types.ts @@ -73,6 +73,7 @@ export interface LocalRepoConfig { isGhe: boolean; renovateUsername: string; productLinks: any; + ignorePrAuthor: boolean; } export type BranchProtection = any; diff --git a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap index c37b40f0e9..1b18a5f283 100644 --- a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap +++ b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap @@ -854,7 +854,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -870,7 +870,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -886,7 +886,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -902,7 +902,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -918,7 +918,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -934,7 +934,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, ] `; @@ -2201,7 +2201,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, Object { "body": "{\\"title\\":\\"title\\",\\"description\\":\\"body\\",\\"state_event\\":\\"close\\"}", @@ -2253,7 +2253,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, Object { "body": "{\\"title\\":\\"Draft: title\\",\\"description\\":\\"body\\"}", @@ -2305,7 +2305,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, Object { "body": "{\\"title\\":\\"Draft: title\\",\\"description\\":\\"body\\"}", @@ -2357,7 +2357,7 @@ Array [ "user-agent": "https://github.com/renovatebot/renovate", }, "method": "GET", - "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined", + "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100", }, Object { "body": "{\\"title\\":\\"title\\",\\"description\\":\\"body\\"}", diff --git a/lib/platform/gitlab/index.spec.ts b/lib/platform/gitlab/index.spec.ts index aab658b448..eda236f6f7 100644 --- a/lib/platform/gitlab/index.spec.ts +++ b/lib/platform/gitlab/index.spec.ts @@ -887,9 +887,7 @@ describe('platform/gitlab', () => { it('returns true if no title and all state', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -907,9 +905,7 @@ describe('platform/gitlab', () => { it('returns true if not open', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -929,9 +925,7 @@ describe('platform/gitlab', () => { it('returns true if open and with title', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -952,9 +946,7 @@ describe('platform/gitlab', () => { it('returns true with title', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -973,9 +965,7 @@ describe('platform/gitlab', () => { it('returns true with draft prefix title', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -994,9 +984,7 @@ describe('platform/gitlab', () => { it('returns true with deprecated draft prefix title', async () => { httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -1283,9 +1271,7 @@ describe('platform/gitlab', () => { await initPlatform('13.3.6-ee'); httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -1303,9 +1289,7 @@ describe('platform/gitlab', () => { await initPlatform('13.3.6-ee'); httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -1323,9 +1307,7 @@ describe('platform/gitlab', () => { await initPlatform('13.3.6-ee'); httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, @@ -1343,9 +1325,7 @@ describe('platform/gitlab', () => { await initPlatform('13.3.6-ee'); httpMock .scope(gitlabApiHost) - .get( - '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined' - ) + .get('/api/v4/projects/undefined/merge_requests?per_page=100') .reply(200, [ { iid: 1, diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts index fa540e7864..94866b09e9 100644 --- a/lib/platform/gitlab/index.ts +++ b/lib/platform/gitlab/index.ts @@ -52,6 +52,7 @@ let config: { mergeMethod: MergeMethod; defaultBranch: string; cloneSubmodules: boolean; + ignorePrAuthor: boolean; } = {} as any; const defaults = { @@ -155,11 +156,13 @@ export async function initRepo({ repository, localDir, cloneSubmodules, + ignorePrAuthor, }: RepoParams): Promise<RepoResult> { config = {} as any; config.repository = urlEscape(repository); config.localDir = localDir; config.cloneSubmodules = cloneSubmodules; + config.ignorePrAuthor = ignorePrAuthor; let res: HttpResponse<RepoResponse>; try { @@ -370,10 +373,16 @@ function massagePr(prToModify: Pr): Pr { } async function fetchPrList(): Promise<Pr[]> { - const query = new URLSearchParams({ + const searchParams = { per_page: '100', - author_id: `${authorId}`, - }).toString(); + } as any; + // istanbul ignore if + if (config.ignorePrAuthor) { + // https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-requests + // default: `scope=created_by_me` + searchParams.scope = 'all'; + } + const query = new URLSearchParams(searchParams).toString(); const urlString = `projects/${config.repository}/merge_requests?${query}`; try { const res = await gitlabApi.getJson< -- GitLab