Skip to content

Commit 62b51ca

Browse files
Copilotalexr00
andcommitted
Add Ready & Merge button for Copilot PRs
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent 3c546ff commit 62b51ca

3 files changed

Lines changed: 56 additions & 5 deletions

File tree

src/github/pullRequestOverview.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
364364
return this.deleteBranch(message);
365365
case 'pr.readyForReview':
366366
return this.setReadyForReview(message);
367+
case 'pr.readyForReviewAndMerge':
368+
return this.setReadyForReviewAndMerge(message);
367369
case 'pr.approve':
368370
return this.approvePullRequestMessage(message);
369371
case 'pr.request-changes':
@@ -649,6 +651,25 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
649651
});
650652
}
651653

654+
private async setReadyForReviewAndMerge(message: IRequestMessage<{ mergeMethod: MergeMethod }>): Promise<void> {
655+
try {
656+
// Step 1: Mark as ready for review
657+
const readyResult = await this._item.setReadyForReview();
658+
659+
// Step 2: Approve the PR
660+
await this._item.approve(this._folderRepositoryManager.repository, '');
661+
662+
// Step 3: Enable auto-merge
663+
await this._item.enableAutoMerge(message.args.mergeMethod);
664+
665+
// Return the ready result to update the UI
666+
this._replyMessage(message, readyResult);
667+
} catch (e) {
668+
vscode.window.showErrorMessage(`Unable to mark ready for review and merge. ${formatError(e)}`);
669+
this._throwError(message, '');
670+
}
671+
}
672+
652673
private async checkoutDefaultBranch(message: IRequestMessage<string>): Promise<void> {
653674
try {
654675
const prBranch = this._folderRepositoryManager.repository.state.HEAD?.name;

webviews/common/context.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ export class PRContext {
8989

9090
public readyForReview = (): Promise<ReadyForReview> => this.postMessage({ command: 'pr.readyForReview' });
9191

92+
public readyForReviewAndMerge = (args: { mergeMethod: MergeMethod }): Promise<ReadyForReview> => this.postMessage({ command: 'pr.readyForReviewAndMerge', args });
93+
9294
public addReviewers = () => this.postMessage({ command: 'pr.change-reviewers' });
9395
public changeProjects = (): Promise<ProjectItemsReply> => this.postMessage({ command: 'pr.change-projects' });
9496
public removeProject = (project: IProjectItem) => this.postMessage({ command: 'pr.remove-project', args: project });

webviews/components/merge.tsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,10 @@ export const OfferToUpdate = ({ mergeable, isSimple, isCurrentlyCheckedOut, canU
281281

282282
};
283283

284-
export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => {
284+
export const ReadyForReview = ({ isSimple, showMergeButton, mergeMethod }: { isSimple: boolean; showMergeButton?: boolean; mergeMethod?: MergeMethod }) => {
285285
const [isBusy, setBusy] = useState(false);
286-
const { readyForReview, updatePR } = useContext(PullRequestContext);
286+
const [isMergeBusy, setMergeBusy] = useState(false);
287+
const { readyForReview, readyForReviewAndMerge, updatePR } = useContext(PullRequestContext);
287288

288289
const markReadyForReview = useCallback(async () => {
289290
try {
@@ -295,6 +296,16 @@ export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => {
295296
}
296297
}, [setBusy, readyForReview, updatePR]);
297298

299+
const markReadyAndMerge = useCallback(async () => {
300+
try {
301+
setMergeBusy(true);
302+
const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod || 'squash' });
303+
updatePR(result);
304+
} finally {
305+
setMergeBusy(false);
306+
}
307+
}, [setMergeBusy, readyForReviewAndMerge, updatePR, mergeMethod]);
308+
298309
return (
299310
<div className="ready-for-review-container">
300311
<div className='ready-for-review-text-wrapper'>
@@ -305,7 +316,18 @@ export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => {
305316
</div>
306317
</div>
307318
<div className='button-container'>
308-
<button disabled={isBusy} onClick={markReadyForReview}>Ready for Review</button>
319+
<button disabled={isBusy || isMergeBusy} onClick={markReadyForReview}>Ready for Review</button>
320+
{showMergeButton && (
321+
<button
322+
className="secondary"
323+
disabled={isBusy || isMergeBusy}
324+
onClick={markReadyAndMerge}
325+
title="Mark as ready for review, approve, and enable auto-merge with squash"
326+
aria-label="Ready for Review, Approve, and Auto-Merge"
327+
>
328+
Ready &amp; Merge
329+
</button>
330+
)}
309331
</div>
310332
</div>
311333
);
@@ -338,10 +360,16 @@ export const Merge = (pr: PullRequest) => {
338360
};
339361

340362
export const PrActions = ({ pr, isSimple }: { pr: PullRequest; isSimple: boolean }) => {
341-
const { hasWritePermission, canEdit, isDraft, mergeable } = pr;
363+
const { hasWritePermission, canEdit, isDraft, mergeable, isCopilotOnMyBehalf, mergeMethodsAvailability } = pr;
342364
if (isDraft) {
343365
// Only PR author and users with push rights can mark draft as ready for review
344-
return canEdit ? <ReadyForReview isSimple={isSimple} /> : null;
366+
if (!canEdit) {
367+
return null;
368+
}
369+
370+
// For Copilot-created PRs, show the "Ready & Merge" button alongside the regular button
371+
const showMergeButton = isCopilotOnMyBehalf && mergeMethodsAvailability.squash;
372+
return <ReadyForReview isSimple={isSimple} showMergeButton={showMergeButton} mergeMethod="squash" />;
345373
}
346374

347375
if (mergeable === PullRequestMergeability.Mergeable && hasWritePermission && !pr.mergeQueueEntry) {

0 commit comments

Comments
 (0)