Closed
bug: --paginate --json 在零结果时输出 null 而非 [](pr list / actions run list) #456
aflyingtoAdd on Jul 10Closed at Jul 27
liujinsheng
Jul 10 Comments:
Jul 10 Comments:
验证记录
- 当前版本或分支: main @ 340f90c(本地构建
./gc version) - 验证方式: 代码审查 + nil 切片 marshal 行为验证(
infra-test/*仓库 PR 列表对当前 token 返回 403,无法联机复现;该 bug 为确定性的输出格式问题,代码级验证足以确认) - 根因确认:
pkg/cmd/pr/list/list.go:202var all []api.PullRequest,paginate 循环于:217执行all = append(all, prs...);当所有页均返回空时all保持 nilpkg/cmd/actions/run/list/list.go:194var all []api.WorkflowRun为同形 pattern(:210append)cmdutil.WriteJSON(pkg/cmdutil/output.go:17)经encoding/json编码:nil 切片 marshal 为null,空切片 marshal 为[](已用最小 Go 程序验证:var nilSlice []int->null,[]int{}->[])- 非 paginate 路径存在 nil 守卫(如 actions 的
trimRuns返回[]api.WorkflowRun{}),故输出[];paginate 路径无此守卫
- 实际结果: 复现成功。
--paginate --json+ 零结果 ->null;--json(非 paginate)->[],两者不一致 - 结论: bug 成立,继续修复。修复方向:在 paginate 路径
WriteJSON前归一化 nil -> 空切片(pr list + actions run list),或统一在cmdutil.WriteJSON层处理 nil 切片;建议一并排查pkg/cmd/下其它var all []T+ paginate 实现
认领开发,进入 status/in-progress。


Jul 10 Added label: status/verified
Jul 10 Added label: status/in-progress
Jul 10 Removed label: status/triage
liujinsheng
Jul 10 Comments:
Jul 10 Comments:
开发进度
- 根因: paginate 路径用
var all []T累积各页结果,所有页为空时all保持 nil;encoding/json将 nil 切片 marshal 为null,而非 paginate 路径有 nil 守卫输出[],两者不一致 - 主要修改: 采用方案 B(集中式),在共享出口
cmdutil.WriteJSON新增normalizeNilSlice,将 nil 切片归一化为空切片;nil interface 保持null不变,单对象输出不受影响。改动pkg/cmdutil/output.go(+21)、新增pkg/cmdutil/output_test.go、pkg/cmd/pr/list/list_test.go(+36 回归测试) - 测试:
go test ./...90 包全部通过;新增测试在修复前失败(输出null)、修复后通过,TDD 验证可捕获该 bug - 实际命令验证:
TestListRunPaginateEmptyResultsEmitsEmptyArray端到端验证listRunpaginate 空结果输出[];infra-test 仓库对当前 token 返回 403,按规范禁止用个人仓库联机测试,故以单元/集成测试覆盖 - 安全影响: 纯逻辑改动(reflect 类型判断),无凭证/网络/IO 变化,无密钥风险
- 风险或未覆盖项: risk/low;改动在共享 WriteJSON 但仅影响 nil 切片(->
[]),nil interface 与非切片类型不变;未逐一排查其它传 nil 切片给 WriteJSON 的命令(nil->[]对 JSON 数组语义始终正确,不构成风险) - 关联 PR: #390


Jul 10 Added label: status/ready-for-review
Jul 10 Removed label: status/in-progress
Jul 22 Added label: status/in-progress
Jul 22 Removed label: status/ready-for-review
现象
--paginate --json在结果为空时输出null,而非[];非 paginate 路径正确输出[]。两个命令均命中(pr list + actions run list)。复现
actions run list(Ascend/pytorch 无流水线运行记录)
$ gc actions run list -R Ascend/pytorch --paginate --json null # ✗ 应为 [] $ gc actions run list -R Ascend/pytorch --json [] # ✓ 非 paginate 正确pr list(强制零结果:不存在分支)
$ gc pr list -R Ascend/pytorch --head zzz-nonexistent --paginate --json null # ✗ 应为 [] $ gc pr list -R Ascend/pytorch --head zzz-nonexistent --json [] # ✓ 非 paginate 正确根因
paginate 路径用
var all []T+append累积各页结果;当所有页都返回空时,all保持nil。cmdutil.WriteJSON(encoding/json)将 nil 切片 marshal 为null(而非[])。非 paginate 路径有 nil 守卫:actions 的
trimRuns(pkg/cmd/actions/run/list/list.go)对nil返回[]api.WorkflowRun{}→ 输出[];pr list 非 paginate 路径返回 API 的空切片 →[]。代码位置(同形 pattern):
pkg/cmd/pr/list/list.go:202var all []api.PullRequest+:217 all = append(all, prs...)pkg/cmd/actions/run/list/list.golistRunspaginate 分支var all []api.WorkflowRun+append影响
--paginate --json时期望 JSON 数组;null破坏数组消费者(需额外 nil 判断),且与--json(非 paginate,输出[])不一致。spec/foundations/agent-friendly-cli.md:只读命令--json输出应稳定可消费)。修复范围
在 paginate 路径 WriteJSON 前归一化 nil→空切片(两个命令),或统一在
cmdutil.WriteJSON层处理 nil 切片。建议先在pr/list治理以保持与既有命令对齐(PR #377 round-2 评审建议),再同步actions/run/list。涉及命令:
gc pr list --paginate --jsongc actions run list --paginate --jsonvar all []T+ paginate 的 list 命令(建议一并排查pkg/cmd/下所有 paginate 实现)。环境与关联
pkg/cmd/actions/run/list/list.go+pkg/cmd/pr/list/list.goparity)期望
--paginate --json零结果时输出[],与非 paginate 一致。