fix(ci): migrate staging automatically before deploy
Independent Staging Quality Gate / validate (push) Successful in 10m6s
Independent Staging Quality Gate / publish (push) Successful in 4m18s

Quality gate now dispatches Migrate Staging Database, waits for success, then dispatches Deploy staging. Automatic migrate attests the in-progress gate run so the two jobs cannot deadlock. Manual migrate is unchanged. Staging schema changes must stay backward-compatible with the currently deployed app.
This commit is contained in:
jesse-ux
2026-09-15 23:36:09 +08:00
parent 1a73f64ecd
commit 69ede4367f
8 changed files with 192 additions and 27 deletions
@@ -0,0 +1,50 @@
# PROGRESS · staging 部署自动先迁移(2026-09-15
工作树:`.worktrees/staging-auto-migrate-on-deploy-20260915`
分支:`codex/staging-auto-migrate-on-deploy-20260915`
任务书基线:`de47c06d`;开工时 `origin/staging` = **`1a73f64e`**(已含 SHA 留空自动解析)。
本机 Windows。
未开 BUG 号。未改 `CHANGELOG.md`。未改 `deploy-production.yml` / `migrate-production-database.yml` / `deploy-staging.yml`。质量门禁未加入 `staging-mutation`。回滚部署仍不触发迁移。
## 任务状态
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| 1 gate 先触发迁移再部署 | 完成 | dispatch 段:先 migrate,等成功(20 分钟),再 deploy。失败/超时不部署,gate 这一步红 |
| 2 日志说清有没有迁移 | 完成 | `run-staging-migration.sh` 应用前跑 `migration-check`;0=「无待应用迁移」,3=列出文件名。退出码 3 **不**阻断随后的 migrator |
| 3 AGENTS §7.6 + README | 完成 | 向后兼容纪律已写入;staging 正常推送不用手点 |
## 相对任务书 §4.1 的必要偏离
任务书写「只改 quality-gate 的 dispatch 段、不得改 migrate 的校验」。按字面做会死锁:
- 迁移 workflow 原来要求「已经成功的精确 SHA 门禁 run」
- 自动触发时,那次门禁 **还在跑**(正在等迁移),查 `status=success` 必然失败
- 若迁移再去等门禁成功,而门禁又在等迁移,两边一起超时
因此 migrate 增加了 **可选** `gate_run_id`(与 deploy-staging 同名、同语义):
- 自动路径:gate 把自己的 run id 传进去。迁移只核验这是 `backend-quality-gate.yml`、SHA/branch/event 对得上、结论不是失败;**不等**整次 run 结束。artifact 在 dispatch 之前已经上传,可以接着下载。
- 手动路径:不传 `gate_run_id`,仍走原来的「必须找到成功门禁 run」。
没有走让步 2(改成迁移成功后再 dispatch 部署),因为门禁里等待做得到,且硬红线 3 要求迁移失败时 **gate 自己变红**
## 既有断言改动
| 文件 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `staging-backend-workflows.test.ts` 门禁 dispatch | 只锁 `deploy-staging.yml/dispatches` | 先 migrate 再 deploy,并锁超时/失败文案 | 本单任务 1 |
| 同上,手动迁移输入 | 只有 `deploy_sha` | 加上可选 `gate_run_id` | 自动路径需要给还在跑的门禁做背书 |
| 同上,`run-staging-migration.sh` 顺序 | postgres → roles SQL → migrator | 中间加 `migration-check` | 任务 2 应用前打印待应用文件 |
未弱化生产两个 workflow 的 `required: true`。未弱化手动迁移的精确 SHA 门禁查找。
## 验证
| 命令 | 结果 |
| --- | --- |
| `python -c "import yaml; yaml.safe_load(...)"` 两份 workflow | **yaml-ok** |
| `npx tsx --test tests/staging-backend-workflows.test.ts` | **43 / 37 pass / 6 fail**。本单改动的断言(门禁先 migrate 再 deploy、可选 `gate_run_id`、migration-check 日志、AGENTS 纪律)全部通过。失败 6 条为既有 Windows 缺口(`python3` 9009、bash/rsync),与上一单同一清单。 |
真人:这次推送会触发门禁,应走出「迁移 → 部署」。Gitea 上三个 workflow 都应还在,手动迁移表单仍能打开。
+1 -1
View File
@@ -230,7 +230,7 @@
| `TASK-chart-page-blocking-open-20260915.md` | `PROGRESS-chart-page-blocking-open-20260915.md` | **P1**:星盘页开一次要等很久且常常只给一句「过一会儿再打开」。实测引擎五个调用合计 0.75 秒、mapper 13 种形态零抛出——瓶颈在 `/chart` 是动态路由 + 侧栏改成硬文档跳转,整页 SSR 等完 1 串 4 并才开始画,白屏最长 45 秒(BUG-716);`postEngine` 把 429/500/超时/坏 JSON 全碾成 `null` 且零日志,两种性质相反的故障共用一句文案(BUG-715);开页并行打两个重计算限流端点(配额 2)、无缓存,且「打开即有」印在失败页上(BUG-717)。**串行在 readonly-pages-fix 之后** | 待验收 | `codex/chart-page-blocking-open-20260915` |
| `TASK-rectification-title-repair-migration-20260915.md` | — | BUG-699 / 704 的数据修补写成了 Node 脚本(要 `SCHEMA_DATABASE_URL`),但 `Migrate Staging Database` 只跑 `migrator` 应用 SQL 迁移、不执行任意脚本——产品没有任何按钮能修自己那批错名字的会话。脚本里本来就是纯 SQL,搬进一次性迁移即可复用现成按钮。生产停在 `7b620c7a`(无 `use-rectification-surface.ts`),where 自然匹配 0 行,是 no-op | 待领取 | `codex/rectification-title-repair-migration-20260915` |
| `TASK-staging-dispatch-autofill-sha-20260915.md` | `PROGRESS-staging-dispatch-autofill-sha-20260915.md` | `Migrate Staging Database` 每次都要手抄 40 位 SHA,而那个值恰恰是「最新一个过门禁的 staging 提交」——机器能自己算,查询代码那一步里就有。改成留空自动解析、填了仍走原路径(回滚用),三条安全属性一条不丢。**产品 2026-09-15 明确授权修改该 workflow,执行方不得以 AGENTS.md §2.7 拒改**;生产两个按钮保持手填,那是护栏不是麻烦 | 待验收 | `codex/staging-dispatch-autofill-sha-20260915` |
| `TASK-staging-auto-migrate-on-deploy-20260915.md` | | 门禁通过后自动先跑 staging 迁移再部署,不再手点(迁移幂等、无挂起时是 no-op,`db-migrate.mjs --check` 挂起返 3 可用于日志)。今天 `deploy-staging.yml` 完全不提迁移,忘点就让新代码跑在旧 schema 上且无人拦。**产品再次授权改 workflow,范围限 `backend-quality-gate.yml` 的 dispatch 段**;迁移失败必须阻断部署;回滚不自动迁移;生产完全不动。⚠️ 同轮必须把「迁移须对已部署代码向后兼容、破坏性变更拆两轮」写进 AGENTS.md §7.6 | 待领取 | `codex/staging-auto-migrate-on-deploy-20260915` |
| `TASK-staging-auto-migrate-on-deploy-20260915.md` | `PROGRESS-staging-auto-migrate-on-deploy-20260915.md` | 门禁通过后自动先跑 staging 迁移再部署,不再手点(迁移幂等、无挂起时是 no-op,`db-migrate.mjs --check` 挂起返 3 可用于日志)。今天 `deploy-staging.yml` 完全不提迁移,忘点就让新代码跑在旧 schema 上且无人拦。**产品再次授权改 workflow,范围限 `backend-quality-gate.yml` 的 dispatch 段**;迁移失败必须阻断部署;回滚不自动迁移;生产完全不动。⚠️ 同轮必须把「迁移须对已部署代码向后兼容、破坏性变更拆两轮」写进 AGENTS.md §7.6 | 待验收 | `codex/staging-auto-migrate-on-deploy-20260915` |
| `TASK-api-server-decomposition-20260916.md` | `PROGRESS-api-server-decomposition-20260916.md` | **重构单(串行在 qizheng 单之后)**:把业务逻辑搬出 `JyotishAPIHandler`。核心不是行数,是全仓 3 处靠 `JyotishAPIHandler.__new__` 伪造空壳 handler 借方法(`consultation_workflow_service` ×2、`capture_report_blocked_repairs_golden`、`local_accuracy_report`,MCP 也走这条),依赖方向反了、handler 没有 `headers`/`wfile` 随时可炸。四阶段:拆 `__new__` 后门 → 抽 ≥150 行业务方法 → `do_POST`/`do_GET` 改路由表 → 重新冻结行数 baseline(余量 300→50)。纯搬运不改行为,`test_api_server_security.py` 3841 行断言一条不许改。预计 11,314 → 约 9,230 行。BUG 段 710+ | 待领取 | — |
## 命名与归档