Skip to content

Commit 23dfeff

Browse files
committed
Merge remote-tracking branch 'origin/master' into check-if-pr-exists
2 parents d0c32e7 + cb06812 commit 23dfeff

File tree

14 files changed

+615
-227
lines changed

14 files changed

+615
-227
lines changed

auth/oauth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func (oa *OAuthFlow) ObtainAccessToken() (accessToken string, err error) {
4747

4848
q := url.Values{}
4949
q.Set("client_id", oa.ClientID)
50-
q.Set("redirect_uri", fmt.Sprintf("http://localhost:%d/callback", port))
50+
q.Set("redirect_uri", fmt.Sprintf("http://127.0.0.1:%d/callback", port))
5151
// TODO: make scopes configurable
5252
q.Set("scope", "repo")
5353
q.Set("state", state)

command/issue.go

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/cli/cli/git"
1515
"github.com/cli/cli/internal/ghrepo"
1616
"github.com/cli/cli/pkg/githubtemplate"
17-
"github.com/cli/cli/pkg/text"
1817
"github.com/cli/cli/utils"
1918
"github.com/spf13/cobra"
2019
"github.com/spf13/pflag"
@@ -23,7 +22,6 @@ import (
2322
func init() {
2423
RootCmd.AddCommand(issueCmd)
2524
issueCmd.AddCommand(issueStatusCmd)
26-
issueCmd.AddCommand(issueViewCmd)
2725

2826
issueCmd.AddCommand(issueCreateCmd)
2927
issueCreateCmd.Flags().StringP("title", "t", "",
@@ -39,6 +37,7 @@ func init() {
3937
issueListCmd.Flags().IntP("limit", "L", 30, "Maximum number of issues to fetch")
4038
issueListCmd.Flags().StringP("author", "A", "", "Filter by author")
4139

40+
issueCmd.AddCommand(issueViewCmd)
4241
issueViewCmd.Flags().BoolP("preview", "p", false, "Display preview of issue content")
4342
}
4443

@@ -67,7 +66,7 @@ var issueStatusCmd = &cobra.Command{
6766
RunE: issueStatus,
6867
}
6968
var issueViewCmd = &cobra.Command{
70-
Use: "view {<number> | <url> | <branch>}",
69+
Use: "view {<number> | <url>}",
7170
Args: func(cmd *cobra.Command, args []string) error {
7271
if len(args) < 1 {
7372
return FlagError{errors.New("issue number or URL required as argument")}
@@ -138,23 +137,7 @@ func issueList(cmd *cobra.Command, args []string) error {
138137
}
139138

140139
out := cmd.OutOrStdout()
141-
table := utils.NewTablePrinter(out)
142-
for _, issue := range issues {
143-
issueNum := strconv.Itoa(issue.Number)
144-
if table.IsTTY() {
145-
issueNum = "#" + issueNum
146-
}
147-
labels := labelList(issue)
148-
if labels != "" && table.IsTTY() {
149-
labels = fmt.Sprintf("(%s)", labels)
150-
}
151-
table.AddField(issueNum, nil, colorFuncForState(issue.State))
152-
table.AddField(replaceExcessiveWhitespace(issue.Title), nil, nil)
153-
table.AddField(labels, nil, utils.Gray)
154-
table.EndRow()
155-
}
156-
table.Render()
157-
140+
printIssues(out, "", len(issues), issues)
158141
return nil
159142
}
160143

@@ -358,7 +341,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
358341
interactive := title == "" || body == ""
359342

360343
if interactive {
361-
tb, err := titleBodySurvey(cmd, title, body, templateFiles)
344+
tb, err := titleBodySurvey(cmd, title, body, defaults{}, templateFiles)
362345
if err != nil {
363346
return fmt.Errorf("could not collect title and/or body: %w", err)
364347
}
@@ -409,21 +392,26 @@ func issueCreate(cmd *cobra.Command, args []string) error {
409392
}
410393

411394
func printIssues(w io.Writer, prefix string, totalCount int, issues []api.Issue) {
395+
table := utils.NewTablePrinter(w)
412396
for _, issue := range issues {
413-
number := utils.Green("#" + strconv.Itoa(issue.Number))
414-
coloredLabels := labelList(issue)
415-
if coloredLabels != "" {
416-
coloredLabels = utils.Gray(fmt.Sprintf(" (%s)", coloredLabels))
397+
issueNum := strconv.Itoa(issue.Number)
398+
if table.IsTTY() {
399+
issueNum = "#" + issueNum
400+
}
401+
issueNum = prefix + issueNum
402+
labels := labelList(issue)
403+
if labels != "" && table.IsTTY() {
404+
labels = fmt.Sprintf("(%s)", labels)
417405
}
418-
419406
now := time.Now()
420407
ago := now.Sub(issue.UpdatedAt)
421-
422-
fmt.Fprintf(w, "%s%s %s%s %s\n", prefix, number,
423-
text.Truncate(70, replaceExcessiveWhitespace(issue.Title)),
424-
coloredLabels,
425-
utils.Gray(utils.FuzzyAgo(ago)))
408+
table.AddField(issueNum, nil, colorFuncForState(issue.State))
409+
table.AddField(replaceExcessiveWhitespace(issue.Title), nil, nil)
410+
table.AddField(labels, nil, utils.Gray)
411+
table.AddField(utils.FuzzyAgo(ago), nil, utils.Gray)
412+
table.EndRow()
426413
}
414+
table.Render()
427415
remaining := totalCount - len(issues)
428416
if remaining > 0 {
429417
fmt.Fprintf(w, utils.Gray("%sAnd %d more\n"), prefix, remaining)

command/issue_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"github.com/cli/cli/test"
1314
"github.com/cli/cli/utils"
1415
)
1516

@@ -28,10 +29,10 @@ func TestIssueStatus(t *testing.T) {
2829
}
2930

3031
expectedIssues := []*regexp.Regexp{
31-
regexp.MustCompile(`#8.*carrots`),
32-
regexp.MustCompile(`#9.*squash`),
33-
regexp.MustCompile(`#10.*broccoli`),
34-
regexp.MustCompile(`#11.*swiss chard`),
32+
regexp.MustCompile(`(?m)8.*carrots.*about.*ago`),
33+
regexp.MustCompile(`(?m)9.*squash.*about.*ago`),
34+
regexp.MustCompile(`(?m)10.*broccoli.*about.*ago`),
35+
regexp.MustCompile(`(?m)11.*swiss chard.*about.*ago`),
3536
}
3637

3738
for _, r := range expectedIssues {
@@ -226,7 +227,7 @@ func TestIssueView(t *testing.T) {
226227
var seenCmd *exec.Cmd
227228
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
228229
seenCmd = cmd
229-
return &outputStub{}
230+
return &test.OutputStub{}
230231
})
231232
defer restoreCmd()
232233

@@ -260,7 +261,7 @@ func TestIssueView_numberArgWithHash(t *testing.T) {
260261
var seenCmd *exec.Cmd
261262
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
262263
seenCmd = cmd
263-
return &outputStub{}
264+
return &test.OutputStub{}
264265
})
265266
defer restoreCmd()
266267

@@ -383,7 +384,7 @@ func TestIssueView_notFound(t *testing.T) {
383384
var seenCmd *exec.Cmd
384385
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
385386
seenCmd = cmd
386-
return &outputStub{}
387+
return &test.OutputStub{}
387388
})
388389
defer restoreCmd()
389390

@@ -430,7 +431,7 @@ func TestIssueView_urlArg(t *testing.T) {
430431
var seenCmd *exec.Cmd
431432
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
432433
seenCmd = cmd
433-
return &outputStub{}
434+
return &test.OutputStub{}
434435
})
435436
defer restoreCmd()
436437

@@ -515,7 +516,7 @@ func TestIssueCreate_web(t *testing.T) {
515516
var seenCmd *exec.Cmd
516517
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
517518
seenCmd = cmd
518-
return &outputStub{}
519+
return &test.OutputStub{}
519520
})
520521
defer restoreCmd()
521522

@@ -541,7 +542,7 @@ func TestIssueCreate_webTitleBody(t *testing.T) {
541542
var seenCmd *exec.Cmd
542543
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
543544
seenCmd = cmd
544-
return &outputStub{}
545+
return &test.OutputStub{}
545546
})
546547
defer restoreCmd()
547548

command/pr_checkout_test.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/cli/cli/context"
10+
"github.com/cli/cli/test"
1011
"github.com/cli/cli/utils"
1112
)
1213

@@ -46,7 +47,7 @@ func TestPRCheckout_sameRepo(t *testing.T) {
4647
return &errorStub{"exit status: 1"}
4748
default:
4849
ranCommands = append(ranCommands, cmd.Args)
49-
return &outputStub{}
50+
return &test.OutputStub{}
5051
}
5152
})
5253
defer restoreCmd()
@@ -98,7 +99,7 @@ func TestPRCheckout_urlArg(t *testing.T) {
9899
return &errorStub{"exit status: 1"}
99100
default:
100101
ranCommands = append(ranCommands, cmd.Args)
101-
return &outputStub{}
102+
return &test.OutputStub{}
102103
}
103104
})
104105
defer restoreCmd()
@@ -147,7 +148,7 @@ func TestPRCheckout_branchArg(t *testing.T) {
147148
return &errorStub{"exit status: 1"}
148149
default:
149150
ranCommands = append(ranCommands, cmd.Args)
150-
return &outputStub{}
151+
return &test.OutputStub{}
151152
}
152153
})
153154
defer restoreCmd()
@@ -193,10 +194,10 @@ func TestPRCheckout_existingBranch(t *testing.T) {
193194
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
194195
switch strings.Join(cmd.Args, " ") {
195196
case "git show-ref --verify --quiet refs/heads/feature":
196-
return &outputStub{}
197+
return &test.OutputStub{}
197198
default:
198199
ranCommands = append(ranCommands, cmd.Args)
199-
return &outputStub{}
200+
return &test.OutputStub{}
200201
}
201202
})
202203
defer restoreCmd()
@@ -248,7 +249,7 @@ func TestPRCheckout_differentRepo_remoteExists(t *testing.T) {
248249
return &errorStub{"exit status: 1"}
249250
default:
250251
ranCommands = append(ranCommands, cmd.Args)
251-
return &outputStub{}
252+
return &test.OutputStub{}
252253
}
253254
})
254255
defer restoreCmd()
@@ -300,7 +301,7 @@ func TestPRCheckout_differentRepo(t *testing.T) {
300301
return &errorStub{"exit status 1"}
301302
default:
302303
ranCommands = append(ranCommands, cmd.Args)
303-
return &outputStub{}
304+
return &test.OutputStub{}
304305
}
305306
})
306307
defer restoreCmd()
@@ -349,10 +350,10 @@ func TestPRCheckout_differentRepo_existingBranch(t *testing.T) {
349350
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
350351
switch strings.Join(cmd.Args, " ") {
351352
case "git config branch.feature.merge":
352-
return &outputStub{[]byte("refs/heads/feature\n")}
353+
return &test.OutputStub{[]byte("refs/heads/feature\n")}
353354
default:
354355
ranCommands = append(ranCommands, cmd.Args)
355-
return &outputStub{}
356+
return &test.OutputStub{}
356357
}
357358
})
358359
defer restoreCmd()
@@ -399,10 +400,10 @@ func TestPRCheckout_differentRepo_currentBranch(t *testing.T) {
399400
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
400401
switch strings.Join(cmd.Args, " ") {
401402
case "git config branch.feature.merge":
402-
return &outputStub{[]byte("refs/heads/feature\n")}
403+
return &test.OutputStub{[]byte("refs/heads/feature\n")}
403404
default:
404405
ranCommands = append(ranCommands, cmd.Args)
405-
return &outputStub{}
406+
return &test.OutputStub{}
406407
}
407408
})
408409
defer restoreCmd()
@@ -452,7 +453,7 @@ func TestPRCheckout_maintainerCanModify(t *testing.T) {
452453
return &errorStub{"exit status 1"}
453454
default:
454455
ranCommands = append(ranCommands, cmd.Args)
455-
return &outputStub{}
456+
return &test.OutputStub{}
456457
}
457458
})
458459
defer restoreCmd()

