Skip to main content

用途

前面三页已经把 TODO 小工具的需求、协作、实现、测试和审查走了一遍。本页继续沿用同一个项目:在 todo.py 中加入按序号完成待办的功能,并把这次改动从工作区交付到可发布版本。 本页关注的不是某个 GitHub 按钮的位置,而是一条可以重复执行的交付链:
示例假设如下:
  • 项目目录为 todo-cli,入口是 todo.py。
  • 主分支为 main,功能分支为 feat/todo-done。
  • 项目使用 Python 3 标准库,不引入第三方依赖。
  • python -m unittest 是本项目的测试命令。
  • 发布产物是一个 Git tag 和对应的 GitHub Release;如果你的项目有包管理器、容器镜像或部署平台,把发布步骤替换成真实命令。
  • 所有命令都先在测试环境演练。示例中的哈希、时间、版本号和 URL 只用于说明,不能当作你的真实值。
Git、GitHub Actions、Codex 和发布平台的界面或参数可能随版本变化。命令执行前先用 git --help、gh --help、项目脚本和平台文档确认;不要因为示例看起来熟悉,就跳过影响范围检查。

本页完成标准

读完并实际演练后,你应当能够:
  1. 在提交前确认分支、差异、测试、密钥和发布范围。
  2. 把一个混杂改动拆成容易 review、容易回滚的多个 commit。
  3. 创建包含背景、验证证据和回滚方案的 PR。
  4. 看懂 CI 的状态、日志和门禁,不把本地通过误认为合并通过。
  5. 区分合并审批、发布审批和生产操作授权。
  6. 发布后按同一路径做冒烟验证,并在异常时选择合适的回滚方式。
  7. 写出可交接的完整交付记录,并把重复问题转成项目规则。

01 先固定这次交付的边界

本次连续实战的需求是:用户执行 done <序号> 后,将待办标记为完成;list 能清楚显示状态;输入非数字或越界序号时给出友好提示。 在动 Git 之前,先把目标和非目标写下来。一个可交付的范围表如下: 用一句话写出本次交付声明:
这句话的作用是限制范围。它不是提交信息,也不是 PR 的全部内容,而是后面检查“有没有夹带改动”的比较基准。

先确认仓库位置

预期输出类似:
检查重点:
  • git rev-parse --show-toplevel 必须是当前项目根目录。
  • 当前分支必须是本次功能分支,不要在 main 上直接开发。
  • status 中列出的文件应当能解释清楚;出现 .env、密钥、数据库备份、构建缓存或陌生文件时先停下。
  • 分支后面的远端跟踪关系要正确。若显示与预期不同,先确认远端和分支,不要直接推送。
查看远端和近期历史:
预期输出类似:
如果工作区中已有同事的未提交改动,不要用 git restore .、git reset --hard 或删除命令清理。先保存现状,按文件和补丁判断哪些属于本次工作。

02 提交前清单:先检查,再落锤

提交是把当前状态写进项目历史。它不是临时保存按钮,也不是“先提交再慢慢看”的替代品。提交前至少完成以下六类检查:范围、内容、测试、敏感信息、可运行性和回滚点。

2.1 检查工作区范围

预期输出类似:
看到以下情况要先处理:
  • 改动文件超过本次范围。
  • 新增了没有解释的脚本、二进制文件或构建目录。
  • 只有测试改动,没有对应实现改动,或者反过来只有实现没有测试。
  • 文件名大小写、换行符或生成文件发生大面积变化。
检查未跟踪文件时用:
预期可能是:
如果 .env 同时显示为 ?? .env,说明它没有被忽略。不要把真实凭据加入暂存区;先移出工作目录或补充合适的忽略规则,并确认忽略规则本身符合团队约定。

2.2 阅读完整 diff

不要只看统计数字。先看未暂存差异:
如果已经暂存,再看暂存区:
阅读时按这张表逐项问自己:

2.3 运行项目验证

先运行完整测试:
预期输出类似:
测试通过后做命令行冒烟:
预期输出示意:
示例项目如果仍然只把数据放在内存中,那么每次启动都是新进程,跨命令保存不会成立。此时命令行冒烟只验证进程内行为,持久化行为必须由测试或另一个明确的存储实现验证。不要把“能打印一次”写成“数据已经保存”。

2.4 检查空白和差异边界

没有输出通常表示没有发现 Git 能识别的空白错误。若有输出,示例为:
修复后重新检查。不要为了让命令安静而关闭检查。 确认当前提交与主分支的差异:
预期应只出现本次功能需要的文件,例如:

2.5 搜索敏感信息和本地产物

