Skip to content

Commit

Permalink
feat(bitbucket): support task autocomplete (#30901)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
3 people authored Sep 21, 2024
1 parent 154dd4f commit 07169cd
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@ You can also use the special `"$default"` string to denote the repository's defa
Do _not_ use the `baseBranches` config option when you've set a `forkToken`.
You may need a `forkToken` when you're using the Forking Renovate app.

## bbAutoResolvePrTasks

Configuring this to `true` means that Renovate will mark all PR Tasks as complete.

## bbUseDefaultReviewers

Configuring this to `true` means that Renovate will detect and apply the default reviewers rules to PRs (Bitbucket only).
Expand Down
8 changes: 8 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,14 @@ const options: RenovateOptions[] = [
cli: false,
env: false,
},
{
name: 'bbAutoResolvePrTasks',
description:
'The PR tasks will be automatically completed after the PR is raised.',
type: 'boolean',
default: false,
supportedPlatforms: ['bitbucket'],
},
{
name: 'bbUseDefaultReviewers',
description: 'Use the default reviewers (Bitbucket only).',
Expand Down
152 changes: 152 additions & 0 deletions lib/modules/platform/bitbucket/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { reset as memCacheReset } from '../../../util/cache/memory';
import type * as _git from '../../../util/git';
import { setBaseUrl } from '../../../util/http/bitbucket';
import type { Platform, PlatformResult, RepoParams } from '../types';
import type { PrTask } from './schema';

jest.mock('../../../util/git');
jest.mock('../../../util/host-rules');
Expand Down Expand Up @@ -1436,6 +1437,157 @@ describe('modules/platform/bitbucket/index', () => {
}),
).rejects.toThrow(new Error('Response code 400 (Bad Request)'));
});

it('lists PR tasks and resolves the unresolved tasks', async () => {
const prTask1: PrTask = {
id: 1,
state: 'UNRESOLVED',
content: {
raw: 'task 1',
},
};
const resolvedPrTask1: Partial<PrTask> = {
state: 'RESOLVED',
content: {
raw: 'task 1',
},
};
const prTask2: PrTask = {
id: 2,
state: 'UNRESOLVED',
content: {
raw: 'task 2',
},
};
const resolvedPrTask2: Partial<PrTask> = {
state: 'RESOLVED',
content: {
raw: 'task 2',
},
};
const prTask3: PrTask = {
id: 3,
state: 'RESOLVED',
content: {
raw: 'task 3',
},
};
const scope = await initRepoMock();
scope
.post('/2.0/repositories/some/repo/pullrequests')
.reply(200, { id: 5 })
.get(`/2.0/repositories/some/repo/pullrequests`)
.query(true)
.reply(200, {
values: [{ id: 5 }],
});
scope
.get('/2.0/repositories/some/repo/pullrequests/5/tasks')
.query({
pagelen: 100,
})
.reply(200, { values: [prTask1, prTask2, prTask3] });
scope
.put(
'/2.0/repositories/some/repo/pullrequests/5/tasks/1',
resolvedPrTask1,
)
.reply(200);
scope
.put(
'/2.0/repositories/some/repo/pullrequests/5/tasks/2',
resolvedPrTask2,
)
.reply(200);
const pr = await bitbucket.createPr({
sourceBranch: 'branch',
targetBranch: 'master',
prTitle: 'title',
prBody: 'body',
platformPrOptions: {
bbUseDefaultReviewers: false,
bbAutoResolvePrTasks: true,
},
});
expect(pr?.number).toBe(5);
});

it('swallows list PR error and PR creation succeeds', async () => {
const scope = await initRepoMock();
scope
.post('/2.0/repositories/some/repo/pullrequests')
.reply(200, { id: 5 })
.get(`/2.0/repositories/some/repo/pullrequests`)
.query(true)
.reply(200, {
values: [{ id: 5 }],
});
scope
.get('/2.0/repositories/some/repo/pullrequests/5/tasks')
.query({
pagelen: 100,
})
.reply(500);
const pr = await bitbucket.createPr({
sourceBranch: 'branch',
targetBranch: 'master',
prTitle: 'title',
prBody: 'body',
platformPrOptions: {
bbUseDefaultReviewers: false,
bbAutoResolvePrTasks: true,
},
});
expect(pr?.number).toBe(5);
});

