Skip to content

Commit c4d183c

Browse files
committed
open prs as draft by default
1 parent 3099f20 commit c4d183c

11 files changed

Lines changed: 343 additions & 32 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,15 +358,15 @@ When creating new PRs, you will be prompted to enter a title for each one. Press
358358
| Flag | Description |
359359
|------|-------------|
360360
| `--auto` | Use auto-generated PR titles without prompting |
361-
| `--draft` | Create new PRs as drafts |
361+
| `--open` | Mark new and existing PRs as ready for review |
362362
| `--remote <name>` | Remote to push to (defaults to auto-detected remote) |
363363

364364
**Examples:**
365365

366366
```sh
367367
gh stack submit
368368
gh stack submit --auto
369-
gh stack submit --draft
369+
gh stack submit --open
370370
```
371371

372372
### `gh stack link`
@@ -386,7 +386,7 @@ If the PRs are not yet in a stack, a new stack is created. If some of the PRs ar
386386
| Flag | Description |
387387
|------|-------------|
388388
| `--base <branch>` | Base branch for the bottom of the stack (default: `main`) |
389-
| `--draft` | Create new PRs as drafts |
389+
| `--open` | Mark new and existing PRs as ready for review |
390390
| `--remote <name>` | Remote to push to (defaults to auto-detected remote) |
391391

392392
**Examples:**
@@ -401,8 +401,8 @@ gh stack link 10 20 30
401401
# Add branches to an existing stack of PRs
402402
gh stack link 42 43 feature-auth feature-ui
403403

404-
# Use a different base branch and create PRs as drafts
405-
gh stack link --base develop --draft feat-a feat-b feat-c
404+
# Use a different base branch and mark PRs as ready for review
405+
gh stack link --base develop --open feat-a feat-b feat-c
406406
```
407407

408408
### `gh stack view`

cmd/link.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414

1515
type linkOptions struct {
1616
base string
17-
draft bool
17+
open bool
1818
remote string
1919
}
2020

@@ -51,7 +51,7 @@ the new PRs (existing PRs are never removed).`,
5151
}
5252

5353
cmd.Flags().StringVar(&opts.base, "base", "main", "Base branch for the bottom of the stack")
54-
cmd.Flags().BoolVar(&opts.draft, "draft", false, "Create new PRs as drafts")
54+
cmd.Flags().BoolVar(&opts.open, "open", false, "Mark new and existing PRs as ready for review")
5555
cmd.Flags().StringVar(&opts.remote, "remote", "", "Remote to push to (defaults to auto-detected remote)")
5656

5757
return cmd
@@ -325,7 +325,7 @@ func createMissingPRs(cfg *config.Config, client github.ClientOps, opts *linkOpt
325325
title := humanize(arg)
326326
body := generatePRBody("")
327327

328-
newPR, err := client.CreatePR(baseBranch, arg, title, body, opts.draft)
328+
newPR, err := client.CreatePR(baseBranch, arg, title, body, !opts.open)
329329
if err != nil {
330330
cfg.Errorf("failed to create PR for branch %s: %v", arg, err)
331331
return nil, ErrAPIFailure
@@ -379,6 +379,17 @@ func fixBaseBranches(cfg *config.Config, client github.ClientOps, opts *linkOpti
379379
cfg.PRLink(r.prNumber, r.prURL), expectedBase)
380380
}
381381
}
382+
383+
// Convert draft PR to ready for review when --open is set.
384+
if opts.open && pr.IsDraft {
385+
if err := client.MarkPRReadyForReview(pr.ID); err != nil {
386+
cfg.Warningf("failed to mark PR %s as ready for review: %v",
387+
cfg.PRLink(r.prNumber, r.prURL), err)
388+
} else {
389+
cfg.Successf("Marked PR %s as ready for review",
390+
cfg.PRLink(r.prNumber, r.prURL))
391+
}
392+
}
382393
}
383394
}
384395

cmd/link_test.go

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ func TestLink_BranchNames_AllNeedPRs(t *testing.T) {
477477
assert.Contains(t, output, "Created stack with 3 PRs")
478478
}
479479

