fix(cma): DDL 与启动剥离 —— 关闭启动 create_all + 部署链特权迁移步 (cma-create-all-minpriv-rootfix-20260916)
ci/woodpecker/push/main Pipeline failed

根治 2026-09-16 发布中断(新增模型触发启动 create_all,cma_api 无 CREATE → 崩溃循环):

- backend/app/database.py: 新增 CMA_AUTO_CREATE 开关(**默认关**),生产启动不再执行 DDL;
  运行库账号因此无需 CREATE(最小权限可保持)
- backend/scripts/db_migrate.py: 特权迁移步(--check/--apply/--rollback/--status);
  幂等(checkfirst)+ journal 留痕 + 可回滚(dump 失败/表有数据即拒绝,fail-closed);
  显式拒绝用应用运行账号 cma_api 执行
- deploy.sh: 新增 [4b] 结构迁移(**重启前**执行;失败即中止部署,后端旧码继续服务)
- backend/scripts/tests/db-migrate-sim-e2e.sh: 17 项 scratch 复现/验收(负控制复现 CREATE command denied)
- docs/db-migration-runbook.md: 运行手册(含 cma_api CREATE 回收步骤)

验收:scratch E2E 17/17;pytest 1113 passed/1 xfailed;生产 --check 缺失表 0 / 列级差异 0;
--apply 幂等空跑 created_new=0(journal 留痕)
This commit is contained in:
Hermes CI Fix
2026-09-16 06:53:28 +08:00
parent d54ffaf531
commit 443a779ece
6 changed files with 709 additions and 3 deletions
+26 -3
View File
@@ -56,10 +56,33 @@ def get_db():
db.close()
def auto_create_enabled() -> bool:
"""启动时建表开关(2026-09-16「启动 create_all × 运行库最小权限」根治)。
**默认关闭**:生产 DDL 由部署链的特权迁移步
`backend/scripts/db_migrate.py`(deploy.sh [4b],应用重启**前**执行)承担,
因此运行库账号(cma_api)无需 CREATE 权限。
仅开发/测试可显式 `CMA_AUTO_CREATE=1` 打开。
事故背景:2026-09-16 01:08 发布新增模型(tax_filing_log/treasury_*)→
启动 create_all 被 `cma_api` 无 CREATE 拒绝 → `CREATE command denied` → 崩溃循环。
"""
return os.getenv("CMA_AUTO_CREATE", "0").strip().lower() in ("1", "true", "yes", "on")
def init_db():
import app.models
Base.metadata.create_all(bind=get_engine())
logger.info("CMA数据库已初始化")
import app.models # 仅注册模型(无 DDL);下方 seed 也复用该引用
if auto_create_enabled():
logger.warning(
"CMA_AUTO_CREATE 已开启 → 启动时执行 create_all(仅限开发/测试;生产请走部署链迁移步)"
)
Base.metadata.create_all(bind=get_engine())
logger.info("CMA数据库已初始化(启动建表)")
else:
logger.info(
"启动建表已关闭(CMA_AUTO_CREATE 未开启):DDL 由部署链 scripts/db_migrate.py 在应用重启前执行"
)
# ── 初始化组织层级示例数据 ──
try: