Skip to content

Commit 4ebb897

Browse files
Adds support for merging Azure DevOps PRs
1 parent 11dd873 commit 4ebb897

File tree

4 files changed

+48
-12
lines changed

4 files changed

+48
-12
lines changed

src/plus/integrations/providers/azureDevOps.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AuthenticationSession, CancellationToken } from 'vscode';
2+
import { window } from 'vscode';
23
import { HostingIntegrationId } from '../../../constants.integrations';
34
import type { Account } from '../../../git/models/author';
45
import type { DefaultBranch } from '../../../git/models/defaultBranch';
@@ -194,13 +195,31 @@ export class AzureDevOpsIntegration extends HostingIntegration<
194195
}
195196

196197
protected override async mergeProviderPullRequest(
197-
_session: AuthenticationSession,
198-
_pr: PullRequest,
199-
_options?: {
198+
{ accessToken }: AuthenticationSession,
199+
pr: PullRequest,
200+
options?: {
200201
mergeMethod?: PullRequestMergeMethod;
201202
},
202203
): Promise<boolean> {
203-
return Promise.resolve(false);
204+
const api = await this.getProvidersApi();
205+
if (pr.refs == null || pr.project == null) return false;
206+
207+
try {
208+
const merged = await api.mergePullRequest(this.id, pr, {
209+
...options,
210+
accessToken: accessToken,
211+
});
212+
return merged;
213+
} catch (ex) {
214+
void this.showMergeErrorMessage(ex);
215+
return false;
216+
}
217+
}
218+
219+
private async showMergeErrorMessage(ex: Error) {
220+
await window.showErrorMessage(
221+
`${ex.message}. Check branch policies, and ensure you have the necessary permissions to merge the pull request.`,
222+
);
204223
}
205224

206225
protected override async getProviderAccountForCommit(

src/plus/integrations/providers/models.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
AzureDevOps,
66
AzureOrganization,
77
AzureProject,
8+
AzureSetPullRequestInput,
89
Bitbucket,
910
EnterpriseOptions,
1011
GetRepoInput,
@@ -264,6 +265,21 @@ export type MergePullRequestFn =
264265
mergeStrategy?: GitMergeStrategy.Squash;
265266
},
266267
options?: EnterpriseOptions,
268+
) => Promise<void>)
269+
| ((
270+
input: {
271+
pullRequest: {
272+
headRef: {
273+
oid: string;
274+
};
275+
} & AzureSetPullRequestInput;
276+
mergeStrategy?:
277+
| GitMergeStrategy.MergeCommit
278+
| GitMergeStrategy.Rebase
279+
| GitMergeStrategy.RebaseThenMergeCommit
280+
| GitMergeStrategy.Squash;
281+
},
282+
options?: EnterpriseOptions,
267283
) => Promise<void>);
268284

269285
export type GetIssueFn = (

src/plus/integrations/providers/providersApi.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export class ProvidersApi {
233233
getReposForAzureProjectFn: providerApis.azureDevOps.getReposForAzureProject.bind(
234234
providerApis.azureDevOps,
235235
) as GetReposForAzureProjectFn,
236+
mergePullRequestFn: providerApis.azureDevOps.mergePullRequest.bind(providerApis.azureDevOps),
236237
},
237238
[IssueIntegrationId.Jira]: {
238239
...providersMetadata[IssueIntegrationId.Jira],
@@ -729,6 +730,10 @@ export class ProvidersApi {
729730
const headRef = pr.refs?.head;
730731
if (headRef == null) return false;
731732

733+
if (provider.id === HostingIntegrationId.AzureDevOps && pr.project == null) {
734+
return false;
735+
}
736+
732737
try {
733738
await provider.mergePullRequestFn?.(
734739
{
@@ -739,6 +744,7 @@ export class ProvidersApi {
739744
repository: {
740745
id: pr.repository.repo,
741746
name: pr.repository.repo,
747+
project: pr.project?.name ?? '',
742748
owner: {
743749
login: pr.repository.owner,
744750
},

src/plus/launchpad/launchpadProvider.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ import type { ConnectionStateChangeEvent } from '../integrations/integrationServ
4646
import { isMaybeGitHubPullRequestUrl } from '../integrations/providers/github/github.utils';
4747
import { isMaybeGitLabPullRequestUrl } from '../integrations/providers/gitlab/gitlab.utils';
4848
import type { EnrichablePullRequest, ProviderActionablePullRequest } from '../integrations/providers/models';
49-
import {
50-
fromProviderPullRequest,
51-
getActionablePullRequests,
52-
toProviderPullRequestWithUniqueId,
53-
} from '../integrations/providers/models';
49+
import { getActionablePullRequests, toProviderPullRequestWithUniqueId } from '../integrations/providers/models';
5450
import {
5551
convertIntegrationIdToEnrichProvider,
5652
convertRemoteProviderIdToEnrichProvider,
@@ -406,7 +402,7 @@ export class LaunchpadProvider implements Disposable {
406402

407403
@log<LaunchpadProvider['merge']>({ args: { 0: i => `${i.id} (${i.provider.name} ${i.type})` } })
408404
async merge(item: LaunchpadItem): Promise<void> {
409-
if (item.graphQLId == null || item.headRef?.oid == null) return;
405+
if (item.headRef?.oid == null) return;
410406
const integrationId = item.provider.id;
411407
if (!isSupportedLaunchpadIntegrationId(integrationId)) return;
412408
const confirm = await window.showQuickPick(['Merge', 'Cancel'], {
@@ -418,8 +414,7 @@ export class LaunchpadProvider implements Disposable {
418414
const integration = await this.container.integrations.get(integrationId);
419415
if (integration == null) return;
420416

421-
const pr: PullRequest = fromProviderPullRequest(item, integration);
422-
await integration.mergePullRequest(pr);
417+
await integration.mergePullRequest(item.underlyingPullRequest);
423418
this.refresh();
424419
}
425420

0 commit comments

Comments
 (0)