Skip to content

Commit 0bb0b93

Browse files
committed
Correctly handle draft status when editing an existing pull request
- Keep current draft status by default - When draft status changes, use gh pr ready (--undo) to update it Fixes #175
1 parent 3d3a61a commit 0bb0b93

File tree

3 files changed

+39
-41
lines changed

3 files changed

+39
-41
lines changed

commands/pick/pick-command.ts

+9-14
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { confirmSettings } from './steps/confirm-settings.ts';
22
import { runCherryPick } from './steps/git-pick.ts';
33
import * as log from '@std/log';
44
import { Git } from '../../lib/git/git.ts';
5-
import { GH } from '../../lib/github/gh.ts';
5+
import { GH, type PullRequest } from '../../lib/github/gh.ts';
66
import { Gum } from '../../lib/gum/gum.ts';
77
import {
88
assertValidBranchName,
@@ -113,10 +113,10 @@ export const pickCommand = new Command()
113113
await assertValidBranchName(branchName);
114114

115115
const remoteBranchExists = await Git.doesBranchExist(`${options.pushRemote}/${branchName}`);
116-
let doesBranchHavePullRequest: Promise<boolean> | undefined;
116+
let existingPullRequestPromise: Promise<PullRequest | null> | null = null;
117117
if (options.pr && remoteBranchExists) {
118118
// Start this check early to save some waiting time
119-
doesBranchHavePullRequest = GH.doesBranchHavePullRequest(branchName);
119+
existingPullRequestPromise = GH.getPullRequestInfoForBranch(branchName);
120120
}
121121

122122
let overwriteLocalBranch = !!options.force;
@@ -142,23 +142,18 @@ export const pickCommand = new Command()
142142
log.debug(`Generated pull request body:\n${body}`);
143143

144144
// This promise is only set if it should be checked, undefined otherwise (await undefined = undefined)
145-
const updatePR = await doesBranchHavePullRequest ?? false;
146-
if (updatePR) {
145+
const existingPR = await existingPullRequestPromise;
146+
if (existingPR) {
147147
log.info('PR exists, updating it!');
148-
const currentPR = await GH.getPullRequestInfoForBranch(branchName);
149-
if (!currentPR) {
150-
log.warn('Unable to retrieve info for existing PR');
151-
} else {
152-
body = replacePRCLIPartOfBody(currentPR.body, body);
153-
}
148+
body = replacePRCLIPartOfBody(existingPR.body, body);
154149
}
155150

156151
const settings = await confirmSettings(
157152
{
158153
push: options.push,
159154
pr: options.pr,
160-
updatePR,
161-
draftPR: options.draft ?? false,
155+
updatePR: !!existingPR,
156+
draftPR: options.draft ?? existingPR?.isDraft ?? false,
162157
pullRemote: options.pullRemote,
163158
pushRemote: options.pushRemote,
164159
overwriteLocalBranch,
@@ -173,7 +168,7 @@ export const pickCommand = new Command()
173168
);
174169

175170
log.info('Go time!');
176-
await runCherryPick(settings);
171+
await runCherryPick(settings, { existingPR });
177172

178173
log.info(colors.bgGreen.brightWhite('✔ Done!'));
179174
});

commands/pick/steps/git-pick.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { colors } from '@cliffy/ansi/colors';
22
import type { Commit } from '../../../lib/git/commit.ts';
33
import { Git } from '../../../lib/git/git.ts';
4-
import { GH } from '../../../lib/github/gh.ts';
4+
import { GH, type PullRequest } from '../../../lib/github/gh.ts';
55
import { runQuietly } from '../../../lib/shell/shell.ts';
66
import { sleep } from '../../../lib/sleep.ts';
77
import * as log from '@std/log';
@@ -27,7 +27,10 @@ export type GitPickSettings = Readonly<{
2727
body: string;
2828
}>;
2929

30-
export async function runCherryPick(settings: GitPickSettings): Promise<void> {
30+
export async function runCherryPick(
31+
settings: GitPickSettings,
32+
context: { existingPR: PullRequest | null },
33+
): Promise<void> {
3134
const cleanupSteps: Array<{ message: string; action: () => unknown | Promise<unknown> }> = [];
3235

3336
try {
@@ -83,6 +86,9 @@ export async function runCherryPick(settings: GitPickSettings): Promise<void> {
8386

8487
log.info(colors.green('▶️ Updating pull request'));
8588
await GH.editPullRequest(prSettings);
89+
if (!context.existingPR || context.existingPR.isDraft !== prSettings.draftPR) {
90+
await GH.setPullRequestDraftStatus(prSettings.draftPR);
91+
}
8692
} else {
8793
log.info(colors.green('▶️ Creating pull request'));
8894
await GH.createPullRequest(prSettings);

lib/github/gh.ts

+22-25
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import { runAndCapture, runCommand } from '../shell/shell.ts';
44
export const GH = {
55
createPullRequest,
66
editPullRequest,
7-
doesBranchHavePullRequest,
7+
setPullRequestDraftStatus,
88
listPullRequests,
99
getPullRequestInfoForCurrentBranch,
1010
getPullRequestInfoForBranch,
1111
};
1212

1313
interface BasePROptions {
1414
baseBranch?: string;
15-
draftPR?: boolean;
1615
}
1716

1817
interface CreatePROptions {
1918
web?: boolean;
19+
draftPR?: boolean;
2020
}
2121

2222
interface ManualTitleAndBody {
@@ -37,6 +37,20 @@ type PullRequestOptions =
3737
& (ManualTitleAndBody | AutomaticTitleAndBody);
3838
type EditPullRequestOptions = BasePROptions & ManualTitleAndBody;
3939

40+
export interface PullRequest {
41+
title: string;
42+
body: string;
43+
number: number;
44+
isDraft: boolean;
45+
baseRefName: string;
46+
headRefName: string;
47+
commits: Array<{
48+
messageHeadLine: string;
49+
messageBody: string;
50+
oid: string; // 464fc5a0d27f6053647cdb5939a936b91aaa91fc
51+
}>;
52+
}
53+
4054
async function getPullRequestInfoForCurrentBranch() {
4155
const json = await runAndCapture(
4256
'gh',
@@ -79,7 +93,6 @@ async function createPullRequest(options: PullRequestOptions) {
7993
async function editPullRequest(options: EditPullRequestOptions) {
8094
const args: string[] = [];
8195
options.baseBranch && args.push('--base', options.baseBranch);
82-
options.draftPR && args.push('--draft');
8396

8497
await runCommand(
8598
'gh',
@@ -95,19 +108,14 @@ async function editPullRequest(options: EditPullRequestOptions) {
95108
);
96109
}
97110

98-
async function doesBranchHavePullRequest(branch: string): Promise<boolean> {
99-
try {
100-
// gh pr view doesn't work for multi-remote use cases, gh pr list does
101-
// This will give a false positive for prs from a different repository with the same branch name
102-
const prs = await listPullRequests({ head: branch });
103-
return prs.length > 0;
104-
} catch {
105-
return false;
106-
}
111+
async function setPullRequestDraftStatus(draft: boolean) {
112+
const args = draft ? ['--undo'] : [];
113+
await runCommand('gh', 'pr', 'ready', ...args);
107114
}
108115

109116
async function getPullRequestInfoForBranch(branch: string) {
110117
// gh pr view doesn't work for multi-remote use cases, gh pr list does
118+
// This will give a false positive for prs from a different repository with the same branch name
111119
const prs = await listPullRequests({ head: branch });
112120
if (prs.length < 1) {
113121
return null;
@@ -135,20 +143,9 @@ async function listPullRequests(options: {
135143
'list',
136144
...args,
137145
'--json',
138-
'title,body,number,commits,headRefName,baseRefName',
146+
'title,body,number,isDraft,commits,headRefName,baseRefName',
139147
);
140-
const prs: Array<{
141-
title: string;
142-
body: string;
143-
number: number;
144-
baseRefName: string;
145-
headRefName: string;
146-
commits: Array<{
147-
messageHeadLine: string;
148-
messageBody: string;
149-
oid: string; // 464fc5a0d27f6053647cdb5939a936b91aaa91fc
150-
}>;
151-
}> = JSON.parse(json);
148+
const prs: Array<PullRequest> = JSON.parse(json);
152149

153150
if (!Array.isArray(prs)) {
154151
log.error('Invalid response from gh cli:', prs);

0 commit comments

Comments
 (0)