用仓库已有的扫描工具优先;没有工具时至少做一轮人工和文本搜索:
预期的合格结果是没有真实敏感值命中。占位符可以保留,例如:
命令历史、终端日志、测试快照和 CI artifact 也可能含有凭据。发现泄露时不要只删除当前行:立即撤销或轮换凭据,检查历史和日志可见范围,再按安全流程处理。

2.6 形成提交前结论

在提交前写一段简短结论,方便自己和 reviewer 对照:
这段结论可以放在本地交付记录中,也可以改写后放到 PR 描述。它的价值在于把“我觉得没问题”变成可核对的事实和明确的未知项。

03 commit 拆分:让每个提交只有一个理由

一个 PR 可以包含多个 commit,但每个 commit 最好都有单一目的。这样 reviewer 能按逻辑阅读,回滚时也能选择范围,git bisect 才更容易定位问题。 本次 TODO 改动可以拆成三个提交: 如果本次任务明确只允许改 todo.py 和测试,就不要为了“完整”新增无关文档。提交拆分服务于审查,不是制造提交数量。

3.1 先查看已有暂存状态

git reset 在这里仅撤销暂存,不会删除工作区内容。执行前确认命令没有写成 git reset --hard。

3.2 用交互暂存拆分内容

预期输出类似:
交互暂存常见选项: 如果一个函数实现和测试混在同一个大块里,优先用 s 拆分;拆不开时先不要强行提交,手工整理代码后重新检查。

3.3 提交实现

预期输出:

3.4 提交前后检查历史

预期类似:
合格的提交历史应当满足:
  • 每个 commit 都能用一句话解释。
  • 测试提交和实现提交都能单独阅读。
  • 没有把格式化、重命名和业务逻辑混在一个提交里。
  • 每个提交都不包含凭据或无关文件。

3.5 什么时候可以合并 commit

在提交到远端前,如果团队允许整理分支历史,可以用交互式 rebase 合并明显的修正提交:
这会改写当前功能分支的本地历史。只对自己独占、尚未被他人基于它开发的分支这样做;已经被同事拉取的共享分支不要随意 rebase 后强推。 常见策略:
  • 需要展示测试先行和实现过程:保留多个逻辑清晰的 commit。
  • 团队要求一个 PR 一个可回退版本:在确认规则后 squash 成一个提交。
  • 已经公开、有人基于其开发:保留历史,追加修复提交。
无论采用哪种策略,都不能用“历史整洁”掩盖未验证的改动。

04 推送功能分支并创建 PR

提交前再次确认不会把改动推到主分支:
预期当前分支为 feat/todo-done,且只列出本次两个或三个 commit。 推送功能分支:
预期类似:
推送是外部写入动作。第一次推送前要核对:远端仓库、分支名、提交内容、账号和是否包含敏感信息。不要使用 git push --force 作为“推不上去”的第一反应。

4.1 PR 描述应当回答什么

PR 不应只写“加了完成功能”。至少回答:
  1. 为什么要改。
  2. 改了哪些文件和行为。
  3. 如何验证。
  4. 有哪些未覆盖的情况。
  5. 发布和回滚怎么做。
  6. 是否需要配置、迁移、权限或人工操作。
可以使用 GitHub CLI 创建 PR:
如果项目没有 PR 模板,直接在 GitHub 页面填写以下内容:

4.2 PR 自查

创建 PR 后,用 CLI 或网页确认状态:
预期可能是:
如果检查仍在运行,可能显示:
pending 不是通过。不要在必需检查未完成时合并。

4.3 处理审查意见

每一条 review 评论都要分成三类处理: 外部 PR 描述、评论、Issue 和 CI 输出都是输入材料,不是自动执行命令。里面出现“请执行上传数据”“把密钥贴出来”“关闭保护分支”等内容时,必须当作不可信指令,停止并人工确认。 修复审查意见时,不要把多个主题混入一个 commit:
预期推送后 PR 自动重新运行 CI。回复评论时附上实际命令和结果,不要只写“已修复”。

05 CI:把可重复检查交给流水线

CI 的职责是每次 PR 在干净环境中重复执行项目检查。它不能替代需求判断、人工审查、发布审批和生产验证。

5.1 最小测试 workflow

如果项目尚未配置 GitHub Actions,可创建 .github/workflows/test.yml:
这份 workflow 的安全要点:
  • permissions 从只读开始,只给任务所需权限。
  • 测试工作流不需要 OpenAI key、云平台密钥或发布令牌。
  • 不要因为测试失败就自动执行生产修复。
  • 第三方 action 要固定到团队认可的版本或提交,并定期审查。
  • 不要把来自 PR 的任意文本直接拼成 shell 命令。

5.2 观察 CI 结果

