X Tutup
Skip to content

Commit cf1feb8

Browse files
committed
Add gh issue list and gh issue view ISSUE_NUMBER
1 parent 357de1b commit cf1feb8

File tree

8 files changed

+303
-39
lines changed

8 files changed

+303
-39
lines changed

command/issue.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"github.com/github/gh-cli/api"
8+
"github.com/github/gh-cli/utils"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func init() {
13+
RootCmd.AddCommand(issueCmd)
14+
issueCmd.AddCommand(
15+
&cobra.Command{
16+
Use: "list",
17+
Short: "List issues",
18+
RunE: issueList,
19+
},
20+
&cobra.Command{
21+
Use: "view issue-number",
22+
Args: cobra.MinimumNArgs(1),
23+
Short: "Open a issue in the browser",
24+
RunE: issueView,
25+
},
26+
)
27+
}
28+
29+
var issueCmd = &cobra.Command{
30+
Use: "issue",
31+
Short: "Work with GitHub issues",
32+
Long: `This command allows you to work with issues.`,
33+
Args: cobra.MinimumNArgs(1),
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
return fmt.Errorf("%+v is not a valid issue command", args)
36+
},
37+
}
38+
39+
func issueList(cmd *cobra.Command, args []string) error {
40+
cmd.SilenceUsage = true
41+
42+
ctx := contextForCommand(cmd)
43+
apiClient, err := apiClientForContext(ctx)
44+
if err != nil {
45+
return err
46+
}
47+
48+
baseRepo, err := ctx.BaseRepo()
49+
if err != nil {
50+
return err
51+
}
52+
53+
currentUser, err := ctx.AuthLogin()
54+
if err != nil {
55+
return err
56+
}
57+
58+
issuePayload, err := api.Issues(apiClient, baseRepo, currentUser)
59+
if err != nil {
60+
return err
61+
}
62+
63+
printHeader("Issues assigned to you")
64+
if issuePayload.Assigned != nil {
65+
printIssues(issuePayload.Assigned...)
66+
} else {
67+
message := fmt.Sprintf(" There are no issues assgined to you")
68+
printMessage(message)
69+
}
70+
fmt.Println()
71+
72+
printHeader("Issues mentioning you")
73+
if len(issuePayload.Mentioned) > 0 {
74+
printIssues(issuePayload.Mentioned...)
75+
} else {
76+
printMessage(" There are no issues mentioning you")
77+
}
78+
fmt.Println()
79+
80+
printHeader("Recent issues")
81+
if len(issuePayload.Recent) > 0 {
82+
printIssues(issuePayload.Recent...)
83+
} else {
84+
printMessage(" There are no recent issues")
85+
}
86+
fmt.Println()
87+
88+
return nil
89+
}
90+
91+
func issueView(cmd *cobra.Command, args []string) error {
92+
cmd.SilenceUsage = true
93+
94+
ctx := contextForCommand(cmd)
95+
96+
baseRepo, err := ctx.BaseRepo()
97+
if err != nil {
98+
return err
99+
}
100+
101+
var openURL string
102+
if number, err := strconv.Atoi(args[0]); err == nil {
103+
// TODO: move URL generation into GitHubRepository
104+
openURL = fmt.Sprintf("https://github.com/%s/%s/issues/%d", baseRepo.RepoOwner(), baseRepo.RepoName(), number)
105+
} else {
106+
return fmt.Errorf("invalid issue number: '%s'", args[0])
107+
}
108+
109+
fmt.Printf("Opening %s in your browser.\n", openURL)
110+
return utils.OpenInBrowser(openURL)
111+
}
112+
113+
func printIssues(issues ...api.Issue) {
114+
for _, issue := range issues {
115+
fmt.Printf(" #%d %s\n", issue.Number, truncateTitle(issue.Title, 70))
116+
}
117+
}

