diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index f8f559ac78e6f9..f01940c91938e8 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -1912,6 +1912,8 @@ Normally when you set `rebaseWhen=auto` Renovate rebases any branch that's behin This behavior is no longer guaranteed when you enable `platformAutomerge` because the platform might automerge a branch which is not up-to-date. For example, GitHub might automerge a Renovate branch even if it's behind the base branch at the time. +Please check platform specific docs for version requirements. + ## platformCommit Only use this option if you run Renovate as a [GitHub App](https://docs.github.com/en/developers/apps/getting-started-with-apps/about-apps). diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index a1673f325247e7..3d62755e85eafa 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -2316,6 +2316,7 @@ const options: RenovateOptions[] = [ name: 'platformAutomerge', description: `Controls if platform-native auto-merge is used.`, type: 'boolean', + supportedPlatforms: ['azure', 'gitea', 'github', 'gitlab'], default: false, }, { diff --git a/lib/modules/platform/gitea/gitea-helper.spec.ts b/lib/modules/platform/gitea/gitea-helper.spec.ts index d67aed8b1a3391..663a0505619e47 100644 --- a/lib/modules/platform/gitea/gitea-helper.spec.ts +++ b/lib/modules/platform/gitea/gitea-helper.spec.ts @@ -351,11 +351,9 @@ describe('modules/platform/gitea/gitea-helper', () => { .post(`/repos/${mockRepo.full_name}/pulls/${mockPR.number}/merge`) .reply(200); - const res = await ght.mergePR( - mockRepo.full_name, - mockPR.number, - 'rebase' - ); + const res = await ght.mergePR(mockRepo.full_name, mockPR.number, { + Do: 'rebase', + }); expect(res).toBeUndefined(); }); }); diff --git a/lib/modules/platform/gitea/gitea-helper.ts b/lib/modules/platform/gitea/gitea-helper.ts index ec257948d5ebbf..f24249de5ecfa0 100644 --- a/lib/modules/platform/gitea/gitea-helper.ts +++ b/lib/modules/platform/gitea/gitea-helper.ts @@ -175,9 +175,10 @@ export type PRSearchParams = { labels?: number[]; }; -export type PRMergeParams = { +export interface PRMergeParams { Do: PRMergeMethod; -}; + merge_when_checks_succeed?: boolean; +} export type CommentCreateParams = CommentUpdateParams; @@ -307,10 +308,9 @@ export async function closePR( export async function mergePR( repoPath: string, idx: number, - method: PRMergeMethod, + params: PRMergeParams, options?: GiteaHttpOptions ): Promise { - const params: PRMergeParams = { Do: method }; const url = `${API_PATH}/repos/${repoPath}/pulls/${idx}/merge`; await giteaHttp.postJson(url, { ...options, diff --git a/lib/modules/platform/gitea/index.md b/lib/modules/platform/gitea/index.md index 35e13836fa34d7..a9af7ebc483e35 100644 --- a/lib/modules/platform/gitea/index.md +++ b/lib/modules/platform/gitea/index.md @@ -19,6 +19,7 @@ Either the account should have full name and email address set to allow Renovate ## Unsupported platform features/concepts - **Adding reviewers to PRs not supported**: Gitea versions older than v1.14.0 do not have the required API. +- **Setting `platformAutomerge` to use platform-native automerge for PRs not supported**: Gitea versions older than v1.17.0 do not have the required API. ## Features awaiting implementation diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts index b2f7add17d88e8..46b78c215557d8 100644 --- a/lib/modules/platform/gitea/index.spec.ts +++ b/lib/modules/platform/gitea/index.spec.ts @@ -1052,6 +1052,98 @@ describe('modules/platform/gitea/index', () => { }) ).rejects.toThrow(); }); + + it('should use platform automerge', async () => { + helper.createPR.mockResolvedValueOnce(mockNewPR); + await initFakePlatform('1.17.0'); + await initFakeRepo(); + const res = await gitea.createPr({ + sourceBranch: mockNewPR.head.label, + targetBranch: 'master', + prTitle: mockNewPR.title, + prBody: mockNewPR.body, + platformOptions: { usePlatformAutomerge: true }, + }); + + expect(res).toHaveProperty('number', mockNewPR.number); + expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref); + + expect(helper.createPR).toHaveBeenCalledTimes(1); + expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, { + base: mockNewPR.base.ref, + head: mockNewPR.head.label, + title: mockNewPR.title, + body: mockNewPR.body, + labels: [], + }); + expect(helper.mergePR).toHaveBeenCalledWith( + mockRepo.full_name, + mockNewPR.number, + { + Do: 'rebase', + merge_when_checks_succeed: true, + } + ); + }); + + it('continues on platform automerge error', async () => { + helper.createPR.mockResolvedValueOnce(mockNewPR); + await initFakePlatform('1.17.0'); + await initFakeRepo(); + helper.mergePR.mockRejectedValueOnce(new Error('fake')); + const res = await gitea.createPr({ + sourceBranch: mockNewPR.head.label, + targetBranch: 'master', + prTitle: mockNewPR.title, + prBody: mockNewPR.body, + platformOptions: { usePlatformAutomerge: true }, + }); + + expect(res).toHaveProperty('number', mockNewPR.number); + expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref); + + expect(helper.createPR).toHaveBeenCalledTimes(1); + expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, { + base: mockNewPR.base.ref, + head: mockNewPR.head.label, + title: mockNewPR.title, + body: mockNewPR.body, + labels: [], + }); + expect(helper.mergePR).toHaveBeenCalledWith( + mockRepo.full_name, + mockNewPR.number, + { + Do: 'rebase', + merge_when_checks_succeed: true, + } + ); + }); + + it('continues if platform automerge is not supported', async () => { + helper.createPR.mockResolvedValueOnce(mockNewPR); + await initFakeRepo(); + const res = await gitea.createPr({ + sourceBranch: mockNewPR.head.label, + targetBranch: 'master', + prTitle: mockNewPR.title, + prBody: mockNewPR.body, + platformOptions: { usePlatformAutomerge: true }, + }); + + expect(res).toHaveProperty('number', mockNewPR.number); + expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref); + + expect(helper.createPR).toHaveBeenCalledTimes(1); + expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, { + base: mockNewPR.base.ref, + head: mockNewPR.head.label, + title: mockNewPR.title, + body: mockNewPR.body, + labels: [], + }); + expect(helper.mergePR).not.toHaveBeenCalled(); + }); }); describe('updatePr', () => { @@ -1127,11 +1219,9 @@ describe('modules/platform/gitea/index', () => { }) ).toBe(true); expect(helper.mergePR).toHaveBeenCalledTimes(1); - expect(helper.mergePR).toHaveBeenCalledWith( - mockRepo.full_name, - 1, - 'rebase' - ); + expect(helper.mergePR).toHaveBeenCalledWith(mockRepo.full_name, 1, { + Do: 'rebase', + }); }); it('should return false when merging fails', async () => { diff --git a/lib/modules/platform/gitea/index.ts b/lib/modules/platform/gitea/index.ts index 84775f81754bcc..c09f551d9efc99 100644 --- a/lib/modules/platform/gitea/index.ts +++ b/lib/modules/platform/gitea/index.ts @@ -487,6 +487,7 @@ const platform: Platform = { prTitle, prBody: rawBody, labels: labelNames, + platformOptions, draftPR, }: CreatePRConfig): Promise { let title = prTitle; @@ -510,6 +511,33 @@ const platform: Platform = { labels: labels.filter(is.number), }); + if (platformOptions?.usePlatformAutomerge) { + if (semver.gte(defaults.version, '1.17.0')) { + try { + await helper.mergePR(config.repository, gpr.number, { + // TODO: pass strategy (#16884) + Do: config.mergeMethod, + merge_when_checks_succeed: true, + }); + + logger.debug( + { prNumber: gpr.number }, + 'Gitea-native automerge: success' + ); + } catch (err) { + logger.warn( + { err, prNumber: gpr.number }, + 'Gitea-native automerge: fail' + ); + } + } else { + logger.debug( + { prNumber: gpr.number }, + 'Gitea-native automerge: not supported on this version of Gitea. Use 1.17.0 or newer.' + ); + } + } + const pr = toRenovatePR(gpr); if (!pr) { throw new Error('Can not parse newly created Pull Request'); @@ -583,11 +611,9 @@ const platform: Platform = { async mergePr({ id, strategy }: MergePRConfig): Promise { try { - await helper.mergePR( - config.repository, - id, - getMergeMethod(strategy) ?? config.mergeMethod - ); + await helper.mergePR(config.repository, id, { + Do: getMergeMethod(strategy) ?? config.mergeMethod, + }); return true; } catch (err) { logger.warn({ err, id }, 'Merging of PR failed');