X Tutup
Skip to content

Commit 4da0261

Browse files
committed
Switch repo list to query via graphql package
Also order results by PUSHED_AT instead of UPDATED_AT to match the web interface.
1 parent 2284ef4 commit 4da0261

File tree

4 files changed

+75
-142
lines changed

4 files changed

+75
-142
lines changed

pkg/cmd/repo/list/fixtures/repoList.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010
"isFork": false,
1111
"isPrivate": false,
1212
"isArchived": false,
13-
"updatedAt": "2021-02-19T06:34:58Z"
13+
"pushedAt": "2021-02-19T06:34:58Z"
1414
},
1515
{
1616
"nameWithOwner": "octocat/cli",
1717
"description": "GitHub CLI",
1818
"isFork": true,
1919
"isPrivate": false,
2020
"isArchived": false,
21-
"updatedAt": "2021-02-19T06:06:06Z"
21+
"pushedAt": "2021-02-19T06:06:06Z"
2222
},
2323
{
2424
"nameWithOwner": "octocat/testing",
2525
"description": null,
2626
"isFork": false,
2727
"isPrivate": true,
2828
"isArchived": false,
29-
"updatedAt": "2021-02-11T22:32:05Z"
29+
"pushedAt": "2021-02-11T22:32:05Z"
3030
}
3131
],
3232
"pageInfo": {

pkg/cmd/repo/list/http.go

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,70 @@
11
package list
22

33
import (
4+
"context"
5+
"net/http"
46
"strings"
7+
"time"
58

6-
"github.com/cli/cli/api"
9+
"github.com/cli/cli/internal/ghinstance"
710
"github.com/shurcooL/githubv4"
11+
"github.com/shurcooL/graphql"
812
)
913

14+
type Repository struct {
15+
NameWithOwner string
16+
Description string
17+
IsFork bool
18+
IsPrivate bool
19+
IsArchived bool
20+
PushedAt time.Time
21+
}
22+
23+
func (r Repository) Info() string {
24+
var tags []string
25+
26+
if r.IsPrivate {
27+
tags = append(tags, "private")
28+
} else {
29+
tags = append(tags, "public")
30+
}
31+
if r.IsFork {
32+
tags = append(tags, "fork")
33+
}
34+
if r.IsArchived {
35+
tags = append(tags, "archived")
36+
}
37+
38+
return strings.Join(tags, ", ")
39+
}
40+
1041
type RepositoryList struct {
1142
Repositories []Repository
1243
TotalCount int
1344
}
1445

15-
func listRepos(client *api.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) {
16-
type response struct {
46+
func listRepos(client *http.Client, hostname string, limit int, owner string, filter FilterOptions) (*RepositoryList, error) {
47+
type query struct {
1748
RepositoryOwner struct {
1849
Repositories struct {
19-
TotalCount int
20-
RepositoryCount int
21-
Nodes []Repository
22-
PageInfo struct {
50+
Nodes []Repository
51+
TotalCount int
52+
PageInfo struct {
2353
HasNextPage bool
2454
EndCursor string
2555
}
26-
}
27-
}
56+
} `graphql:"repositories(first: $perPage, after: $endCursor, privacy: $privacy, isFork: $fork, ownerAffiliations: OWNER, orderBy: { field: PUSHED_AT, direction: DESC })"`
57+
} `graphql:"repositoryOwner(login: $owner)"`
2858
}
2959

30-
query := `
31-
query RepoList($owner: String!, $per_page: Int!, $endCursor: String, $fork: Boolean, $privacy: RepositoryPrivacy) {
32-
repositoryOwner(login: $owner) {
33-
repositories(
34-
first: $per_page,
35-
after: $endCursor,
36-
privacy: $privacy,
37-
isFork: $fork,
38-
ownerAffiliations: OWNER,
39-
orderBy: { field: UPDATED_AT, direction: DESC }) {
40-
totalCount
41-
nodes {
42-
nameWithOwner
43-
description
44-
isFork
45-
isPrivate
46-
isArchived
47-
updatedAt
48-
}
49-
pageInfo {
50-
hasNextPage
51-
endCursor
52-
}
53-
}
54-
}
55-
}`
56-
5760
perPage := limit
5861
if perPage > 100 {
5962
perPage = 100
6063
}
6164

6265
variables := map[string]interface{}{
6366
"owner": githubv4.String(owner),
64-
"per_page": githubv4.Int(perPage),
67+
"perPage": githubv4.Int(perPage),
6568
"endCursor": (*githubv4.String)(nil),
6669
}
6770

@@ -79,40 +82,29 @@ func listRepos(client *api.Client, hostname string, limit int, owner string, fil
7982
variables["fork"] = (*githubv4.Boolean)(nil)
8083
}
8184

82-
var repos []Repository
83-
var totalCount int
84-
85-
var result response
86-
85+
listResult := RepositoryList{}
8786
pagination:
8887
for {
89-
err := client.GraphQL(hostname, query, variables, &result)
88+
var result query
89+
gql := graphql.NewClient(ghinstance.GraphQLEndpoint(hostname), client)
90+
err := gql.QueryNamed(context.Background(), "RepositoryList", &result, variables)
9091
if err != nil {
9192
return nil, err
9293
}
9394

94-
repos = append(repos, result.RepositoryOwner.Repositories.Nodes...)
95-
96-
if len(repos) >= limit {
97-
if len(repos) > limit {
98-
repos = repos[:limit]
95+
listResult.TotalCount = result.RepositoryOwner.Repositories.TotalCount
96+
for _, repo := range result.RepositoryOwner.Repositories.Nodes {
97+
listResult.Repositories = append(listResult.Repositories, repo)
98+
if len(listResult.Repositories) >= limit {
99+
break pagination
99100
}
100-
break pagination
101101
}
102102

103103
if !result.RepositoryOwner.Repositories.PageInfo.HasNextPage {
104104
break
105105
}
106-
107106
variables["endCursor"] = githubv4.String(result.RepositoryOwner.Repositories.PageInfo.EndCursor)
108107
}
109108

110-
totalCount = result.RepositoryOwner.Repositories.TotalCount
111-
112-
listResult := &RepositoryList{
113-
Repositories: repos,
114-
TotalCount: totalCount,
115-
}
116-
117-
return listResult, nil
109+
return &listResult, nil
118110
}

pkg/cmd/repo/list/list.go

Lines changed: 12 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package list
33
import (
44
"fmt"
55
"net/http"
6-
"strings"
76
"time"
87

98
"github.com/cli/cli/api"
@@ -45,14 +44,12 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
4544
var (
4645
flagPublic bool
4746
flagPrivate bool
48-
flagSource bool
49-
flagFork bool
5047
)
5148

5249
cmd := &cobra.Command{
53-
Use: "list",
50+
Use: "list [<owner>]",
5451
Args: cobra.MaximumNArgs(1),
55-
Short: "List repositories from a user or organization",
52+
Short: "List repositories owned by user or organization",
5653
RunE: func(c *cobra.Command, args []string) error {
5754
if opts.Limit < 1 {
5855
return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", opts.Limit)}
@@ -61,7 +58,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
6158
if flagPrivate && flagPublic {
6259
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--public` or `--private`")}
6360
}
64-
if flagSource && flagFork {
61+
if opts.Source && opts.Fork {
6562
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--source` or `--fork`")}
6663
}
6764

@@ -71,9 +68,6 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
7168
opts.Visibility = "public"
7269
}
7370

74-
opts.Fork = flagFork
75-
opts.Source = flagSource
76-
7771
if len(args) > 0 {
7872
opts.Owner = args[0]
7973
}
@@ -88,8 +82,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
8882
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", 30, "Maximum number of repositories to list")
8983
cmd.Flags().BoolVar(&flagPrivate, "private", false, "Show only private repositories")
9084
cmd.Flags().BoolVar(&flagPublic, "public", false, "Show only public repositories")
91-
cmd.Flags().BoolVar(&flagSource, "source", false, "Show only source repositories")
92-
cmd.Flags().BoolVar(&flagFork, "fork", false, "Show only forks")
85+
cmd.Flags().BoolVar(&opts.Source, "source", false, "Show only non-forks")
86+
cmd.Flags().BoolVar(&opts.Fork, "fork", false, "Show only forks")
9387

9488
return cmd
9589
}
@@ -118,58 +112,36 @@ func listRun(opts *ListOptions) error {
118112
Source: opts.Source,
119113
}
120114

121-
listResult, err := listRepos(apiClient, ghinstance.OverridableDefault(), opts.Limit, owner, filter)
115+
listResult, err := listRepos(httpClient, ghinstance.OverridableDefault(), opts.Limit, owner, filter)
122116
if err != nil {
123117
return err
124118
}
125119

126120
cs := opts.IO.ColorScheme()
127-
128121
tp := utils.NewTablePrinter(opts.IO)
129-
130-
notArchived := filter.Fork || filter.Source
131-
132-
matchCount := len(listResult.Repositories)
133122
now := opts.Now()
134123

135124
for _, repo := range listResult.Repositories {
136-
if notArchived && repo.IsArchived {
137-
matchCount--
138-
listResult.TotalCount--
139-
continue
140-
}
141-
142-
nameWithOwner := repo.NameWithOwner
143-
144125
info := repo.Info()
145126
infoColor := cs.Gray
146-
visibility := "Public"
147-
148127
if repo.IsPrivate {
149128
infoColor = cs.Yellow
150-
visibility = "Private"
151129
}
152130

153-
description := repo.Description
154-
updatedAt := repo.UpdatedAt.Format(time.RFC3339)
155-
131+
tp.AddField(repo.NameWithOwner, nil, cs.Bold)
132+
tp.AddField(text.ReplaceExcessiveWhitespace(repo.Description), nil, nil)
133+
tp.AddField(info, nil, infoColor)
156134
if tp.IsTTY() {
157-
tp.AddField(nameWithOwner, nil, cs.Bold)
158-
tp.AddField(text.ReplaceExcessiveWhitespace(description), nil, nil)
159-
tp.AddField(info, nil, infoColor)
160-
tp.AddField(utils.FuzzyAgoAbbr(now, repo.UpdatedAt), nil, nil)
135+
tp.AddField(utils.FuzzyAgoAbbr(now, repo.PushedAt), nil, cs.Gray)
161136
} else {
162-
tp.AddField(nameWithOwner, nil, nil)
163-
tp.AddField(text.ReplaceExcessiveWhitespace(description), nil, nil)
164-
tp.AddField(visibility, nil, nil)
165-
tp.AddField(updatedAt, nil, nil)
137+
tp.AddField(repo.PushedAt.Format(time.RFC3339), nil, nil)
166138
}
167139
tp.EndRow()
168140
}
169141

170142
if isTerminal {
171143
hasFilters := filter.Visibility != "" || filter.Fork || filter.Source
172-
title := listHeader(owner, matchCount, listResult.TotalCount, hasFilters)
144+
title := listHeader(owner, len(listResult.Repositories), listResult.TotalCount, hasFilters)
173145
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title)
174146
}
175147

@@ -186,34 +158,3 @@ func listHeader(owner string, matchCount, totalMatchCount int, hasFilters bool)
186158

187159
return fmt.Sprintf("Showing %d of %d repositories in @%s", matchCount, totalMatchCount, owner)
188160
}
189-
190-
type Repository struct {
191-
NameWithOwner string
192-
Description string
193-
IsFork bool
194-
IsPrivate bool
195-
IsArchived bool
196-
UpdatedAt time.Time
197-
}
198-
199-
func (r Repository) Info() string {
200-
var info string
201-
var tags []string
202-
203-
if r.IsPrivate {
204-
tags = append(tags, "private")
205-
}
206-
if r.IsFork {
207-
tags = append(tags, "fork")
208-
}
209-
if r.IsArchived {
210-
tags = append(tags, "archived")
211-
}
212-
213-
if len(tags) > 0 {
214-
tags[0] = strings.Title(tags[0])
215-
info = strings.Join(tags, ", ")
216-
}
217-
218-
return info
219-
}

pkg/cmd/repo/list/list_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func TestRepoList_nontty(t *testing.T) {
6262
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`),
6363
)
6464
httpReg.Register(
65-
httpmock.GraphQL(`query RepoList\b`),
65+
httpmock.GraphQL(`query RepositoryList\b`),
6666
httpmock.FileResponse("./fixtures/repoList.json"),
6767
)
6868

@@ -84,9 +84,9 @@ func TestRepoList_nontty(t *testing.T) {
8484
assert.Equal(t, "", stderr.String())
8585

8686
assert.Equal(t, heredoc.Doc(`
87-
octocat/hello-world My first repository Public 2021-02-19T06:34:58Z
88-
octocat/cli GitHub CLI Public 2021-02-19T06:06:06Z
89-
octocat/testing Private 2021-02-11T22:32:05Z
87+
octocat/hello-world My first repository public 2021-02-19T06:34:58Z
88+
octocat/cli GitHub CLI public, fork 2021-02-19T06:06:06Z
89+
octocat/testing private 2021-02-11T22:32:05Z
9090
`), stdout.String())
9191
}
9292

@@ -104,7 +104,7 @@ func TestRepoList_tty(t *testing.T) {
104104
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`),
105105
)
106106
httpReg.Register(
107-
httpmock.GraphQL(`query RepoList\b`),
107+
httpmock.GraphQL(`query RepositoryList\b`),
108108
httpmock.FileResponse("./fixtures/repoList.json"),
109109
)
110110

@@ -129,9 +129,9 @@ func TestRepoList_tty(t *testing.T) {
129129
130130
Showing 3 of 3 repositories in @octocat
131131
132-
octocat/hello-world My first repository 8h
133-
octocat/cli GitHub CLI Fork 8h
134-
octocat/testing Private 7d
132+
octocat/hello-world My first repository public 8h
133+
octocat/cli GitHub CLI public, fork 8h
134+
octocat/testing private 7d
135135
`), stdout.String())
136136
}
137137

@@ -144,10 +144,10 @@ func TestRepoList_filtering(t *testing.T) {
144144
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`),
145145
)
146146
http.Register(
147-
httpmock.GraphQL(`query RepoList\b`),
147+
httpmock.GraphQL(`query RepositoryList\b`),
148148
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
149149
assert.Equal(t, "PRIVATE", params["privacy"])
150-
assert.Equal(t, float64(2), params["per_page"])
150+
assert.Equal(t, float64(2), params["perPage"])
151151
}),
152152
)
153153

0 commit comments

Comments
 (0)
X Tutup