6.5 KiB
生产部署验证与迁移门禁:设计说明
1. 边界与目标
本任务是一次受控生产发布与验证合同,不改变合同业务实现。部署 tooling 只在必要时增加生产 target/migration preflight 及静态测试;应用代码、生产 .env、真实业务数据和企业微信不在本任务改动范围。
核心状态序列:
candidate/source
-> production target identity (read-only)
-> existing deployment aggregate preflight (read-only)
-> candidate migration manifest + pre-status (read-only)
-> terminated row count / rollback window snapshot (read-only)
-> approved deploy-existing (schema/app write)
-> post-status applied (read-only)
-> /ready + build + smoke + permission-negative (read-only)
-> closeout / PAUSED classification
任何箭头左侧门禁失败都不得进入右侧动作。尤其 migrate deploy 之前不得 seed、切换应用或执行业务写入。
2. 生产目标识别
2.1 输入
EXPECTED_PRODUCTION_ORIGIN:执行时提供的完整 HTTPS origin。EXPECTED_PRODUCTION_DB_NAME:执行时提供的预期生产数据库名;仅用于比较,不输出。- Existing deployment contract:由
deployment-contract-lib.sh从受控 systemd unit 读取六个 allowlist 参数。 - 本任务 wrapper 不接受未发布提交 override;灾备/远端故障恢复保持为另一条需单独批准的流程。
2.2 只读判据
远端 helper 读取 .env 的键名和值只在内存中比较:
NODE_ENV=production、COOKIE_SECURE=true。APP_ORIGIN与EXPECTED_PRODUCTION_ORIGIN完全一致,scheme 为 HTTPS,host 不是localhost、127.0.0.1、::1,也不是示例/测试/暂存 origin。POSTGRES_DB与EXPECTED_PRODUCTION_DB_NAME一致;数据库 URL 的 database 部分一致,host 不得是回环地址或本地回归库标识。- 生产目标的 compose/path 名称不能单独作为身份依据;
trialrun只描述部署脚本族,不能被误当作生产身份或排除条件。生产/非生产判定以显式 origin + DB identity 为准。
失败只输出稳定 code,例如 PROD_TARGET_ORIGIN_MISMATCH、PROD_TARGET_DB_MISMATCH,不输出实际值。
3. 迁移 preflight 设计
3.1 Candidate manifest
从候选 commit 的 server/prisma/migrations/* 生成排序后的 migration ID 清单。ID 只允许 Prisma migration 目录安全字符。EXPECTED_PENDING_MIGRATIONS 是执行时的显式 allowlist;本轮预期是合同 termination migration,若已提前应用则 pending 为空。
3.2 Production read-only comparison
在当前生产 Compose 数据库容器上只读查询 _prisma_migrations:
- 已应用集合:
finished_at IS NOT NULL AND rolled_back_at IS NULL。 - 异常集合:
rolled_back_at IS NOT NULL、失败日志/未完成行或 applied ID 不在 candidate manifest。 - pending 集合:candidate manifest − applied 集合。
- 输出仅为计数、migration ID 和状态,不输出
DATABASE_URL、密码或原始日志。
同时运行 npx prisma migrate status。原始 stdout/stderr 只保留在受控执行现场;任务 artifacts 写入去除 datasource、数据库 URL、目标 origin/DB 值的脱敏副本,人工核对副本是否与集合比较一致。退出码不能替代原始输出。
3.3 Gate rules
- 异常集合非空 ->
PAUSED。 - pending 集合不是
EXPECTED_PENDING_MIGRATIONS的精确集合 ->PAUSED。 - candidate manifest 无法读取、status 输出无法人工解释或数据库连接目标不确定 ->
PAUSED。 - 只读查询不得调用
migrate deploy、seed、reset、resolve、db push 或手工 SQL DDL。
4. 回滚窗口
预部署只读查询:
SELECT count(*) FROM contract_registry WHERE status = 'terminated';
- count = 0:
rollbackWindow=OPEN;部署失败可在不 drop schema 的前提下回退到兼容应用。 - count > 0:
rollbackWindow=CLOSED;旧应用回退被禁止,只允许 roll-forward/兼容 hotfix。 - 窗口关闭事件是第一条已提交
terminated记录产生的瞬间,而非发布时间、迁移时间或人工计时。 - 本任务不执行终止写入,因此不会主动把窗口从 OPEN 变为 CLOSED。
5. 发布与只读验收
- 只用
deploy-existing.sh,固定SYNC_ENV=0与DEPLOY_DATA_MODE=existing。 - wrapper 调用既有
deploy.sh时设置DEPLOY_STATUS_OUTPUT_MODE=quiet,避免其历史 status 检查把 datasource 原文打到部署终端;详细 pre/post 证据由受控 helper 生成。 - 部署脚本内部
migrate deploy是唯一允许的 schema 写入;其后必须在受控现场保留 post-status 原始输出,并生成脱敏证据副本,确认全部 applied。 /readybuild SHA、容器 build、公开短 SHA和候选 commit 一致。- 基础 smoke、external deps、runtime smoke、公开资源扫描及权限负向路径均只读;登录态/企微/真实 OCR 缺少批准凭据时记录
PAUSED/SKIP。 - 权限负向路径(未登录、不具备合同管理权限、详情不存在/无权)必须排在任何未来真实终止写入之前;当前任务没有该写入。
6. 失败分流与证据
| 阶段 | 失败处理 | 是否可重复部署 |
|---|---|---|
| target/preflight/status | 停止,不同步/不写 DB | 修正输入后重新预检 |
| migrate deploy/init | 保留现场,禁止 reset/resolve/drop;按批准决定兼容回退或 roll-forward | 未确认现场前不可重复 |
/ready/runtime smoke |
记录应用失败;窗口 OPEN 时按既有发布合同回退,否则 roll-forward | 需复核窗口状态 |
| backup refresh | 标记 DEPLOYED_BUT_CONTRACT_REFRESH_FAILED,不重复发布应用 |
只修复合同 |
| public/live/permission negative | 不宣称可测,记录 PAUSED/FAIL | 不自动重跑业务写入 |
Preflight 与 deploy.sh 之间要求远端配置/数据库身份变更冻结;若无法保证该窗口不可变,停止在 preflight 阶段。post-status 只能确认结果,不能补救并发期间写入错误目标的风险。
7. 证据与隐私
- 任务只保存 commit、状态码、计数、migration ID、报告引用和脱敏原因。
- 每次 pre/post 运行开始先清理报告目录中两个已知 evidence 文件,避免重试复用目录时旧 PASS 证据残留;不做目录级递归删除。
- 不保存 SSH target、用户名、绝对远端路径、数据库 URL、凭据、业务合同正文、终止原因或真实响应 payload。
- 生产 target gate 和 pre/post status 的原始输出若包含环境敏感内容,先做脱敏副本;原始日志留在受控执行环境,不进 Git。