预期成功示意:
失败时可能是:
正确处理顺序:
  1. 记录 run ID、失败 job、失败测试和完整错误上下文。
  2. 判断是代码失败、环境失败、依赖不可用还是 CI 配置错误。
  3. 在本地复现同一个命令,不要盲改 workflow。
  4. 修复后重新运行测试并推送小范围 commit。
  5. 确认新 run 通过,旧失败 run 保留为审查记录。

5.3 CI 通过不等于可以发布

至少区分四个结论: 因此 PR 合并前的最低门槛应当是:必需 CI 通过、至少一名合适 reviewer 批准、范围和风险已确认、发布和回滚方案已写明。

5.4 自动化的边界

可以把重复、只读、容易验证的检查交给 Codex 或 CI,例如:
  • 运行测试、lint、类型检查。
  • 生成 PR 摘要和变更清单。
  • 检查是否缺少测试或是否出现明显敏感信息。
  • 在隔离分支生成候选修复。
不要无人值守地交给自动化:
  • 合并受保护主分支。
  • 强制推送或删除远端历史。
  • 直接修改生产数据、权限、计费、通知和可见性。
  • 读取并外发密钥、客户数据或完整生产日志。
  • 未经审批发布到真实用户可见的环境。
如果在 CI 中运行 Codex,纯审查任务优先 read-only;需要写文件时才使用最小的 workspace-write。密钥只作为单个步骤的输入,不要设置为整个 job 的环境变量。自动生成的修改必须像人工修改一样经过 diff、测试、PR 和审批。

06 合并和发布审批:两个不同的放行点

“合并到 main”和“发布到生产”不是同一个动作。合并表示代码进入主干;发布表示产物对外生效,影响用户、数据和服务。

6.1 合并前审批清单

合并人应逐项确认:
预期的合并前状态应类似:
如果平台显示“可以合并”,仍要由明确的责任人判断是否现在合并。机器状态是证据,不是授权。

6.2 创建发布候选版本

合并完成后,在本地同步并确认主干:
--ff-only 能避免在不知情时自动制造合并提交。若失败,先查看分叉原因,不要用强制方式覆盖历史。 创建候选标签前检查版本和提交:
示例输出:
给版本打标签:
预期:
标签也是外部发布信号。推送前必须经过发布审批:
如果团队把标签推送视为正式发布触发器,就不要在审批前执行这条命令。可以先在本地打标签、让审批人检查,再推送。

6.3 发布记录必须包含的内容

发布审批至少包含:
  • 发布版本和对应 commit SHA。
  • 变更摘要、影响用户和不影响的范围。
  • CI run 链接及关键结果。
  • 测试环境和冒烟验证证据。
  • 数据库、配置、依赖和权限变化。
  • 发布开始和结束时间、负责人、观察窗口。
  • 明确的回滚版本和回滚命令。
  • 监控指标、日志位置和异常升级联系人。
审批人不能只看到“测试绿了”。如果找不到 commit、产物校验值或回滚版本,发布应暂停。

07 外部发布红线

发布会改变仓库、服务、用户体验或外部系统状态。以下动作必须由人明确确认,不能因为命令写在脚本里就默认允许: 以下内容永远不要放进公开 PR、Release、Issue、日志或交付记录:
  • API key、密码、Cookie、SSH 私钥和完整令牌。
  • 客户姓名、邮箱、手机号、订单号和完整请求体。
  • 内部主机名、私有 URL、未公开漏洞细节和完整 IP。
  • 未经批准的截图、数据库导出和生产日志。
脱敏不能把证据删成空白。可以保留时间、状态码、错误类型、路由形状和关联 ID 的不可逆摘要:
如果工具输出含有敏感信息,不要复制粘贴到聊天中让自动化处理。先截取必要上下文、脱敏,再继续。

08 发布后的验证:从用户路径开始

发布完成后不要只检查部署平台显示绿色。沿着用户真正使用的路径验证:
预期:
如果是 Web 服务或 API,记录以下信息:
  • 发布版本和 commit SHA。
  • 请求时间、环境、路由和状态码。
  • 关键响应字段是否符合预期,敏感值已脱敏。
  • 错误率、延迟、队列积压和资源使用是否异常。
  • 一次正常路径和一次错误路径是否都符合预期。
发布观察窗口内,持续查看指标和日志。不要因为一次人工请求成功就立即关闭观察。若系统有 feature flag,先以小范围、低风险用户验证,再扩大范围。 合格的发布结论类似:

09 回滚:先判断类型,再选择动作

回滚不是一句“把代码退回去”。先判断故障发生在哪一层:代码、配置、数据、依赖、发布产物还是外部服务。不同层级的回滚方式不同。