480-
func TestLink_BranchNames_DraftFlag(t *testing.T) {
480+
func TestLink_BranchNames_DefaultDraft(t *testing.T) {
481481
restore := git.SetOps(newLinkGitMock("feat-a", "feat-b"))
482482
defer restore()
483483

@@ -512,13 +512,120 @@ func TestLink_BranchNames_DraftFlag(t *testing.T) {
512512
}
513513

514514
cmd := LinkCmd(cfg)
515-
cmd.SetArgs([]string{"--draft", "feat-a", "feat-b"})
515+
cmd.SetArgs([]string{"feat-a", "feat-b"})
516+
cmd.SetOut(io.Discard)
517+
cmd.SetErr(io.Discard)
518+
err := cmd.Execute()
519+
520+
assert.NoError(t, err)
521+
assert.True(t, createdDraft, "PRs should be created as drafts by default")
522+
}
523+
524+
func TestLink_BranchNames_OpenFlag(t *testing.T) {
525+
restore := git.SetOps(newLinkGitMock("feat-a", "feat-b"))
526+
defer restore()
527+
528+
var createdDraft bool
529+
prCounter := 0
530+
531+
cfg, _, _ := config.NewTestConfig()
532+
cfg.GitHubClientOverride = &github.MockClient{
533+
FindPRForBranchFn: func(branch string) (*github.PullRequest, error) {
534+
return nil, nil
535+
},
536+
FindPRByNumberFn: func(n int) (*github.PullRequest, error) {
537+
heads := map[int]string{1: "feat-a", 2: "feat-b"}
538+
bases := map[int]string{1: "main", 2: "feat-a"}
539+
if h, ok := heads[n]; ok {
540+
return &github.PullRequest{Number: n, HeadRefName: h, BaseRefName: bases[n]}, nil
541+
}
542+
return nil, nil
543+
},
544+
CreatePRFn: func(base, head, title, body string, draft bool) (*github.PullRequest, error) {
545+
createdDraft = draft
546+
prCounter++
547+
return &github.PullRequest{
548+
Number: prCounter, HeadRefName: head, BaseRefName: base,
549+
URL: fmt.Sprintf("https://github.com/o/r/pull/%d", prCounter),
550+
}, nil
551+
},
552+
ListStacksFn: func() ([]github.RemoteStack, error) {
553+
return []github.RemoteStack{}, nil
554+
},
555+
CreateStackFn: func([]int) (int, error) { return 1, nil },
556+
}
557+
558+
cmd := LinkCmd(cfg)
559+
cmd.SetArgs([]string{"--open", "feat-a", "feat-b"})
560+
cmd.SetOut(io.Discard)
561+
cmd.SetErr(io.Discard)
562+
err := cmd.Execute()
563+
564+
assert.NoError(t, err)
565+
assert.False(t, createdDraft, "PRs should not be created as drafts when --open is set")
566+
}
567+
568+
func TestLink_OpenFlag_ConvertsDraftPRs(t *testing.T) {
569+
restore := git.SetOps(newLinkGitMock("feat-a", "feat-b"))
570+
defer restore()
571+
572+
var markedReady []string
573+
574+
cfg, _, errR := config.NewTestConfig()
575+
cfg.GitHubClientOverride = &github.MockClient{
576+
FindPRForBranchFn: func(branch string) (*github.PullRequest, error) {
577+
switch branch {
578+
case "feat-a":
579+
return &github.PullRequest{
580+
Number: 1, ID: "PR_1", HeadRefName: "feat-a", BaseRefName: "main",
581+
IsDraft: true, URL: "https://github.com/o/r/pull/1",
582+
}, nil
583+
case "feat-b":
584+
return &github.PullRequest{
585+
Number: 2, ID: "PR_2", HeadRefName: "feat-b", BaseRefName: "feat-a",
586+
IsDraft: true, URL: "https://github.com/o/r/pull/2",
587+
}, nil
588+
}
589+
return nil, nil
590+
},
591+
FindPRByNumberFn: func(n int) (*github.PullRequest, error) {
592+
switch n {
593+
case 1:
594+
return &github.PullRequest{
595+
Number: 1, ID: "PR_1", HeadRefName: "feat-a", BaseRefName: "main",
596+
IsDraft: true, URL: "https://github.com/o/r/pull/1",
597+
}, nil
598+
case 2:
599+
return &github.PullRequest{
600+
Number: 2, ID: "PR_2", HeadRefName: "feat-b", BaseRefName: "feat-a",
601+
IsDraft: true, URL: "https://github.com/o/r/pull/2",
602+
}, nil
603+
}
604+
return nil, nil
605+
},
606+
MarkPRReadyForReviewFn: func(prID string) error {
607+
markedReady = append(markedReady, prID)
608+
return nil
609+
},
610+
ListStacksFn: func() ([]github.RemoteStack, error) {
611+
return []github.RemoteStack{}, nil
612+
},
613+
CreateStackFn: func([]int) (int, error) { return 1, nil },
614+
}
615+
616+
cmd := LinkCmd(cfg)
617+
cmd.SetArgs([]string{"--open", "feat-a", "feat-b"})
516618
cmd.SetOut(io.Discard)
517619
cmd.SetErr(io.Discard)
518620
err := cmd.Execute()
519621

622+
cfg.Err.Close()
623+
errOut, _ := io.ReadAll(errR)
624+
output := string(errOut)
625+
520626
assert.NoError(t, err)
521-
assert.True(t, createdDraft, "PRs should be created as drafts when --draft is set")
627+
assert.Equal(t, []string{"PR_1", "PR_2"}, markedReady, "both draft PRs should be marked ready")
628+
assert.Contains(t, output, "Marked PR")
522629
}
523630

524631
func TestLink_MixedArgs_PRNumberAndBranch(t *testing.T) {

cmd/submit.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
type submitOptions struct {
2020
auto bool
21-
draft bool
21+
open bool
2222
remote string
2323
}
2424

@@ -34,7 +34,7 @@ func SubmitCmd(cfg *config.Config) *cobra.Command {
3434
}
3535

3636
cmd.Flags().BoolVar(&opts.auto, "auto", false, "Use auto-generated PR titles without prompting")
37-
cmd.Flags().BoolVar(&opts.draft, "draft", false, "Create PRs as drafts")
37+
cmd.Flags().BoolVar(&opts.open, "open", false, "Mark new and existing PRs as ready for review")
3838
cmd.Flags().StringVar(&opts.remote, "remote", "", "Remote to push to (defaults to auto-detected remote)")
3939

4040
return cmd
@@ -235,6 +235,17 @@ func ensurePR(cfg *config.Config, client github.ClientOps, s *stack.Stack, i int
235235
cfg.Printf("PR %s for %s is up to date", cfg.PRLink(pr.Number, pr.URL), b.Branch)
236236
}
237237

238+
// Convert draft PR to ready for review when --open is set.
239+
if opts.open && pr.IsDraft {
240+
if err := client.MarkPRReadyForReview(pr.ID); err != nil {
241+
cfg.Warningf("failed to mark PR %s as ready for review: %v",
242+
cfg.PRLink(pr.Number, pr.URL), err)
243+
} else {
244+
cfg.Successf("Marked PR %s as ready for review",
245+
cfg.PRLink(pr.Number, pr.URL))
246+
}
247+
}
248+
238249
return nil
239250
}
240251

@@ -263,7 +274,7 @@ func createPR(cfg *config.Config, client github.ClientOps, s *stack.Stack, i int
263274
}
264275
body := generatePRBody(prBody)
265276

266-
newPR, createErr := client.CreatePR(baseBranch, b.Branch, title, body, opts.draft)
277+
newPR, createErr := client.CreatePR(baseBranch, b.Branch, title, body, !opts.open)
267278
if createErr != nil {
268279
cfg.Warningf("failed to create PR for %s: %v", b.Branch, createErr)
269280
return nil

0 commit comments

Comments
 (0)