command/pr_create.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,39 @@ import (
1515
"github.com/spf13/cobra"
1616
)
1717

18+
type defaults struct {
19+
Title string
20+
Body string
21+
}
22+
23+
func computeDefaults(baseRef, headRef string) (defaults, error) {
24+
commits, err := git.Commits(baseRef, headRef)
25+
if err != nil {
26+
return defaults{}, err
27+
}
28+
29+
out := defaults{}
30+
31+
if len(commits) == 1 {
32+
out.Title = commits[0].Title
33+
body, err := git.CommitBody(commits[0].Sha)
34+
if err != nil {
35+
return defaults{}, err
36+
}
37+
out.Body = body
38+
} else {
39+
out.Title = utils.Humanize(headRef)
40+
41+
body := ""
42+
for _, c := range commits {
43+
body += fmt.Sprintf("- %s\n", c.Title)
44+
}
45+
out.Body = body
46+
}
47+
48+
return out, nil
49+
}
50+
1851
func prCreate(cmd *cobra.Command, _ []string) error {
1952
ctx := contextForCommand(cmd)
2053
remotes, err := ctx.Remotes()
@@ -73,30 +106,50 @@ func prCreate(cmd *cobra.Command, _ []string) error {
73106
return fmt.Errorf("could not parse body: %w", err)
74107
}
75108

109+
defs, defaultsErr := computeDefaults(baseBranch, headBranch)
110+
76111
isWeb, err := cmd.Flags().GetBool("web")
77112
if err != nil {
78113
return fmt.Errorf("could not parse web: %q", err)
79114
}
80115

116+
autofill, err := cmd.Flags().GetBool("fill")
117+
if err != nil {
118+
return fmt.Errorf("could not parse fill: %q", err)
119+
}
120+
81121
action := SubmitAction
82122
if isWeb {
83123
action = PreviewAction
124+
if (title == "" || body == "") && defaultsErr != nil {
125+
return fmt.Errorf("could not compute title or body defaults: %w", defaultsErr)
126+
}
127+
} else if autofill {
128+
if defaultsErr != nil {
129+
return fmt.Errorf("could not compute title or body defaults: %w", defaultsErr)
130+
}
131+
action = SubmitAction
132+
title = defs.Title
133+
body = defs.Body
84134
} else {
85135
fmt.Fprintf(colorableErr(cmd), "\nCreating pull request for %s into %s in %s\n\n",
86136
utils.Cyan(headBranch),
87137
utils.Cyan(baseBranch),
88138
ghrepo.FullName(baseRepo))
139+
if (title == "" || body == "") && defaultsErr != nil {
140+
fmt.Fprintf(colorableErr(cmd), "%s warning: could not compute title or body defaults: %s\n", utils.Yellow("!"), defaultsErr)
141+
}
89142
}
90143

91144
// TODO: only drop into interactive mode if stdin & stdout are a tty
92-
if !isWeb && (title == "" || body == "") {
145+
if !isWeb && !autofill && (title == "" || body == "") {
93146
var templateFiles []string
94147
if rootDir, err := git.ToplevelDir(); err == nil {
95148
// TODO: figure out how to stub this in tests
96149
templateFiles = githubtemplate.Find(rootDir, "PULL_REQUEST_TEMPLATE")
97150
}
98151

99-
tb, err := titleBodySurvey(cmd, title, body, templateFiles)
152+
tb, err := titleBodySurvey(cmd, title, body, defs, templateFiles)
100153
if err != nil {
101154
return fmt.Errorf("could not collect title and/or body: %w", err)
102155
}
@@ -243,4 +296,5 @@ func init() {
243296
prCreateCmd.Flags().StringP("base", "B", "",
244297
"The branch into which you want your code merged")
245298
prCreateCmd.Flags().BoolP("web", "w", false, "Open the web browser to create a pull request")
299+
prCreateCmd.Flags().BoolP("fill", "f", false, "Do not prompt for title/body and just use commit info")
246300
}

0 commit comments

Comments
 (0)