From 720eb0c2270f4c3406ad05682c03da1388c783b4 Mon Sep 17 00:00:00 2001 From: zyycn Date: Sun, 26 Jul 2026 23:20:38 +0800 Subject: [PATCH] ci: freeze applied migrations behind a sha256 manifest sqlx re-validates applied-migration checksums on every startup, so any byte change to a released migration file locks existing deployments out of starting with no automatic recovery. Record the released migrations in backend/migrations/.frozen-sha256 and fail CI when a frozen file changes or a new migration lands without being enrolled. Document the freeze rule and the local test-database environment in backend/migrations/README.md. The migration files themselves stay byte-identical: adding even a header comment would trip the same startup validation this guard protects. Co-authored-by: Claude --- .github/workflows/_quality.yml | 15 +++++++++++++++ backend/migrations/.frozen-sha256 | 2 ++ backend/migrations/README.md | 30 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 backend/migrations/.frozen-sha256 create mode 100644 backend/migrations/README.md diff --git a/.github/workflows/_quality.yml b/.github/workflows/_quality.yml index 5b5a48be6..9a328ed1e 100644 --- a/.github/workflows/_quality.yml +++ b/.github/workflows/_quality.yml @@ -65,6 +65,21 @@ jobs: with: ref: ${{ inputs.ref }} + # 已应用迁移的字节被 sqlx 启动校验钉死:改动已发布迁移会让所有 + # 存量部署重启即失败。冻结清单保证旧文件不被改、新文件必须入册。 + - name: Migration freeze check + working-directory: backend/migrations + shell: bash + run: | + set -euo pipefail + sha256sum --check --strict .frozen-sha256 + for file in *.sql; do + if ! awk '{print $2}' .frozen-sha256 | grep -Fxq "$file"; then + echo "::error file=backend/migrations/$file::migration $file is not frozen; append its sha256 to backend/migrations/.frozen-sha256" + exit 1 + fi + done + - name: Setup Rust uses: dtolnay/rust-toolchain@1a3a6d54512beeaffd394f8d516ca16f2c506a20 # 1.97 with: diff --git a/backend/migrations/.frozen-sha256 b/backend/migrations/.frozen-sha256 new file mode 100644 index 000000000..a55f368c4 --- /dev/null +++ b/backend/migrations/.frozen-sha256 @@ -0,0 +1,2 @@ +b3b5e7a9b46460c2f0f4a4248768c68ff5fd09d90f902078b38bf707b9609cae 0001_initial.sql +886c1acad435c61f4b29a45762daf922923aec845b11955f452950c4de351932 0002_retention_delete_indexes.sql diff --git a/backend/migrations/README.md b/backend/migrations/README.md new file mode 100644 index 000000000..3006980f4 --- /dev/null +++ b/backend/migrations/README.md @@ -0,0 +1,30 @@ +# 数据库迁移 + +sqlx 在服务启动时(`serve` 监听之前)按编号顺序执行本目录的迁移,并把每个 +文件的 checksum 记入 `_sqlx_migrations`。此后每次启动都会重新校验:**已应用 +迁移的文件字节与记录不一致时,服务直接拒绝启动**。 + +## 冻结规则 + +- 已合入 main 的迁移文件**永久冻结**,一个字节都不能改——包括注释和空白。 + 任何改动都会让所有已部署实例(含自更新链路上的存量用户)在下次重启时 + 因 checksum 失配而无法启动,且没有自动恢复路径。 +- schema 变更一律新增编号迁移(`0003_...`、`0004_...`),永远不回改旧文件。 +- `.frozen-sha256` 是冻结清单(`sha256sum` 格式)。CI 会校验:清单内文件 + 字节未变,且目录里每个 `*.sql` 都已入册。新增迁移时在同一提交里执行 + `sha256sum 000N_xxx.sql >> .frozen-sha256` 入册。 +- 若确需修正已冻结迁移里的错误,用新迁移做补偿性变更(`alter` / 回填), + 不要动原文件。 + +## 本地测试库 + +`gateway-store` 的 PG/Redis 集成测试需要以下环境变量,未设置时在本地 +静默跳过(CI 缺失则直接失败): + +```bash +export CPR_TEST_DATABASE_URL='postgres://:@127.0.0.1:5432/' +export CPR_TEST_REDIS_URL='redis://:@127.0.0.1:6379' +``` + +测试自建随机 schema / key 前缀做隔离,可安全指向开发库;凭据按本地部署 +实际值填写(`docker inspect` 对应容器可查)。