command/issue_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package command
2+
3+
import (
4+
"os"
5+
"regexp"
6+
"testing"
7+
8+
"github.com/github/gh-cli/test"
9+
)
10+
11+
func TestIssueList(t *testing.T) {
12+
initBlankContext("OWNER/REPO", "master")
13+
http := initFakeHTTP()
14+
15+
jsonFile, _ := os.Open("../test/fixtures/issueList.json")
16+
defer jsonFile.Close()
17+
http.StubResponse(200, jsonFile)
18+
19+
output, err := test.RunCommand(RootCmd, "issue list")
20+
if err != nil {
21+
t.Errorf("error running command `issue list`: %v", err)
22+
}
23+
24+
expectedIssues := []*regexp.Regexp{
25+
regexp.MustCompile(`#8.*carrots`),
26+
regexp.MustCompile(`#9.*squash`),
27+
regexp.MustCompile(`#10.*broccoli`),
28+
regexp.MustCompile(`#11.*swiss chard`),
29+
}
30+
31+
for _, r := range expectedIssues {
32+
if !r.MatchString(output) {
33+
t.Errorf("output did not match regexp /%s/", r)
34+
}
35+
}
36+
}
37+
38+
func TestIssueView(t *testing.T) {
39+
initBlankContext("OWNER/REPO", "master")
40+
http := initFakeHTTP()
41+
42+
jsonFile, _ := os.Open("../test/fixtures/issueView.json")
43+
defer jsonFile.Close()
44+
http.StubResponse(200, jsonFile)
45+
46+
teardown, callCount := mockOpenInBrowser()
47+
defer teardown()
48+
49+
output, err := test.RunCommand(RootCmd, "issue view 8")
50+
if err != nil {
51+
t.Errorf("error running command `issue view`: %v", err)
52+
}
53+
54+
if output == "" {
55+
t.Errorf("command output expected got an empty string")
56+
}
57+
58+
if *callCount != 1 {
59+
t.Errorf("OpenInBrowser should be called 1 time but was called %d time(s)", *callCount)
60+
}
61+
}

