关于 ship
ship 是一套自动化工作流,可将功能分支变更依次送审、测试、提交,更新发布元数据并创建拉取请求。它会把变更整理成便于二分定位的提交,并在测试失败或需要人工判断时暂停。
主要功能
- 01测试前合并基准分支
- 02运行项目测试与合并前审查
- 03自动修复符合条件的审查问题
- 04将变更整理为便于二分的提交
- 05按需更新版本与变更日志
- 06准备拉取请求
适用场景
- 01将功能分支变更发布为拉取请求
- 02在合并前运行检查和代码审查
- 03把大型变更拆分为逻辑提交
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ borkweb/ skills --skill ship安装后在 Claude Code 中输入/ship确认已加载。
详细介绍
步骤 0:检测基础分支
确定此 PR 的目标分支。后续所有步骤中都将其称为“基础分支”。
运行共享辅助程序:
BASE=$(bash "$CLAUDE_PLUGIN_ROOT/scripts/detect-base-branch.sh")
该辅助程序依次检查当前 PR 的 baseRefName、仓库的默认分支,然后回退到 main。它始终会输出一个可用的分支名称。
Ship:全自动 Ship 工作流
你正在运行 /ship 工作流。这是一个非交互式、全自动工作流。任何步骤都不要请求确认。用户输入了 /ship,这就意味着立即执行。直接运行完整流程,并在最后输出 PR URL。
仅在以下情况停止:
- 当前处于基础分支(中止)
- 存在无法自动解决的合并冲突(停止并显示冲突)
- 测试失败(停止并显示失败情况)
- 合并前审查发现需要用户判断的 ASK 项
- 需要进行 MINOR 或 MAJOR 版本升级(询问 — 参见步骤 5)
绝不要因以下情况停止:
- 存在未提交的更改(始终包含这些更改)
- 选择版本升级类型(自动选择 MICRO 或 PATCH — 参见步骤 5)
- CHANGELOG 内容(根据提交自动生成)
- 提交消息审批(自动提交)
- 多文件变更集(自动拆分为可二分排查的提交)
- 检测到 TODOS.md 中已完成的条目(自动标记)
- 可自动修复的审查结果(死代码、N+1、过时注释 — 自动修复)
步骤 1:起飞前检查
-
检查当前分支。如果处于基础分支,中止:“你当前处于基础分支。请从功能分支执行 Ship。”
-
运行
git status(绝不要使用-uall)。始终包含未提交的更改 — 无需询问。 -
运行
git diff <base>...HEAD --stat和git log <base>..HEAD --oneline,了解即将发布的内容。 -
检测关联的问题。 扫描以下位置中的问题引用:
- 分支名称(例如,
fix/123-broken-login、feature/GH-45) - 提交消息(
Fixes #123、Closes #45、Relates to #78) - 引用了问题的 TODOS.md 条目 收集所有问题编号 — 在步骤 9 中将其写入 PR 正文。
- 分支名称(例如,
故障恢复: 如果 git status 或 git log 失败(例如,索引损坏),运行 git fsck 并报告结果。不要继续。
步骤 2:合并基础分支(在测试之前)
获取基础分支并将其合并到功能分支,以便针对合并后的状态运行测试:
git fetch origin <base> && git merge origin/<base> --no-edit
如果存在合并冲突: 如果冲突简单(VERSION、schema、CHANGELOG 排序),尝试自动解决。如果冲突复杂或含糊不清,停止并显示冲突。
如果已是最新状态: 静默继续。
故障恢复: 如果 git fetch 失败(网络、身份验证),报告错误并停止。如果 git merge 因冲突以外的原因失败(例如,历史记录不相关),报告错误并停止。
步骤 3:运行测试(针对合并后的代码)
根据代码库检测项目的测试命令(package.json 脚本、Makefile、Gemfile 等),并运行该命令。
如果有任何测试失败: 显示失败情况并停止。不要继续。
如果全部通过: 静默继续 — 简要记录测试数量即可。
故障恢复: 如果测试运行器本身无法执行(缺少依赖、配置错误),清楚地报告错误。不要将“运行器崩溃”视为“测试通过”。
步骤 3.5:合并前审查(委托给审查技能)
根据 /review 技能的检查清单进行审查,不要重复实现其逻辑。
-
从审查技能目录中读取
checklist.md(skills/gstack/review/checklist.md)。如果未找到,则停止并报告。 -
从审查技能目录中读取
design-checklist.md(skills/gstack/review/design-checklist.md)。留待步骤 3.5.4 使用。 -
运行
git diff origin/<base>获取完整差异。 -
按照检查清单分两轮审查:
- 第 1 轮(严重问题): SQL 与数据安全、迁移与架构安全、竞态条件与并发、身份验证与权限缺陷、大模型输出信任边界、枚举与值完整性、API 契约破坏性变更
- 第 2 轮(提示性问题): 错误处理反模式、条件副作用、魔法数字与字符串耦合、无用代码与一致性、大模型提示词问题、测试缺口、加密与熵、时间窗口安全、边界处的类型强制转换、视图/前端、性能与打包影响
-
检查前端变更:
bash "$CLAUDE_PLUGIN_ROOT/scripts/detect-frontend-files.sh" "$BASE"如果有文件输出,则应用设计检查清单(在步骤 2 中加载)。先检查仓库根目录中的 DESIGN.md——其中认可的模式不会被标记。
-
根据检查清单中的 Fix-First 启发式,将每项发现归类为 AUTO-FIX 或 ASK。严重问题倾向于 ASK;提示性问题倾向于 AUTO-FIX。
-
自动修复所有 AUTO-FIX 项。 每项修复输出一行:
[AUTO-FIXED] [file:line] 问题 → 采取的措施 -
如果仍有 ASK 项, 则在一个 AskUserQuestion 中列出:
- 每项注明编号、严重程度、问题和建议修复方案
- 每项提供选项:A) 修复 B) 跳过
- 总体建议
-
所有修复(自动修复和用户批准的修复)完成后:
- 如果应用了任何修复:提交已修复的文件,然后在继续前重新运行测试。
- 如果没有应用任何修复:继续步骤 4。
-
输出摘要:
合并前审查:N 个问题 — M 个自动修复,K 个待询问(J 个已修复,L 个已跳过)
保存审查输出——该内容将在步骤 9 中写入 PR 正文。
步骤 3.7:对抗性审查
当差异符合以下任一条件时,通过 Agent 工具派遣一名对抗性审查员:
- 更改超过 200 行
- 涉及身份验证、支付或安全相关文件
- 引入新的外部服务集成
- 包含新的加密操作或机密信息处理
对于少于 200 行且不涉及敏感文件的差异,跳过此步骤。
子代理提示词:
“使用 git diff origin/<base> 阅读此分支的差异。像攻击者和混沌工程师一样思考。你的任务是找出这段代码在生产环境中可能发生故障的方式。检查:边缘情况、竞态条件、安全漏洞、资源泄漏、故障模式、静默数据损坏、会静默地产生错误结果的逻辑错误、吞掉故障的错误处理,以及信任边界违规。采取对抗性视角。务必全面。不要称赞——只指出问题。对于每项发现,将其归类为 FIXABLE(你知道如何修复)或 INVESTIGATE(需要人工判断)。”
呈现审查发现。FIXABLE 发现进入 Fix-First 流程(自动修复;如果代码有更改,则重新运行测试)。INVESTIGATE 发现仅作提示。
保存对抗性审查输出——该内容将在步骤 9 中写入 PR 正文。
第 4 步:提交(可二分定位的代码块)
目标: 创建小型、逻辑清晰的提交,让 git bisect 能够顺利工作。
-
将更改分组为逻辑提交。每个提交应包含一项连贯的更改。
-
提交顺序——默认采用以下顺序,并根据项目结构调整:
- **先提交基础更改:**迁移、配置、架构、路由、依赖项
- **接着提交核心逻辑:**模型、服务、库(及其测试)
- **最后提交表层更改:**控制器、视图、CLI 命令(及其测试)
- **元数据始终最后提交:**在最后一次提交中包含 VERSION + CHANGELOG + TODOS.md
对于库或 CLI 工具,可调整顺序:核心 API → 内部模块 → 公共接口 → 元数据。 对于 monorepo,按软件包而不是按层分组。
-
规则:
- 模块/服务及其测试应放在同一次提交中
- 如果总 diff 较小(跨 < 4 个文件且少于 50 行),可以只创建一次提交
- 每次提交都必须独立有效——不能有损坏的导入
-
撰写提交消息:
<type>: <summary>(feat/fix/chore/refactor/docs)
第 5 步:版本升级(自动决定)
如果存在 VERSION 文件:
-
读取当前版本。
-
根据更改内容而非更改数量自动决定升级级别:
MICRO(自动)——仅内部更改:
- Bug 修复、拼写错误、注释更新、仅涉及测试的更改
- 不改变公共 API 或行为的重构
- 文档更新
PATCH(自动)——面向用户但不具破坏性的更改:
- 会改变可观察行为的 Bug 修复
- 性能改进
- 带默认值的新可选参数
- 依赖项更新
MINOR(询问)——新增能力:
- 新端点、命令、功能或公共 API 方法
- 新数据库表或重要的架构新增内容
- 新的外部服务集成
MAJOR(询问)——破坏性更改:
- 删除或重命名公共 API 方法/端点
- 更改响应格式或必需参数
- 需要进行数据转换的数据库迁移
- 停止支持某个运行时/平台
-
计算并写入新版本。
如果不存在 VERSION 文件,则跳过此步骤。
第 6 步:CHANGELOG(自动生成)
如果存在 CHANGELOG.md:
-
阅读文件头以了解格式。
-
使用第 4 步中的提交消息作为主要依据,自动生成条目:
- 前缀为
feat:的提交 →### Added - 前缀为
fix:的提交 →### Fixed - 前缀为
refactor:/chore:的提交 →### Changed - 删除功能的提交 →
### Removed - 对未分类的更改,通过 diff 分析补充分类
- 编写简洁、描述清晰的项目符号条目
- 在文件头之后插入条目,日期使用今天
- 前缀为
**不要询问用户描述更改内容。**根据提交和 diff 推断。
如果不存在 CHANGELOG.md,则跳过此步骤。
第 6.5 步:TODOS.md(自动更新)
如果存在 TODOS.md:
- 将 diff 与未完成的 TODO 逐项对照
- 自动标记已完成的项目——仅当 diff 明确表明工作已完成时才标记(谨慎判断)
- 将已完成项目移至 Completed 部分,并注明版本和日期
- 输出摘要:
TODOS.md: N 项已标记为完成。M 项仍未完成。
如果不存在 TODOS.md,则静默跳过。
第 6.7 步:计划完成情况审查
如果此分支存在计划文件,请核实计划中的所有内容是否都已交付。如果未找到计划文件,则静默跳过。
计划文件发现
- 对话上下文(优先): 如果宿主 Agent 的系统消息中提到了计划文件路径,请直接使用该路径。
- 文件系统搜索(备用): 在
docs/plans/、docs/designs/、.claude/plans/和项目根目录中查找与当前分支名称匹配的 Markdown 文件。选择最近修改的匹配文件。如果有多个候选文件且没有明确匹配项,则视为“未找到计划文件”。 - 验证: 读取前 20 行。如果内容看起来与当前分支的工作无关,则视为“未找到计划文件”。
如果未找到计划文件: 跳过并输出“未检测到计划文件 — 跳过计划完成情况审查。”继续执行步骤 7。
作为子 Agent 分派
使用 Agent 工具,并将 subagent_type 设为 "general-purpose"。子 Agent 在全新上下文中读取计划和差异;父 Agent 获取结论。子 Agent 提示词:
审查当前分支的计划完成情况。基准分支:
<base>。计划文件:<path>。提取可执行项,包括复选框(
- [ ]/- [x])、编号实现步骤、祈使句(“添加 X”“创建 Y”)、文件级规范和测试要求、数据模型变更。最多 50 项。忽略背景/概述/问题部分、开放问题(“TBD”“TODO: decide”)、明确推迟的事项(“Out of scope:”“Future:”“P2:”“P3:”)以及以往的审查报告。对每项进行分类,判断如何验证:
- DIFF-VERIFIABLE — 在此仓库的
git diff <base>...HEAD中应能看到。- CROSS-REPO — 指向同级仓库中的文件。如果路径可访问,尝试执行
[ -f <path> ];如果不可访问,则标为 UNVERIFIABLE。- EXTERNAL-STATE — 指向外部系统中的状态(DNS、OAuth 允许列表、SaaS 配置)。始终标为 UNVERIFIABLE。
- CONTENT-SHAPE — 文件必须符合特定规范。在标为 UNVERIFIABLE 之前,先运行项目验证工具(
validate-*、lint-docs等)。路径具体性规则: 如果计划项指定了具体文件系统路径,则必须根据
[ -f <path> ]标记为 DONE 或 NOT DONE。只有路径确实抽象或无法访问时,才可标为 UNVERIFIABLE。每项的状态:
- DONE — 差异或文件系统中有明确证据。注明文件。
- PARTIAL — 已完成部分工作,但尚未完成。
- NOT DONE — 验证已执行,且得到了否定证据。
- CHANGED — 以不同方式实现,但达成了相同目标;注明差异。
- UNVERIFIABLE — 无法证实或证伪。注明具体的人工检查项。
诚信规则: 处理某项交付内容的代码,并不等于交付内容本身。提供 Markdown 提取库,不等于提供 Markdown 文件。如无法确定是 DONE 还是 UNVERIFIABLE,优先选择 UNVERIFIABLE。
按类别(Implementation / Tests / Migrations / Cross-Repo / External)分组,输出 Markdown 检查清单,并为每项添加
[STATUS]前缀。最后用一行总结:COMPLETION: X/Y DONE, A PARTIAL, B NOT DONE, C CHANGED, D UNVERIFIABLE。
门控逻辑
子 Agent 返回后,按优先级顺序评估:
-
任何 NOT DONE 项(已知尚未完成的工作)。使用 AskUserQuestion:
- 显示完成检查清单。
- “计划中有 {N} 项尚未完成(NOT DONE)。”
- 选项:A) 停止 — 在发布前完成实现。B) 仍然发布 — 作为 P1 TODO 延后处理。C) 有意移除 — 从范围中删除。
- 建议:核心功能缺失时选 A;文档/配置的小问题选 B。
- 选 A:停止。选 B:继续,对每项将
Deferred from plan: {path}追加到 TODOS.md。选 C:继续,在 PR 正文中注明。
-
任何 UNVERIFIABLE 项(仅在 NOT DONE 项已解决或不存在后处理)。必须逐项确认 — 不得一揽子确认。 逐项循环:
- 对每项使用 AskUserQuestion,询问该项的具体手动检查(“确认:
~/path/to/file是否存在?”,而不是“是否已检查所有项目?”)。 - 每项选项:Y) 确认已完成 — 引用你验证过的内容(自由文本)。N) 尚未完成 — 阻止发布,返回优先级 1。D) 有意移除。
- 任何一项选 N:停止。全部选 Y 或 D:继续,在 PR 正文中加入
## Plan Completion — Manual Verifications,列出每项选 Y 的证据,并将每项选 D 标记为已移除。 - 上限: 如果 UNVERIFIABLE 项超过 5 项,先将它们列出并询问:(1) 逐项确认 [推荐];(2) 停止并缩小范围;(3) 明确接受一揽子确认(警告:这正是此门控要防止的失败模式)。
- 对每项使用 AskUserQuestion,询问该项的具体手动检查(“确认:
-
仅有 PARTIAL 项(没有 NOT DONE 或 UNVERIFIABLE): 继续,并在 PR 正文中注明。不阻止发布。
-
全部为 DONE 或 CHANGED: 通过。继续。
子 Agent 失败时默认拒绝通过
如果子 Agent 失败或返回格式错误的输出,不要默默放行。尝试在父上下文中直接运行提取一次。如果仍然失败,则通过 AskUserQuestion 呈现:
“无法运行计划完成情况审计({reason})。选项:A) 跳过审计并仍然发布 — 在 PR 正文中记录已跳过审计。B) 停止并修复审计。”
建议:B。默默地默认放行正是此门控要防止的失败模式。
PR 正文
在第 9 步的 PR 正文中添加 ## Plan Completion 部分,并附上检查清单摘要。如果通过逐项流程确认了 UNVERIFIABLE 项,还要添加 ## Plan Completion — Manual Verifications,列出每项确认。
Step 7: 验证门控
绝对原则:没有最新的验证证据,不得声称已完成。
推送前,如果代码在第 4 至 6.5 步期间发生过更改,请重新验证:
- 如果第 3 步运行测试后有任何代码更改(例如评审修复),请重新运行测试。粘贴最新输出。
- 如果项目有构建步骤,请运行构建。
- 如果测试失败: 停止。不要推送。
Step 8: 推送
git push -u origin <branch-name>
恢复处理: 如果推送失败:
- 分支保护/pre-receive hook 拒绝: 报告错误。不要强制推送。说明用户需要解决的问题(例如所需的评审者、状态检查)。
- 身份验证失败: 报告并停止。
- 远程分支分叉(非快进): 报告并停止。不要强制推送。建议运行
git pull --rebase origin <branch-name>,然后重新验证。
Step 9: 创建 PR
Step 9.1: 检测 PR 元数据
-
标签: 检查仓库的标签惯例:
gh label list --limit 20 --json name --jq '.[].name'自动应用与更改类型匹配的标签:
- 修复类提交使用
bug/bugfix - 新功能类提交使用
enhancement/feature - 锁文件有更改时使用
dependencies - 如果仓库使用规模标签,则按差异行数应用(S/M/L) 如果没有匹配的标签,则跳过 — 不要创建新标签。
- 修复类提交使用
-
评审者: 检查 CODEOWNERS:
cat .github/CODEOWNERS 2>/dev/null || cat CODEOWNERS 2>/dev/null如果存在 CODEOWNERS,让 GitHub 自动分配评审者。否则,跳过评审者分配。
-
草稿模式: 如果符合以下任一条件,则以草稿形式创建(
--draft):- 分支名称以
wip/或draft/开头 - 任意提交消息包含
[WIP]或[DRAFT] - TODOS.md 中有与此分支工作相关且标记为进行中的项目
- 分支名称以
第 9.2 步:创建 PR
gh pr create --base <base> \
--title "<type>: <summary>" \
[--draft] \
[--label "<label1>" --label "<label2>"] \
--body "$(cat <<'EOF'
## Summary
<bullet points from CHANGELOG or diff summary>
<If linked issues were detected in Step 1:>
Closes #<issue>
<or>
Relates to #<issue>
## Pre-Landing Review
<findings from Step 3.5, or "No issues found.">
<If adversarial review was run in Step 3.7:>
## Adversarial Review
<INVESTIGATE findings, or "No issues found.">
<FIXABLE findings that were applied are noted as resolved.>
## TODOS
<completed items, or "No TODO items completed in this PR.">
<If Step 6.7 ran:>
## Plan Completion
<checklist summary from Step 6.7, or "No plan file detected.">
<If UNVERIFIABLE items were confirmed via per-item flow:>
## Plan Completion — Manual Verifications
<each Y'd item with user evidence; each D'd item marked "intentionally dropped">
## Test plan
- [x] All tests pass
- [x] Verification gate passed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
输出 PR URL。
**恢复方法:**如果 gh pr create 失败:
- **PR 已存在:**运行
gh pr view --json url --jq .url,并输出现有 URL。主动提出更新现有 PR 正文。 - **缺少权限:**报告情况并停止。
- **网络错误:**重试一次。如果仍然失败,报告情况并停止。代码已推送——用户可以手动创建 PR。
重要规则
- **绝不跳过测试。**如果测试失败,停止。
- **绝不强制推送。**仅使用常规
git push。 - 绝不询问无关紧要的确认事项(例如“准备好推送了吗?”“创建 PR 吗?”)。但以下情况必须停止:版本升级(MINOR/MAJOR)以及上线前审核中的 ASK 项。
- 将提交拆分,以便进行二分排查——每个提交对应一项逻辑变更。
- **必须保守地判断 TODOS.md 中任务是否完成。**仅当差异明确显示任务已完成时,才将其标记为完成。
- **没有最新的验证证据,绝不推送。**如果第 3 步之后代码有变更,推送前必须重新运行验证。
- **优先恢复,而非放弃。**每个步骤都包含恢复指南。放弃前应遵循指南。
- 目标是:用户输入
/ship后,接下来看到的就是 PR URL。