mcpField
P
prod-push

prod-push

github.com/davidondrej云与 DevOps4.2k59 次安装今天更新
代码仓库

关于 prod-push

用于通过 DeepAPI 的 main 分支、CI 和 Vercel 流程发布生产环境变更的技能,面向发布操作人员;要求获得用户明确授权,并跟踪部署与健康检查。

主要功能

  1. 01仅推送至 main 的流程
  2. 02合并 worktree 变更的步骤
  3. 03跟踪 CI 与部署
  4. 04验证生产环境健康状态
  5. 05事故恢复指引

适用场景

  1. 01发布经授权的生产变更
  2. 02在主检出目录落地 worktree 提交
  3. 03排查 CI 或部署失败
  4. 04事故期间跟踪恢复提交

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidondrej/skills --skill prod-push

安装后在 Claude Code 中输入/prod-push确认已加载。

详细介绍

生产推送

仅在用户明确要求推送后才执行。绝不要自行推送。

此处的发布方式

  • 直接推送到 main。不创建 PR,不使用旁支。
  • GitHub Actions CI(.github/workflows/ci.yml)始终报告必需的 verify。代码会运行类型检查和 lint;仅在相关路径发生变化时才运行生成文件、浏览器、数据库和 Windows 检查。完整测试套件每晚运行。
  • Vercel(项目 deep-api)负责生产构建,并为每次 main 推送构建,包括仅修改文档的提交(ADR 0161 移除了忽略构建规则;不要重新启用)。仅当 verify 通过时才会将其部署到 Production。生产环境由 deepapi.co 提供服务。
  • 同一分支上的新 CI 运行会取消正在进行的运行(cancel-in-progress: true)。

从工作树落地更改