command/pr.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func prView(cmd *cobra.Command, args []string) error {
129129

130130
func printPrs(prs ...api.PullRequest) {
131131
for _, pr := range prs {
132-
fmt.Printf(" #%d %s %s\n", pr.Number, truncateTitle(pr.Title), utils.Cyan("["+pr.HeadRefName+"]"))
132+
fmt.Printf(" #%d %s %s\n", pr.Number, truncateTitle(pr.Title, 50), utils.Cyan("["+pr.HeadRefName+"]"))
133133
}
134134
}
135135

@@ -141,9 +141,7 @@ func printMessage(s string) {
141141
fmt.Println(utils.Gray(s))
142142
}
143143

144-
func truncateTitle(title string) string {
145-
const maxLength = 50
146-
144+
func truncateTitle(title string, maxLength int) string {
147145
if len(title) > maxLength {
148146
return title[0:maxLength-3] + "..."
149147
}

command/pr_test.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,9 @@ import (
55
"regexp"
66
"testing"
77

8-
"github.com/github/gh-cli/api"
9-
"github.com/github/gh-cli/context"
108
"github.com/github/gh-cli/test"
11-
"github.com/github/gh-cli/utils"
129
)
1310

14-
func initBlankContext(repo, branch string) {
15-
initContext = func() context.Context {
16-
ctx := context.NewBlank()
17-
ctx.SetBaseRepo(repo)
18-
ctx.SetBranch(branch)
19-
return ctx
20-
}
21-
}
22-
23-
func initFakeHTTP() *api.FakeHTTP {
24-
http := &api.FakeHTTP{}
25-
apiClientForContext = func(context.Context) (*api.Client, error) {
26-
return api.NewClient(api.ReplaceTripper(http)), nil
27-
}
28-
return http
29-
}
30-
3111
func TestPRList(t *testing.T) {
3212
initBlankContext("OWNER/REPO", "master")
3313
http := initFakeHTTP()
@@ -114,18 +94,3 @@ func TestPRView_NoActiveBranch(t *testing.T) {
11494
t.Errorf("OpenInBrowser should be called once but was called %d time(s)", *callCount)
11595
}
11696
}
117-
118-
func mockOpenInBrowser() (func(), *int) {
119-
callCount := 0
120-
originalOpenInBrowser := utils.OpenInBrowser
121-
teardown := func() {
122-
utils.OpenInBrowser = originalOpenInBrowser
123-
}
124-
125-
utils.OpenInBrowser = func(_ string) error {
126-
callCount++
127-
return nil
128-
}
129-
130-
return teardown, &callCount
131-
}

command/testing.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package command
2+
3+
import (
4+
"github.com/github/gh-cli/api"
5+
"github.com/github/gh-cli/context"
6+
"github.com/github/gh-cli/utils"
7+
)
8+
9+
func initBlankContext(repo, branch string) {
10+
initContext = func() context.Context {
11+
ctx := context.NewBlank()
12+
ctx.SetBaseRepo(repo)
13+
ctx.SetBranch(branch)
14+
return ctx
15+
}
16+
}
17+
18+
func initFakeHTTP() *api.FakeHTTP {
19+
http := &api.FakeHTTP{}
20+
apiClientForContext = func(context.Context) (*api.Client, error) {
21+
return api.NewClient(api.ReplaceTripper(http)), nil
22+
}
23+
return http
24+
}
25+
26+
func mockOpenInBrowser() (func(), *int) {
27+
callCount := 0
28+
originalOpenInBrowser := utils.OpenInBrowser
29+
teardown := func() {
30+
utils.OpenInBrowser = originalOpenInBrowser
31+
}
32+
33+
utils.OpenInBrowser = func(_ string) error {
34+
callCount++
35+
return nil
36+
}
37+
38+
return teardown, &callCount
39+
}

test/fixtures/issueList.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"data": {
3+
"assigned": {
4+
"issues": {
5+
"edges": [
6+
{
7+
"node": {
8+
"number": 9,
9+
"title": "corey thinks squash tastes bad"
10+
}
11+
},
12+
{
13+
"node": {
14+
"number": 10,
15+
"title": "broccoli is a superfood"
16+
}
17+
}
18+
]
19+
}
20+
},
21+
"mentioned": {
22+
"issues": {
23+
"edges": [
24+
{
25+
"node": {
26+
"number": 8,
27+
"title": "rabbits eat carrots"
28+
}
29+
},
30+
{
31+
"node": {
32+
"number": 11,
33+
"title": "swiss chard is neutral"
34+
}
35+
}
36+
]
37+
}
38+
},
39+
"recent": {
40+
"issues": {
41+
"edges": []
42+
}
43+
},
44+
45+
"pageInfo": { "hasNextPage": false }
46+
}
47+
}

test/fixtures/issueView.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"data": {
3+
"repository": {
4+
"issues": {
5+
"edges": [
6+
{
7+
"node": {
8+
"number": 8,
9+
"title": "rabbits eat carrots",
10+
"url": "https://github.com/github/gh-cli/pull/10"
11+
}
12+
},
13+
{
14+
"node": {
15+
"number": 9,
16+
"title": "corey thinks squash tastes bad"
17+
}
18+
},
19+
{
20+
"node": {
21+
"number": 10,
22+
"title": "broccoli is a superfood"
23+
}
24+
},
25+
{
26+
"node": {
27+
"number": 11,
28+
"title": "swiss chard is neutral"
29+
}
30+
}
31+
]
32+
}
33+
},
34+
"pageInfo": { "hasNextPage": false }
35+
}
36+
}

test/helpers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func RunCommand(root *cobra.Command, s string) (string, error) {
7171
root.SetArgs(strings.Split(s, " "))
7272
_, err = root.ExecuteC()
7373
})
74+
7475
if err != nil {
7576
return "", err
7677
}

0 commit comments

Comments
 (0)
X Tutup