feat(deploy): 部署改成按需重建,无 schema 变更自动跳过备份

每次部署都无条件重建 backend + web 两个镜像(实测后端 3m35s、前端 2m54s),
并且每次都在迁移前 pg_dump + 传 OSS(生产 492MB dump 那一步约 12 分钟);
只改了后端也要把前端整条链重跑一遍。

改动:
- 新增 deploy/lib/classify-changes.sh:把改动文件映射成"要重建哪一端",
  规则保守——apps/backend/** → backend、apps/frontend/**|deploy/caddy/**|前端 Dockerfile → web、
  文档/部署脚本/测试文件 → 不重建,compose 文件与未知路径 → 两端都重建。
- ubuntu-deploy.sh 用 .deploy-state(上次成功部署的 commit + .env 指纹)算 git diff,
  未变化的一端不 build、也不 --force-recreate,容器保持运行;若它没在跑则用现有镜像拉起。
  两端都要建时合并为一次 compose build(bake 并行)。
- 备份:改动不含 apps/backend/src/db/migrations/** 时自动跳过(--backup 强制、
  --skip-backup 无条件跳过);算不出改动范围(首次使用、force push)时不自动跳过。
- compose pull 改 --policy missing:不再每次重新校验 postgres tag,避免顺手静默升级数据库镜像。
- 新增 --dry-run(打印计划后退出,含基础镜像 digest)、--all、--backend-only/--web-only;
  部署结束打印各阶段耗时,回答"这次怎么比上次慢"不用靠推算。
- .deploy-state 进 .gitignore;README 与 docs/部署启动与数据库重建.md 同步。

验证:新增 7 个用例直接执行分类脚本断言"改动→重建范围",并守住"状态只在健康检查后才落盘"
等关键顺序;全量 595 个单测、typecheck、lint、check:sql 全通过。
This commit is contained in:
yml2213
2026-10-03 16:29:05 +08:00
parent 30ae35d3a5
commit 0b48313617
6 changed files with 555 additions and 20 deletions
+8 -1
View File
@@ -216,11 +216,18 @@ bash deploy/ubuntu-deploy.sh
首次若无 `.env`,脚本会从示例生成模板并退出;填好后再执行一次即可。脚本会检查 Docker、占位配置、数据目录权限,构建启动并等待健康检查。
**按需重建**:脚本用 `.deploy-state` 记录上次成功部署的 commit 与 `.env` 指纹,再按 `git diff --name-only 上次..HEAD` 判断这次要重建哪一端 —— 只改 `apps/backend/**` 就只重建后端镜像,只改 `apps/frontend/**`(或 `deploy/caddy/**`)就只重建 Web 镜像,只改文档/部署脚本时两端镜像都不重建、容器保持运行;`.env` 变化、compose 文件变化或路径拿不准时两端都重建兜底。不需要重建的一侧既不跑 Docker 构建、也不重建容器。部署结束会打印各阶段耗时。
常用参数:
| 参数 | 说明 |
| --- | --- |
| `--skip-backup` | 跳过部署前的自动数据库备份(默认每次部署都会先 `pg_dump` 再上传 OSS,数据量较大时耗时明显)。仅代码改动、无 schema 变更时可加;涉及数据库迁移或 `--reset-db` 前建议保留备份 |
| (无) | 按 git 改动自动判断重建哪一端;本次改动不含 `apps/backend/src/db/migrations/**` 时自动跳过迁移前备份 |
| `--dry-run` | 只打印计划(改了什么、重建哪一端、要不要备份、基础镜像 digest)后退出,不做任何改动 |
| `--all` | 强制两端镜像都重建(忽略改动检测) |
| `--backend-only` / `--web-only` | 只重建指定的一端 |
| `--backup` | 强制迁移前备份(即使本次无 schema 变更) |
| `--skip-backup` | 无条件跳过部署前的数据库备份 |
| `--reset-db` | 删除 PostgreSQL 数据卷后按 migrations 全量重建(执行前会自动抓取最后一份备份) |
| `--yes` | 跳过 `--reset-db` 的交互确认(等价于 `RESET_DB_CONFIRM=1`) |