From 4435dd251d15b45804e8f2d368bab0fc506726ad Mon Sep 17 00:00:00 2001 From: laurencewannamaker Date: Sun, 5 Jul 2026 22:37:11 +0000 Subject: [PATCH] =?UTF-8?q?feat(list):=20label/member=20+list=20=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=E5=88=86=E9=A1=B5=E4=B8=8E=20--all=EF=BC=9Bmember=20l?= =?UTF-8?q?ist=20=E6=94=B9=E8=B5=B0=20v1=20=E7=AB=AF=E7=82=B9=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=9D=9E=E7=AE=A1=E7=90=86=E5=91=98=20403?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- doc/changes/list-all-pagination.md | 13 +++++++++++-- shortcuts/label/label.go | 12 ++++++++++++ shortcuts/member/member.go | 23 ++++++++++++++++++++++- shortcuts/member/member_test.go | 4 ++-- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/doc/changes/list-all-pagination.md b/doc/changes/list-all-pagination.md index 68bf550..88d1f24 100644 --- a/doc/changes/list-all-pagination.md +++ b/doc/changes/list-all-pagination.md @@ -16,15 +16,24 @@ - 遵循 `total_count`:达到总数即停止;另设最大页数护栏,防止 忽略 `page` 参数的端点造成死循环。 - `PaginateAll` 保持原签名,委托给 `PaginateAllKey`。 -- 九个分页 list 命令新增 `--all` 布尔参数(默认 false): +- 十一个分页 list 命令新增 `--all` 布尔参数(默认 false): - `issue +list --all`(合并结果同样应用 number/database_id 规范化) - `pr +list --all`、`branch +list --all`、`release +list --all` - `milestone +list --all`、`org +list --all`、`repo +list --all` - `search +repos --all`、`search +users --all` + - `label +list --all`、`member +list --all` - 对应资源键:`issues`/`pulls`/`branches`/`releases`/`milestones`/ - `organizations`/`projects`/`users`(均生产实测确认) + `organizations`/`projects`/`users`/`issue_tags`/`collaborators` + (均生产实测确认) - 输出与单页响应同构:`{"total_count": N, "<资源名>": [...]}`。 - 总数字段兼容 `total_count` 与 `count`(如 `/users/:login/projects`)。 +- 修复两个既有分页语义缺口(均生产实测确认端点本身分页): + - `label +list` 完全没有 `--page/--limit`(端点实际返回 `total_count`),现已补齐; + - `member +list` 既无分页又走遗留路径(非管理员直接 403),现改走 + `/v1/:owner/:repo/collaborators`(支持分页且普通成员可读)。 +- 未加 `--all` 的 list 端点均经生产验证为非分页或未部署: + `licenses`/`ignores` 返回全量数组;`pm/pipelines` 404 未部署; + `pipeline +runs` 用 `total_data` 非标准包裹;dataset 端点未部署(平台 issue #144255)。 - 中英文 i18n 新增 `flag.all` 文案。 ## 命令示例 diff --git a/shortcuts/label/label.go b/shortcuts/label/label.go index 2b6a298..a0c3320 100644 --- a/shortcuts/label/label.go +++ b/shortcuts/label/label.go @@ -32,16 +32,28 @@ func Shortcuts() []*common.Shortcut { {Name: "only-name", Usage: "Return only label id and name: true or false"}, {Name: "sort-by", Usage: "Sort field: updated_on, created_on, issues_count"}, {Name: "sort-direction", Usage: "Sort direction: asc or desc"}, + {Name: "page", Short: "p", Usage: "Page number", Default: "1"}, + {Name: "limit", Short: "l", Usage: "Items per page", Default: "20"}, + {Name: "all", Usage: "Fetch all pages automatically (ignores --page)", Bool: true, Default: "false"}, }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { return err } q := url.Values{} + q.Set("page", ctx.Arg("page")) + q.Set("limit", ctx.Arg("limit")) setQueryIfPresent(q, "keyword", ctx.Arg("keyword")) setQueryIfPresent(q, "only_name", ctx.Arg("only-name")) setQueryIfPresent(q, "order_by", ctx.Arg("sort-by")) setQueryIfPresent(q, "order_direction", ctx.Arg("sort-direction")) + if ctx.Arg("all") == "true" { + items, err := ctx.PaginateAllKey(labelPath(ctx), q, "issue_tags") + if err != nil { + return err + } + return ctx.Output(common.NewListEnvelope("issue_tags", items)) + } env, err := ctx.CallAPIWithQuery("GET", labelPath(ctx), q) if err != nil { return err diff --git a/shortcuts/member/member.go b/shortcuts/member/member.go index 3734da3..0b236d6 100644 --- a/shortcuts/member/member.go +++ b/shortcuts/member/member.go @@ -26,11 +26,26 @@ func Shortcuts() []*common.Shortcut { { Name: "list", Description: "List repository members", + Flags: []common.Flag{ + {Name: "page", Short: "p", Usage: "Page number", Default: "1"}, + {Name: "limit", Short: "l", Usage: "Items per page", Default: "20"}, + {Name: "all", Usage: "Fetch all pages automatically (ignores --page)", Bool: true, Default: "false"}, + }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { return err } - env, err := ctx.CallAPI("GET", collaboratorsPath(ctx), nil) + q := url.Values{} + q.Set("page", ctx.Arg("page")) + q.Set("limit", ctx.Arg("limit")) + if ctx.Arg("all") == "true" { + items, err := ctx.PaginateAllKey(collaboratorsV1Path(ctx), q, "collaborators") + if err != nil { + return err + } + return ctx.Output(common.NewListEnvelope("collaborators", items)) + } + env, err := ctx.CallAPIWithQuery("GET", collaboratorsV1Path(ctx), q) if err != nil { return err } @@ -254,6 +269,12 @@ func collaboratorsPath(ctx *common.RuntimeContext) string { return fmt.Sprintf("/%s/%s/collaborators", ctx.Owner, ctx.Repo) } +// collaboratorsV1Path is the v1 read endpoint, which supports pagination and +// does not require admin permission (the legacy path rejects non-admins). +func collaboratorsV1Path(ctx *common.RuntimeContext) string { + return fmt.Sprintf("/v1/%s/%s/collaborators", ctx.Owner, ctx.Repo) +} + func collaboratorsRemovePath(ctx *common.RuntimeContext) string { return fmt.Sprintf("%s/remove", collaboratorsPath(ctx)) } diff --git a/shortcuts/member/member_test.go b/shortcuts/member/member_test.go index d379e82..63872e9 100644 --- a/shortcuts/member/member_test.go +++ b/shortcuts/member/member_test.go @@ -15,8 +15,8 @@ import ( func TestMemberList(t *testing.T) { server := newMemberTestServer(t, func(w http.ResponseWriter, r *http.Request) { - assertRequest(t, r, "GET", "/owner/repo/collaborators.json") - writeJSON(t, w, map[string]interface{}{"total_count": 1, "members": []interface{}{}}) + assertRequest(t, r, "GET", "/v1/owner/repo/collaborators.json") + writeJSON(t, w, map[string]interface{}{"total_count": 1, "collaborators": []interface{}{}}) }) defer server.Close()