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
+37 -2
View File
@@ -178,7 +178,41 @@ DATABASE_URL=postgres://postgres:你的数据库密码@postgres:5432/order_site
bash deploy/ubuntu-deploy.sh
```
脚本会:检查 Docker、检查 `.env` 占位值、检查 `DATABASE_URL`、修正 `apps/backend/data` 权限、构建启动、等待健康检查、打印已应用迁移。
脚本会:检查 Docker、检查 `.env` 占位值、检查 `DATABASE_URL`、修正 `apps/backend/data` 权限、按改动决定重建哪一端、启动、等待健康检查、打印已应用迁移与各阶段耗时。
### 按需重建(默认行为)
脚本把「上次成功部署的 commit + `.env` 指纹」记在项目根的 `.deploy-state`(已 gitignore),
再按 `git diff --name-only 上次commit..HEAD` 判断本次要重建哪一端:
| 改动 | 结果 |
| --- | --- |
| `apps/backend/**`(含 migrations) | 只重建 backend 镜像与容器 |
| `apps/frontend/**`、`deploy/caddy/**`、前端 Dockerfile | 只重建 web 镜像与容器 |
| `README.md`、`docs/**`、`deploy/*.sh`、`deploy/lib/**`、`scripts/**` | 不重建任何镜像,容器保持运行(只剩迁移 + 健康检查) |
| `docker-compose.yml`、`.env` 变化、未知路径、上次 commit 不在仓库里(force push/回滚) | 两端都重建(保守兜底) |
不需要重建的一侧既不跑 Docker 构建、也不 `--force-recreate`;如果它恰好没在运行(服务器重启过),
脚本用现有镜像把它拉起来。首次在有 `--skip-backup` 之前用本脚本时没有 `.deploy-state`,
会按"全量重建一次"处理,之后即进入增量。
备份策略同理:本次改动不含 `apps/backend/src/db/migrations/**` 时自动跳过迁移前备份
(生产上 `pg_dump` + 传 OSS 接近 12 分钟);`--backup` 可强制备份,`--skip-backup` 可无条件跳过,
只有"能算出改动范围"时才会自动跳过。
常用参数:
| 参数 | 说明 |
| --- | --- |
| `--dry-run` | 只打印计划后退出:上次部署的 commit、改动文件、要重建哪一端、是否备份、基础镜像 digest |
| `--all` | 强制两端都重建 |
| `--backend-only` / `--web-only` | 只重建指定一端 |
| `--backup` | 强制迁移前备份 |
| `--skip-backup` | 跳过迁移前备份 |
| `--reset-db` / `--yes` | 见下文「服务器重建数据库」 |
基础镜像也只补缺:`docker compose pull --ignore-buildable --policy missing`
(不再每次部署重新校验 postgres tag,避免顺手把数据库镜像静默升级)。
```bash
docker compose ps
@@ -188,7 +222,8 @@ docker compose logs -f backend
## 服务器日常更新
```bash
bash deploy/ubuntu-deploy.sh
cd /home/order_site && git pull
bash deploy/ubuntu-deploy.sh # 想看这次会做什么:加 --dry-run
```
后端启动时自动执行未应用的增量迁移,一般不必单独跑迁移。