Agent 在关联工作树中开发(cursor/*、agent/* 分支);以下流程不会在这些分支上运行。要发布工作树中的更改:

  1. 在工作树中:在自己的分支上提交所有更改。绝不要在工作树中提交到 main。
  2. 在主检出目录(~/code/DeepAPI)中:工作区必须干净(git status --porcelain --untracked-files=no 不输出任何内容)。如果工作区因不属于你的 WIP 而有未提交更改,请停止并告知用户——绝不要先将其暂存。
  3. 执行 git pull --rebase origin main,然后合入你自己的分支:git merge <branch>(可能会快进),或者在分支历史混乱时 cherry-pick 其提交。一次只合入一个分支——绝不要在一次操作中合入两个 Agent 分支。
  4. 在主检出目录中解决冲突,重新运行相关检查,然后从下方流程的第 2 步继续。
  5. 推送经验证已上线后,保留工作树和分支——是否删除始终由用户决定,而不是你。

流程

重复执行,直到确切的更改已上线。

# 0. Fail closed before touching Git. Ship only from the primary main checkout:
#    linked worktrees use their own branch's hook and may carry stale safety code.
[ "$(git branch --show-current)" = "main" ] || { echo "prod-push: not on main" >&2; exit 1; }
git_dir=$(git rev-parse --path-format=absolute --git-dir)
git_common_dir=$(git rev-parse --path-format=absolute --git-common-dir)
[ "$git_dir" = "$git_common_dir" ] || { echo "prod-push: linked worktree pushes are forbidden" >&2; exit 1; }
if git config --local --get-regexp '^user\.(name|email)$' >/dev/null; then
  echo "prod-push: unexpected local Git identity override" >&2
  exit 1
fi
if [ "$(git config user.name)" != "David Ondrej" ] || [ "$(git config user.email)" != "<email-address>" ]; then
  echo "prod-push: unexpected effective Git identity" >&2
  exit 1
fi
# If an incident is active and you are not the named recovery owner, STOP.
# Follow docs/runbooks/incident-response.md if you are the owner.
npm run incident:status

# 1. commit ONLY your files. WIP belongs in worktrees, never parked in this
#    primary checkout — it must be clean between pushes. Never `git add -A`,
#    `git add .`, or `commit -a`. If a file you touched also carries someone
#    else's unfinished edits, copy it aside (outside the repo), strip their
#    part, commit yours — and restore the copy only AFTER the push, because
#    step 2 requires a clean tree.
git add <your files> && git commit
[ "$(git log -1 --format='%an <%ae>')" = "David Ondrej <<email-address>>" ] || { echo "prod-push: unexpected commit author" >&2; exit 1; }

# 2. sync + push (other agents also commit to main)
# The pull requires a CLEAN tree. --autostash is BANNED here: replaying
# dirty edits onto fresh upstream commits creates UU conflicts and orphaned
# autostash entries (this bit us on 2026-07-25). If tracked dirt remains
# that is not yours, stop and tell the user — never stash around it.
[ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "prod-push: tree not clean; commit yours or stop" >&2; exit 1; }
# The pre-push hook typechecks the EXACT pushed commit in a clean worktree.
# Incident recovery pushes still run the full suite. Never use `--no-verify`.
git pull --rebase origin main
git push origin main
if git config --local --get-regexp '^user\.(name|email)$' >/dev/null; then
  echo "prod-push: push introduced a local Git identity override; stop" >&2
  exit 1
fi
SHA=$(git rev-parse HEAD)

# 3. Find the CI run immediately, then let GitHub stream it to completion
RUN_ID=""
for attempt in 1 2 3 4 5 6; do
  RUN_ID=$(gh run list --workflow=ci.yml --branch=main --limit=10 \
    --json headSha,databaseId --jq ".[] | select(.headSha==\"$SHA\") | .databaseId")
  [ -n "$RUN_ID" ] && break
  sleep 5
done
[ -n "$RUN_ID" ] || { echo "prod-push: CI run not found" >&2; exit 1; }
gh run watch "$RUN_ID" --exit-status

# 4. CI green -> confirm Vercel promoted this exact commit.
# EVERY main push deploys (ADR 0161), docs-only commits included — always
# verify the deployment; never skip this on a "docs-only" judgment call.
gh api "repos/davidondrej/DeepAPI/commits/$SHA/status" \
  --jq '{state, vercel_logs: [.statuses[] | select(.context=="Vercel") | .target_url][0]}'
DEP=$(gh api "repos/davidondrej/DeepAPI/deployments?sha=$SHA&environment=Production&per_page=1" --jq '.[0].id')
gh api "repos/davidondrej/DeepAPI/deployments/$DEP/statuses" --jq '.[0].state'
# If promotion is still pending, poll every 10 seconds.

# 5. confirm prod is serving
curl -sS -m 15 https://deepapi.co/v1/health

完成 = CI 通过、Production 部署成功,且对应 SHA 的 GET /v1/health 状态正常。然后向用户报告已发布的内容。

正在处理的事故通道

只有 npm run incident:status 指定的负责人可以使用此通道。不要混入无关工作。

# Exactly one minimal recovery commit. The hook verifies that exact commit.
DEEPAPI_INCIDENT_RECOVERY=1 DEEPAPI_INCIDENT_OWNER=<owner> git push origin main
SHA=$(git rev-parse HEAD)

使用常规流程跟踪该确切 SHA 的 CI、Production 部署和健康状态。在第一个 SHA 得出最终结果前,不要推送第二次恢复尝试。只有用户可以决定恢复已得到验证并解除冻结;使用运行手册中的解除命令,并且只提交 .github/incident-freeze.json。

故障模式

CI 失败——获取确切失败步骤和输出:

gh run view <databaseId> --log-failed

在本地复现(npm run verify,或仅运行失败的步骤,例如 npm run typecheck),修复问题、提交,然后重新开始流程。推送前日志是确切提交的权威记录;手动运行检查的是共享工作区,可能包含其他 Agent 的 WIP。新的推送会获得新的 $SHA——跟踪该 SHA。

  • CI 通过,但 Vercel 状态为 failure——Vercel 构建本身出错。使用 npm run build 复现。如果问题出在 Vercel 端(环境变量、平台限制),请停止并向用户提供 vercel_logs 链接——Agent 有意不具备 Vercel CLI 或仪表板访问权限。
  • CI conclusion: cancelled——较新的推送取代了你的推送;你的提交不会自行部署。确认较新的 sha 包含你的更改(git merge-base --is-ancestor $SHA <newer-sha>),并改为跟踪该 sha。
  • 事故期间 CI conclusion: cancelled——停止。未经许可的较新推送进入了 main;告知用户,并在采取其他恢复操作前指出其 SHA。
  • 健康检查返回 {"ok":false}(HTTP 503)——迁移漂移:已部署代码依赖尚未应用到生产数据库的迁移。部署本身仍然成功。Agent 绝不能写入生产环境——告知用户需要应用哪个迁移,然后停止循环。
  • 推送被拒绝(non-fast-forward)——期间有人推送了更改。执行 git pull --rebase origin main 后再次推送。

硬性规则

  • 未经用户明确同意,绝不推送。绝不强制推送。绝不推送旁支。
  • 绝不绕过或弱化 CI 来让检查通过:不得删除或跳过测试,不得使用 --no-verify,不得在检查失败时绕过检查进行合并。
  • npm run smoke:prod 仅限用户运行(会产生真实费用)。免费的生产环境检查是 GET /v1/health。
  • 如果更改包含数据库迁移,用户会手动将其应用到生产环境——在他们完成操作前,健康检查预计会持续失败,并在报告中说明这一点。

评论

支持 Markdown