Skip to content

Commit 17bbc08

Browse files
committed
feat(repos): accurate pagination docs and footer for list repos
The list repos endpoint always paginates (page 1, 15 per page by default, max 50) with no unpaginated "return all" mode, so correct the long description and example accordingly. Surface the page context in table output with a "Showing page X of Y, total Z repos" footer from the response envelope, mirroring list controls/environments/flows, and make the no-repos message page-aware. Covered by a server-free unit test plus an integration assertion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Kh9pJ2yQbnUPhytgtPSzv
1 parent 8186c43 commit 17bbc08

2 files changed

Lines changed: 32 additions & 17 deletions

File tree

cmd/kosli/listRepos.go

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import (
1414

1515
const listReposShortDesc = `List repos for an org. `
1616

17-
const listReposLongDesc = listReposShortDesc + `By default, all repos for the org are returned.
18-
Pass --page-limit and/or --page to paginate the results.
17+
const listReposLongDesc = listReposShortDesc + `The results are always paginated:
18+
by default the first page is returned with 15 repos per page. Use --page to select
19+
a page and --page-limit to change the page size (maximum 50).
1920
The list can be filtered by name with --name (exact match), by VCS provider with
2021
--provider, and by external repo ID with --repo-id.`
2122

2223
const listReposExample = `
23-
# list all repos for an org:
24+
# list repos for an org (first page, 15 per page):
2425
kosli list repos \
2526
--api-token yourAPIToken \
2627
--org yourOrgName
@@ -38,9 +39,9 @@ kosli list repos \
3839
--org yourOrgName \
3940
--output json
4041
41-
# show the second page of repos (25 per page):
42+
# show the second page of repos (15 per page):
4243
kosli list repos \
43-
--page-limit 25 \
44+
--page-limit 15 \
4445
--page 2 \
4546
--api-token yourAPIToken \
4647
--org yourOrgName
@@ -117,29 +118,37 @@ func (o *listReposOptions) run(out io.Writer) error {
117118
})
118119
}
119120

120-
func printReposListAsTable(raw string, out io.Writer, page int) error {
121-
var repos []map[string]any
122-
var response struct {
123-
Repos []map[string]any `json:"repos"`
124-
}
121+
type listReposResponse struct {
122+
Repos []map[string]any `json:"repos"`
123+
Page int `json:"page"`
124+
TotalPages int `json:"total_pages"`
125+
TotalCount int `json:"total_count"`
126+
}
125127

126-
err := json.Unmarshal([]byte(raw), &response)
127-
if err != nil {
128+
func printReposListAsTable(raw string, out io.Writer, page int) error {
129+
response := &listReposResponse{}
130+
if err := json.Unmarshal([]byte(raw), response); err != nil {
128131
return err
129132
}
130-
repos = response.Repos
131133

132-
if len(repos) == 0 {
133-
logger.Info("No repos were found.")
134+
if len(response.Repos) == 0 {
135+
msg := "No repos were found"
136+
if page != 1 {
137+
msg = fmt.Sprintf("%s at page number %d", msg, page)
138+
}
139+
logger.Info(msg + ".")
134140
return nil
135141
}
136142

137143
header := []string{"NAME", "URL", "PROVIDER", "TAGS"}
138144
rows := []string{}
139-
for _, repo := range repos {
145+
for _, repo := range response.Repos {
140146
row := fmt.Sprintf("%v\t%v\t%v\t%s", repo["name"], repo["url"], repo["provider"], formatRepoTags(repo["tags"]))
141147
rows = append(rows, row)
142148
}
149+
150+
rows = append(rows, fmt.Sprintf("\nShowing page %d of %d, total %d repos", response.Page, response.TotalPages, response.TotalCount))
151+
143152
tabFormattedPrint(out, header, rows)
144153

145154
return nil

cmd/kosli/listRepos_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ func (suite *ListReposCommandTestSuite) TestListReposCmd() {
136136
cmd: fmt.Sprintf(`list repos %s`, suite.acmeOrgKosliArguments),
137137
goldenRegex: `(?m)^list-repos-suite-org/untagged-repo\s+https://github\.com/list-repos-suite-org/untagged-repo\s+github\s*$`,
138138
},
139+
{
140+
name: "15-table output shows the pagination footer",
141+
cmd: fmt.Sprintf(`list repos %s`, suite.acmeOrgKosliArguments),
142+
goldenRegex: `(?m)^Showing page 1 of \d+, total \d+ repos$`,
143+
},
139144
}
140145

141146
runTestCmd(suite.T(), tests)
@@ -151,11 +156,12 @@ func TestPrintReposListAsTableRendersBlankTagsCell(t *testing.T) {
151156
raw := `{"repos":[
152157
{"name":"o/tagged","url":"https://github.com/o/tagged","provider":"github","tags":{"team":"platform"}},
153158
{"name":"o/untagged","url":"https://github.com/o/untagged","provider":"github","tags":{}}
154-
]}`
159+
],"page":1,"total_pages":3,"total_count":45}`
155160
var buf bytes.Buffer
156161
require.NoError(t, printReposListAsTable(raw, &buf, 1))
157162
out := buf.String()
158163
require.Regexp(t, `(?m)^NAME\s+URL\s+PROVIDER\s+TAGS\s*$`, out)
159164
require.Regexp(t, `(?m)^o/tagged\s+https://github\.com/o/tagged\s+github\s+team=platform\s*$`, out)
160165
require.Regexp(t, `(?m)^o/untagged\s+https://github\.com/o/untagged\s+github\s*$`, out)
166+
require.Regexp(t, `(?m)^Showing page 1 of 3, total 45 repos$`, out)
161167
}

0 commit comments

Comments
 (0)