diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 29b1438ed6e2543565f18d7a5fbba42a03c639bc..f8f559ac78e6f992bd1f0237a7cd2f80602eefcd 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -632,12 +632,12 @@ If you want the PRs created by Renovate to be considered as drafts rather than n } ``` -This option is evaluated at PR/MR creation time and is only supported on the following platforms: GitHub, GitLab, Azure. +This option is evaluated at PR/MR creation time. <!-- prettier-ignore --> !!! note - GitLab implements draft status by checking whether the PR's title starts with certain strings. - This means that `draftPR` on GitLab is incompatible with the legacy method of triggering Renovate to rebase a PR by renaming the PR to start with `rebase!`. + GitLab and Gitea implement draft status by checking if the PR's title starts with certain strings. + This means that `draftPR` on GitLab and Gitea are incompatible with the legacy method of triggering Renovate to rebase a PR by renaming the PR to start with `rebase!`. ## enabled diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index d3892306dafe50d65196dda54171b9e51c21a844..a1673f325247e7c8a4627bddf0962d110efa8727 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -254,7 +254,7 @@ const options: RenovateOptions[] = [ 'If set to `true` then Renovate creates draft PRs, instead of normal status PRs.', type: 'boolean', default: false, - supportedPlatforms: ['github', 'gitlab', 'azure'], + supportedPlatforms: ['azure', 'gitea', 'github', 'gitlab'], }, { name: 'dryRun', diff --git a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap b/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap index 1cf4a43dd82e577c0fbeaba7b283c0683204cd22..fb0f058c67db67261b95334f6d0b38e7e1aad226 100644 --- a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap +++ b/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap @@ -9,6 +9,7 @@ Object { "createdAt": "2014-04-01T05:14:20Z", "displayNumber": "Pull Request #42", "hasAssignees": false, + "isDraft": false, "number": 42, "sha": "0d9c7726c3d628b7e28af234595cfd20febdbf8e", "sourceBranch": "pr-branch", @@ -28,6 +29,7 @@ Object { "createdAt": "2014-04-01T05:14:20Z", "displayNumber": "Pull Request #42", "hasAssignees": false, + "isDraft": false, "number": 42, "sha": "0d9c7726c3d628b7e28af234595cfd20febdbf8e", "sourceBranch": "pr-branch", @@ -47,6 +49,7 @@ Object { "createdAt": "2015-03-22T20:36:16Z", "displayNumber": "Pull Request #1", "hasAssignees": false, + "isDraft": false, "number": 1, "sha": "some-head-sha", "sourceBranch": "some-head-branch", @@ -66,6 +69,7 @@ Object { "createdAt": "2015-03-22T20:36:16Z", "displayNumber": "Pull Request #1", "hasAssignees": false, + "isDraft": false, "number": 1, "sha": "some-head-sha", "sourceBranch": "some-head-branch", @@ -93,6 +97,7 @@ Array [ "createdAt": "2015-03-22T20:36:16Z", "displayNumber": "Pull Request #1", "hasAssignees": false, + "isDraft": false, "number": 1, "sha": "some-head-sha", "sourceBranch": "some-head-branch", @@ -109,6 +114,7 @@ Array [ "createdAt": "2011-08-18T22:30:38Z", "displayNumber": "Pull Request #2", "hasAssignees": false, + "isDraft": false, "number": 2, "sha": "other-head-sha", "sourceBranch": "other-head-branch", @@ -117,6 +123,23 @@ Array [ "targetBranch": "other-base-branch", "title": "Other PR", }, + Object { + "bodyStruct": Object { + "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4", + }, + "cannotMergeReason": undefined, + "createdAt": "2011-08-18T22:30:39Z", + "displayNumber": "Pull Request #3", + "hasAssignees": false, + "isDraft": true, + "number": 3, + "sha": "draft-head-sha", + "sourceBranch": "draft-head-branch", + "sourceRepo": "some/repo", + "state": "open", + "targetBranch": "draft-base-branch", + "title": "Draft PR", + }, ] `; @@ -130,6 +153,7 @@ Array [ "createdAt": "2015-03-22T20:36:16Z", "displayNumber": "Pull Request #1", "hasAssignees": false, + "isDraft": false, "number": 1, "sha": "some-head-sha", "sourceBranch": "some-head-branch", @@ -146,6 +170,7 @@ Array [ "createdAt": "2011-08-18T22:30:38Z", "displayNumber": "Pull Request #2", "hasAssignees": false, + "isDraft": false, "number": 2, "sha": "other-head-sha", "sourceBranch": "other-head-branch", @@ -154,6 +179,23 @@ Array [ "targetBranch": "other-base-branch", "title": "Other PR", }, + Object { + "bodyStruct": Object { + "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4", + }, + "cannotMergeReason": undefined, + "createdAt": "2011-08-18T22:30:39Z", + "displayNumber": "Pull Request #3", + "hasAssignees": false, + "isDraft": true, + "number": 3, + "sha": "draft-head-sha", + "sourceBranch": "draft-head-branch", + "sourceRepo": "some/repo", + "state": "open", + "targetBranch": "draft-base-branch", + "title": "Draft PR", + }, ] `; diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts index 299b39a0db9f6fae91d3354613d87f06ae89c24d..b2f7add17d88e8032f22f76cad7d67d8a8a316de 100644 --- a/lib/modules/platform/gitea/index.spec.ts +++ b/lib/modules/platform/gitea/index.spec.ts @@ -97,6 +97,22 @@ describe('modules/platform/gitea/index', () => { repo: partial<ght.Repo>({ full_name: mockRepo.full_name }), }, }), + partial<MockPr>({ + number: 3, + title: 'WIP: Draft PR', + body: 'other random pull request', + state: PrState.Open, + diff_url: 'https://gitea.renovatebot.com/some/repo/pulls/3.diff', + created_at: '2011-08-18T22:30:39Z', + closed_at: '2016-01-09T10:03:22Z', + mergeable: true, + base: { ref: 'draft-base-branch' }, + head: { + label: 'draft-head-branch', + sha: 'draft-head-sha', + repo: partial<ght.Repo>({ full_name: mockRepo.full_name }), + }, + }), ]; const mockIssues: ght.Issue[] = [ @@ -848,6 +864,21 @@ describe('modules/platform/gitea/index', () => { expect(res).toHaveProperty('state', mockPR.state); }); + it('should find pull request with draft', async () => { + const mockPR = mockPRs[2]; + helper.searchPRs.mockResolvedValueOnce(mockPRs); + await initFakeRepo(); + + const res = await gitea.findPr({ + branchName: mockPR.head.label, + prTitle: 'Draft PR', + state: mockPR.state, + }); + expect(res).toHaveProperty('sourceBranch', mockPR.head.label); + expect(res).toHaveProperty('title', 'Draft PR'); + expect(res).toHaveProperty('state', mockPR.state); + }); + it('should return null for missing pull request', async () => { helper.searchPRs.mockResolvedValueOnce(mockPRs); await initFakeRepo(); @@ -912,6 +943,7 @@ describe('modules/platform/gitea/index', () => { targetBranch: 'master', prTitle: mockNewPR.title, prBody: mockNewPR.body, + draftPR: true, }); expect(res).toHaveProperty('number', mockNewPR.number); @@ -921,7 +953,7 @@ describe('modules/platform/gitea/index', () => { expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, { base: mockNewPR.base.ref, head: mockNewPR.head.label, - title: mockNewPR.title, + title: `WIP: ${mockNewPR.title}`, body: mockNewPR.body, labels: [], }); @@ -1024,6 +1056,7 @@ describe('modules/platform/gitea/index', () => { describe('updatePr', () => { it('should update pull request with title', async () => { + helper.searchPRs.mockResolvedValueOnce(mockPRs); await initFakeRepo(); await gitea.updatePr({ number: 1, prTitle: 'New Title' }); @@ -1034,6 +1067,7 @@ describe('modules/platform/gitea/index', () => { }); it('should update pull request with title and body', async () => { + helper.searchPRs.mockResolvedValueOnce(mockPRs); await initFakeRepo(); await gitea.updatePr({ number: 1, @@ -1048,7 +1082,24 @@ describe('modules/platform/gitea/index', () => { }); }); + it('should update pull request with draft', async () => { + helper.searchPRs.mockResolvedValueOnce(mockPRs); + await initFakeRepo(); + await gitea.updatePr({ + number: 3, + prTitle: 'New Title', + prBody: 'New Body', + }); + + expect(helper.updatePR).toHaveBeenCalledTimes(1); + expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 3, { + title: 'WIP: New Title', + body: 'New Body', + }); + }); + it('should close pull request', async () => { + helper.searchPRs.mockResolvedValueOnce(mockPRs); await initFakeRepo(); await gitea.updatePr({ number: 1, diff --git a/lib/modules/platform/gitea/index.ts b/lib/modules/platform/gitea/index.ts index 7aaed4f9a56b2240a749c0c20f882eb412ecd770..84775f81754bccf6cd4fb7bdf9d600c4955f7230 100644 --- a/lib/modules/platform/gitea/index.ts +++ b/lib/modules/platform/gitea/index.ts @@ -54,6 +54,8 @@ interface GiteaRepoConfig { cloneSubmodules: boolean; } +const DRAFT_PREFIX = 'WIP: '; + const defaults = { hostType: PlatformId.Gitea, endpoint: 'https://gitea.com/', @@ -96,11 +98,19 @@ function toRenovatePR(data: helper.PR): Pr | null { return null; } + let title = data.title; + let isDraft = false; + if (title.startsWith(DRAFT_PREFIX)) { + title = title.substring(DRAFT_PREFIX.length); + isDraft = true; + } + return { number: data.number, displayNumber: `Pull Request #${data.number}`, state: data.state, - title: data.title, + title, + isDraft, bodyStruct: getPrBodyStruct(data.body), sha: data.head.sha, sourceBranch: data.head.label, @@ -474,13 +484,18 @@ const platform: Platform = { async createPr({ sourceBranch, targetBranch, - prTitle: title, + prTitle, prBody: rawBody, labels: labelNames, + draftPR, }: CreatePRConfig): Promise<Pr> { + let title = prTitle; const base = targetBranch; const head = sourceBranch; const body = sanitize(rawBody); + if (draftPR) { + title = DRAFT_PREFIX + title; + } logger.debug(`Creating pull request: ${title} (${head} => ${base})`); try { @@ -550,10 +565,15 @@ const platform: Platform = { async updatePr({ number, - prTitle: title, + prTitle, prBody: body, state, }: UpdatePrConfig): Promise<void> { + let title = prTitle; + if ((await getPrList()).find((pr) => pr.number === number)?.isDraft) { + title = DRAFT_PREFIX + title; + } + await helper.updatePR(config.repository, number, { title, ...(body && { body }),