# Release Draft 闸门机制 — 设计文档 **REQ**: REQ-20260416-0017 P0-3 **状态**: 设计阶段 **创建时间**: 2026-04-16 --- ## 1. 背景 ### 当前问题 ai-proj 当前 CI/CD 流程(`.gitea/workflows/build.yaml`): ``` push 到 main → 自动 build 镜像 → 自动部署生产 ``` **这个链路没有"最后一道人工闸门"**。merge develop→main 的 PR 一合并,生产就开始部署。一旦合并,只能事后回滚。 **典型事故场景**:PR 评审时漏了某个改动,merge 后立即推到线上,5 分钟后发现问题,但已经影响用户 5 分钟。 ### 借鉴方案 **devflow-claude `/req:release` 命令的核心做法:** 1. merge PR 不直接部署 2. 创建一个 **draft release**(草稿) 3. **需要人工在 Gitea/GitHub 平台点 "Publish" 按钮** 才触发部署 4. Draft 可以审查 SQL migration、回滚脚本、changelog 等"产物清单" 5. Publish 后才打 tag + 触发 build.yaml --- ## 2. 目标 - 在 merge main 和"真正部署"之间插入 **人工 publish 闸门** - 提供 **release 产物清单预览**(SQL migration / 回滚脚本 / changelog) - 支持 **一键回滚**(从 draft release 追溯回滚脚本) - 与现有 ai-proj MCP 工具链集成 --- ## 3. 架构设计 ### 3.1 流程对比 **当前流程:** ``` [feat/xxx] → PR → merge develop → 测试 [develop] → PR → merge main → ✗ 立即触发 build.yaml → 生产部署 ``` **新流程:** ``` [feat/xxx] → PR → merge develop → 测试 [develop] → PR → merge main → Gitea draft release(待审查) ↓ 人工 publish ↓ tag 推送 + 触发 build.yaml → 生产部署 ``` ### 3.2 组件职责 | 组件 | 新增/改造 | 职责 | |------|---------|------| | **ai-proj backend** | 新增 | `/api/v1/releases` REST API(创建 draft、publish、查询、回滚) | | **mcp-task-bridge** | 新增工具 | `create_release_draft` / `publish_release` / `list_releases` / `get_release` / `rollback_release` | | **Gitea Actions** | 改造 | `build.yaml` 触发条件改为 `release.published` 事件 | | **`release` skill** | 新增 | AI 侧命令封装(`/release new`, `/release publish`, `/release rollback`) | | **release-draft.sh** | 新增脚本 | 本地命令行工具,用于在 CI 外手动创建 draft | ### 3.3 数据模型 **新增 `releases` 表:** ```sql CREATE TABLE releases ( id SERIAL PRIMARY KEY, display_id VARCHAR(64) UNIQUE NOT NULL, -- e.g. "RELEASE-20260416-001" version VARCHAR(64) NOT NULL, -- e.g. "v1.2.0" title VARCHAR(255) NOT NULL, description TEXT, status VARCHAR(32) NOT NULL, -- draft / published / rolled_back git_base_ref VARCHAR(255) NOT NULL, -- e.g. previous tag "v1.1.9" git_head_ref VARCHAR(255) NOT NULL, -- e.g. commit sha after merge git_tag VARCHAR(64), -- 生成的 tag,publish 后才有 changelog_md TEXT NOT NULL, -- 自动生成的 changelog sql_migration_paths TEXT[], -- 合并的 SQL 文件路径列表 sql_rollback_md TEXT, -- 回滚脚本 gitea_release_id BIGINT, -- Gitea 上的 release ID gitea_release_url VARCHAR(512), created_by INT REFERENCES users(id), published_by INT REFERENCES users(id), published_at TIMESTAMP, rolled_back_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` ### 3.4 API 设计 | Endpoint | 作用 | 权限 | |----------|------|------| | `POST /api/v1/releases/drafts` | 创建 draft release(自动推导版本、生成 changelog) | 开发者 | | `POST /api/v1/releases/:id/publish` | 发布(推 tag、触发部署) | 需确认 | | `GET /api/v1/releases` | 列表 | 所有人 | | `GET /api/v1/releases/:id` | 详情 | 所有人 | | `POST /api/v1/releases/:id/rollback` | 回滚到上一版本 | 管理员 | ### 3.5 Gitea 集成 **创建 draft release(调 Gitea API):** ```http POST /api/v1/repos/{owner}/{repo}/releases { "tag_name": "v1.2.0", "target_commitish": "main", "name": "v1.2.0", "body": "......", "draft": true } ``` **build.yaml 改造:** ```yaml # Before on: push: branches: [main] # After on: release: types: [published] workflow_dispatch: # 保留手动触发 ``` **关键变化**:`release.published` 事件只在草稿被 publish 时触发,merge main 不再自动触发。 --- ## 4. 用户流程(典型场景) ### 4.1 开发者发版 ``` 1. 合并 PR 到 main(照旧) 2. 在 Claude Code 里:/release new - AI 读 git log,自动推导版本号 - 汇总 SQL migration - 生成 changelog - 生成回滚脚本 - 展示产物清单,等用户确认 3. 确认后:MCP create_release_draft → 后端 API → Gitea draft 4. 收到 Slack/企微通知:"draft release v1.2.0 已创建,请审查" 5. 打开 Gitea 查看产物清单 6. 点 "Publish release" 按钮 7. CI/CD 自动触发 → 生产部署 ``` ### 4.2 回滚 ``` 1. /release rollback v1.2.0 2. MCP rollback_release → 后端执行回滚 SQL → 重新部署上一版本镜像 3. 记录到 releases 表:status=rolled_back ``` --- ## 5. 渐进式落地路径(P0-3 拆分子任务) 由于 P0-3 完整实现涉及后端 + MCP + CI/CD + skill 四个层面,**不建议单会话完成**。拆分为: | 子任务 | 范围 | 风险 | |-------|------|------| | **P0-3.1** | 后端 release model + migration | 低(只是建表) | | **P0-3.2** | 后端 /api/v1/releases API(draft + publish) | 中 | | **P0-3.3** | MCP 工具 create_release_draft / publish_release | 中 | | **P0-3.4** | `release` skill 创建 | 低 | | **P0-3.5** | Gitea draft release 集成 | 中 | | **P0-3.6** | build.yaml 改为 release.published 触发 | **高**(影响所有人的发版流程) | | **P0-3.7** | 回滚能力 | 中 | **推荐落地节奏:** - 先做 P0-3.1 ~ P0-3.5(构建基础能力) - **保留旧 push-to-main 流程作为 fallback** - 跑 2 周观察 - 再做 P0-3.6(切换 CI/CD 触发源) - 最后 P0-3.7(回滚能力) --- ## 6. 风险与应对 ### R1: build.yaml 触发源切换影响所有开发者 **影响**:目前大家习惯 "merge main 即部署",切换后需等 publish **应对**: - 上线前 1 周发邮件 + 企微通知 - 提供手动触发(workflow_dispatch)作为应急入口 - 文档化新流程 ### R2: 回滚脚本自动生成不够可靠 **影响**:复杂 migration(如数据迁移)的回滚无法自动推导 **应对**: - AI 只生成简单反向 DDL(CREATE→DROP 等) - 复杂情况标记 `⚠️ 需手工补全` - 回滚 SQL 由人工审查后纳入 draft release ### R3: draft release 被忘记 publish **影响**:功能开发完但生产没部署 **应对**: - 企微机器人每天检查超 24h 未 publish 的 draft,发提醒 - 管理员 dashboard 显示 draft 列表 --- ## 7. 验收标准 - [ ] `releases` 表创建,migration 落地 - [ ] `/api/v1/releases/drafts` 创建 draft(至少支持最小字段) - [ ] MCP `create_release_draft` 工具可用 - [ ] 能从 Claude Code 一键创建 Gitea draft release - [ ] build.yaml 支持 `release.published` 触发 - [ ] 至少跑通 1 次完整流程(draft → 人工 publish → 自动部署) - [ ] 回滚脚本自动生成覆盖 >80% 的 DDL 场景 --- ## 8. 参考 - devflow-claude: `plugins/req/commands/release.md` - REQ-20260416-0017(母需求) - 当前 build.yaml: `/Users/donglinlai/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml` - Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease --- ## 9. 下一步行动 - [ ] 本文档提交评审 - [ ] 评审通过后拆 7 个子任务(P0-3.1 ~ P0-3.7)并分别关联到 REQ-20260416-0017 - [ ] 从 P0-3.1(数据模型)开始实施