fix(ci): reject procedural migration bodies
All checks were successful
ci / verify (pull_request) Successful in 9m24s

This commit is contained in:
chengcheng 2026-07-17 17:04:05 +08:00
parent f226f9c953
commit 86509d5c43
3 changed files with 15 additions and 2 deletions

View File

@ -95,7 +95,7 @@ rootless DinD 不是 VM 沙箱。外层仍按官方要求使用 `--privileged`
## 数据库迁移兼容门禁
`deploy/ci/production-migration-base` 必须等于当前生产 API 对应的完整源码 SHA。`scripts/ci-validate-migrations.mjs` 只允许新增、按文件名严格追加且为普通文件的迁移;生产已知迁移以及已经进入 `main` 的迁移不可修改或删除。DROP、TRUNCATE、DELETE、MERGE、CREATE OR REPLACE、动态 EXECUTE、RENAME、列类型变更、增加/设置 `NOT NULL`、删除默认值和 REVOKE 会直接使 CI 失败。
`deploy/ci/production-migration-base` 必须等于当前生产 API 对应的完整源码 SHA。`scripts/ci-validate-migrations.mjs` 只允许新增、按文件名严格追加且为普通文件的迁移;生产已知迁移以及已经进入 `main` 的迁移不可修改或删除。DROP、TRUNCATE、DELETE、MERGE、CREATE OR REPLACE、动态 EXECUTE、事务控制、DO/函数/过程定义、RENAME、列类型变更、增加/设置 `NOT NULL`、删除默认值和 REVOKE 会直接使 CI 失败。需要过程式迁移时先拆成可审计的声明式 expand/contract SQL不能把过程体藏在普通或 dollar-quoted 字符串中。
该规则允许 INSERT/UPDATE 等数据迁移,因此不能替代备份和恢复验证。每次生产发布成功并完成 `pg_restore` 演练后,创建一个只前移 `deploy/ci/production-migration-base` 的受审 PR不要把基线指向尚未成功部署的提交。

View File

@ -192,6 +192,11 @@ function executableSql(sql, source = 'SQL input') {
}
const destructiveRules = [
{
name: 'procedural SQL body',
pattern:
/^\s*(?:DO\b|CREATE\s+(?:OR\s+REPLACE\s+)?(?:FUNCTION|PROCEDURE)\b)/i,
},
{
name: 'dynamic SQL execution',
pattern: /\bEXECUTE\b/i,

View File

@ -165,7 +165,15 @@ END;
$$;
SQL
commit_all "$repo" plpgsql_block_end
expect_pass "$repo" "$base"
expect_fail "$repo" "$base" 'procedural SQL body'
repo=$(new_repo ordinary_string_procedural_body)
base=$(git -C "$repo" rev-parse HEAD)
cat >"$repo/apps/api/migrations/0002_do_string.sql" <<'SQL'
DO LANGUAGE plpgsql 'BEGIN DROP TABLE accounts; END';
SQL
commit_all "$repo" ordinary_string_procedural_body
expect_fail "$repo" "$base" 'procedural SQL body'
repo=$(new_repo ordinary_string_boundary)
base=$(git -C "$repo" rev-parse HEAD)