it('swallows resolve PR task error and PR creation succeeds', async () => {
const prTask1: PrTask = {
id: 1,
state: 'UNRESOLVED',
content: {
raw: 'task 1',
},
};
const resolvedPrTask1: Partial<PrTask> = {
state: 'RESOLVED',
content: {
raw: 'task 1',
},
};
const scope = await initRepoMock();
scope
.post('/2.0/repositories/some/repo/pullrequests')
.reply(200, { id: 5 })
.get(`/2.0/repositories/some/repo/pullrequests`)
.query(true)
.reply(200, {
values: [{ id: 5 }],
});
scope
.get('/2.0/repositories/some/repo/pullrequests/5/tasks')
.query({
pagelen: 100,
})
.reply(200, { values: [prTask1] });
scope
.put(
'/2.0/repositories/some/repo/pullrequests/5/tasks/1',
resolvedPrTask1,
)
.reply(500);
const pr = await bitbucket.createPr({
sourceBranch: 'branch',
targetBranch: 'master',
prTitle: 'title',
prBody: 'body',
platformPrOptions: {
bbUseDefaultReviewers: false,
bbAutoResolvePrTasks: true,
},
});
expect(pr?.number).toBe(5);
});
});

describe('getPr()', () => {
Expand Down
52 changes: 51 additions & 1 deletion lib/modules/platform/bitbucket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { smartTruncate } from '../utils/pr-body';
import { readOnlyIssueBody } from '../utils/read-only-issue-body';
import * as comments from './comments';
import { BitbucketPrCache } from './pr-cache';
import { RepoInfo, Repositories } from './schema';
import { RepoInfo, Repositories, UnresolvedPrTasks } from './schema';
import type {
Account,
BitbucketStatus,
Expand Down Expand Up @@ -925,6 +925,9 @@ export async function createPr({
renovateUserUuid,
pr,
);
if (platformPrOptions?.bbAutoResolvePrTasks) {
await autoResolvePrTasks(pr);
}
return pr;
} catch (err) /* istanbul ignore next */ {
// Try sanitizing reviewers
Expand Down Expand Up @@ -952,11 +955,58 @@ export async function createPr({
renovateUserUuid,
pr,
);
if (platformPrOptions?.bbAutoResolvePrTasks) {
await autoResolvePrTasks(pr);
}
return pr;
}
}
}

async function autoResolvePrTasks(pr: Pr): Promise<void> {
logger.debug(`Auto resolve PR tasks in #${pr.number}`);
try {
const unResolvedTasks = (
await bitbucketHttp.getJson(
`/2.0/repositories/${config.repository}/pullrequests/${pr.number}/tasks`,
{ paginate: true, pagelen: 100 },
UnresolvedPrTasks,
)
).body;

logger.trace(
{
prId: pr.number,
listTaskRes: unResolvedTasks,
},
'List PR tasks',
);

for (const task of unResolvedTasks) {
const res = await bitbucketHttp.putJson(
`/2.0/repositories/${config.repository}/pullrequests/${pr.number}/tasks/${task.id}`,
{
body: {
state: 'RESOLVED',
content: {
raw: task.content.raw,
},
},
},
);
logger.trace(
{
prId: pr.number,
updateTaskResponse: res,
},
'Put PR tasks - mark resolved',
);
}
} catch (err) {
logger.warn({ prId: pr.number, err }, 'Error resolving PR tasks');
}
}

export async function updatePr({
number: prNo,
prTitle: title,
Expand Down
20 changes: 20 additions & 0 deletions lib/modules/platform/bitbucket/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,23 @@ export const Repositories = z
values: LooseArray(RepoInfo),
})
.transform((body) => body.values);

const TaskState = z.union([z.literal('RESOLVED'), z.literal('UNRESOLVED')]);

const PrTask = z.object({
id: z.number(),
state: TaskState,
content: z.object({
raw: z.string(),
}),
});

export type PrTask = z.infer<typeof PrTask>;

export const UnresolvedPrTasks = z
.object({
values: z.array(PrTask),
})
.transform((data) =>
data.values.filter((task) => task.state === 'UNRESOLVED'),
);
1 change: 1 addition & 0 deletions lib/modules/platform/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export type PlatformPrOptions = {
automergeStrategy?: MergeStrategy;
azureWorkItemId?: number;
bbUseDefaultReviewers?: boolean;
bbAutoResolvePrTasks?: boolean;
gitLabIgnoreApprovals?: boolean;
usePlatformAutomerge?: boolean;
forkModeDisallowMaintainerEdits?: boolean;
Expand Down
1 change: 1 addition & 0 deletions lib/workers/repository/update/pr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function getPlatformPrOptions(
autoApprove: !!config.autoApprove,
automergeStrategy: config.automergeStrategy,
azureWorkItemId: config.azureWorkItemId ?? 0,
bbAutoResolvePrTasks: !!config.bbAutoResolvePrTasks,
bbUseDefaultReviewers: !!config.bbUseDefaultReviewers,
gitLabIgnoreApprovals: !!config.gitLabIgnoreApprovals,
forkModeDisallowMaintainerEdits: !!config.forkModeDisallowMaintainerEdits,
Expand Down

0 comments on commit 07169cd

Please sign in to comment.