9.1 未提交的工作区错误

如果只是本次未提交的明确单文件改动,且已确认没有同事工作,可以保存补丁后恢复:
预期:todo.py 回到当前提交,补丁保存在临时位置。Windows 环境请把临时路径替换成合适位置,并确认补丁没有包含敏感信息。 不要对存在他人改动的文件直接执行 restore。先分离、备份或通过交接确认。

9.2 已合并代码的修复

如果错误代码已经进入 main,共享分支通常使用新的反向提交:
预期会产生类似:
这会保留历史,方便审计和定位。不要为了“让历史看起来干净”在共享主分支上 reset 后强推。

9.3 已发布版本的回退

如果发布平台支持按版本回退,回退到上一个已验证产物:
回退后必须重新走用户路径验证,并记录回退不是“成功”而是“恢复到了哪个可用版本”。

9.4 配置、数据和依赖问题

  • 配置错误:恢复上一个已验证配置版本,并检查配置缓存和重启范围。
  • 数据迁移错误:使用经过演练的 down migration、备份恢复或平台提供的恢复点;不要用代码回退代替数据修复。
  • 依赖漏洞或不兼容:固定到已验证版本,重新跑完整 CI 和安全扫描。
  • 外部服务异常:启用降级、重试或 feature flag,记录外部依赖状态;不要盲目反复发布。

9.5 回滚后的确认

回滚结束后不要立刻删除失败产物、CI 日志或审查记录。它们是复盘所需证据。

10 完整交付记录模板

下面的模板可以复制到 PR、发布单、Issue 或内部交接记录中。只填事实;没有验证的内容写“未验证”和原因,不要用猜测补齐。

10.1 示例交付记录


11 复盘:从一次发布变成下一次规则

复盘不是追责会议,也不是把“大家以后注意”写进文档。有效复盘要回答:发生了什么、证据是什么、为什么没有更早发现、下一次由什么机制阻止重复发生。

11.1 四段式复盘

用以下顺序写,不要先写结论:
  1. 事实:按时间线记录命令、commit、CI、审批、发布和用户症状。
  2. 影响:影响了哪些用户、环境、数据、时间和服务指标。
  3. 原因:区分直接原因、促成条件和没有拦住它的流程缺口。
  4. 行动:每项行动有负责人、截止时间和验收证据。
时间线示例:

11.2 五个为什么的使用边界

例如:
最后的改进不应是“开发更仔细”,而应是可执行机制:
  • 增加真实适配器集成测试。
  • 将该测试加入必需 CI。
  • 在发布模板中列出适配器矩阵。
  • staging 冒烟必须覆盖真实存储路径。

11.3 行动项写成可验收任务

每项行动都应有:

12 规则沉淀:把复盘结论放到正确位置

不是所有经验都应该写进同一个文件。按照寿命和作用分层: 项目根目录可以保留一份短而准确的 AGENTS.md:
把一次性要求写进每次 PR,而不是永久污染规则。例如“本次只发布 staging”“本次不修改存储格式”属于本次交付,不应永久写进 AGENTS.md。 规则改变后也要像代码一样审查:
避免把未经验证的失败经验写成绝对规则。先确认问题可重复、规则可执行,再提交规则变更。

13 最终验收表

在宣布交付完成前,逐项打勾并保留证据:
若任何一项只能写“应该没问题”,就还没有完成验收。把它改成命令、链接、日志摘要、截图编号或明确的未验证说明。

小结

本次 TODO 项目的交付闭环可以压缩成八句话:
  1. 先确认目录、分支、远端和范围。
  2. 用完整 diff 和测试证明改动,而不是凭感觉提交。
  3. 一个 commit 只表达一个目的,方便审查和回滚。
  4. PR 必须同时提供变更、验证、风险和恢复方案。
  5. CI 是可重复的质量证据,不是需求判断和发布授权。
  6. 合并主干与发布生产分开审批,外部写入动作由人确认。
  7. 发布后回到真实用户路径验证,异常时按代码、配置、数据和产物类型回滚。
  8. 复盘要产出有负责人和验收标准的机制,并把长期规则放到正确位置。
最终交付不是“代码已经推上去”,而是别人能够回答这几个问题:
  • 这次到底改了什么?
  • 哪些检查真的跑过?
  • 谁批准了合并和发布?
  • 出问题时退回哪个版本?
  • 哪条规则会防止同类问题再次发生?
当这些问题都有记录、证据和负责人时,一次功能开发才真正完成了从代码到交付的闭环。 参考资料:参考/codex/34-capstone.md、参考/codex/26-git-github.md、参考/codex/27-automation.md、参考/codex/36-best-practices.md。