跳转到主要内容

为什么 PIG 保持扁平的 Cobra 命令层

一个顶层命令对应 cmd 中一个文件,具体行为进入 cli 与 internal 包的源码布局决策。

决策日期: 2026-06-30
状态: 当前仍在执行的仓库架构。
当前参考: pig 命令总览源码仓库
范围: Go 源码归属与命令注册方式,而不是公开命令分类本身。

决策

cmd 包保持扁平。一个顶层命令对应一个顶层 Go 文件:pg.gopb.gopt.gope.gosty.godo.gorepo.go 等。即使命令树很复杂,也继续留在这个入口文件中, 除非另有明确的布局决策。

文件可以很长,但只应包含 Cobra 关注点:名称、别名、注解、参数、实参校验、帮助、注册和选项映射。 具体工作放入 cli/*internal/* 或其它实现包。

背景

早期命令增长产生了很多以子命令命名的小文件,也出现了多套确认、结构化输出、计划渲染和旧接口包装实现。 一些简单问题因此难以回答:顶层命令在哪里注册,某个别名由谁拥有,两段辅助代码是否在执行同一策略?

PIG 的命令族很大,但它们的公开语法是一个整体。把语法集中在一个位置更容易审查冲突, 实现包则继续按职责拆分。

考虑过的方案

  • cmd 下为每个命令建目录。 普通命令不采用,因为一个公开语法会散落在多个包中,也容易把业务逻辑放到 Cobra 附近。
  • 每个子命令一个文件。 注册、别名和继承参数将难以作为整体审阅。
  • 把所有逻辑都放进 cmd 操作逻辑依赖 Cobra 状态后,测试与复用都会变差。
  • 抽象每一行重复代码。 猜测式框架会遮蔽命令语法;只有已经稳定、跨命令复用的胶水才应共享。

契约

  • cmd/root.go 负责根命令、全局参数和顶层注册;
  • cmd/utils.go 负责共享的命令层辅助函数;
  • 每个普通顶层命令有一个对应的顶层源码文件,可以配一个同名测试文件;
  • Cobra 层校验语法和映射选项,但不执行具体操作;
  • 确认、注解、结构化输出与计划辅助逻辑只有一套实现;
  • 实现包接收普通选项并返回类型化结果或错误,不依赖 Cobra 全局状态。

影响

部分命令文件会有意保持较长。这个取舍是可接受的,因为公开接口可以作为整体审阅,而实现仍然在下层拆分。 规则也减少了别名或参数移动时的文件震荡,为 Agent 提供确定的阅读起点。

边界是架构性的,不是外观性的:把业务逻辑藏在闭包里的短 cmd 文件仍然违规; 只包含声明式命令胶水的长文件则没有问题。

验证与演进

约定记录在 9eb70db, 随后由大型命令面整合提交 fb93602 落实。 Guard 测试检查别名冲突与命令注册,实现层行为则由各包测试验证。

当前状态

这仍是新工作的仓库规则。公开命令文档统一放在本站;源码布局约束继续靠近代码保留, 确保贡献者与编码 Agent 在修改前就能看到。