Files
order_site/deploy/lib/classify-changes.sh
T
yml2213 0b48313617 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 全通过。
2026-10-03 16:29:05 +08:00

58 lines
2.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 把「本次改动涉及哪些文件」映射成「需要重建哪些镜像」。
#
# 用法:
# printf '%s\n' "apps/backend/src/x.ts" | bash deploy/lib/classify-changes.sh
# bash deploy/lib/classify-changes.sh --paths a.txt b.txt
#
# 输出:空格分隔的服务名(backend / web),两端都不用重建时输出空行。
#
# 规则刻意保守——只有"明确属于单端、且进不了另一侧镜像"的改动才允许只重建一端,
# 其余一律两端都重建,宁可多建一次也不允许"改了却没生效"。
# 判断依据是各 Dockerfile 的 COPY 范围:
# backend 镜像 = apps/backend/**(排除 .dockerignore 里的 node_modules/dist/data)
# web 镜像 = apps/frontend/** + deploy/caddy/Caddyfile
classify_changed_paths() {
local backend=0 web=0 path
local -a out=()
# `|| [[ -n "${path}" ]]` 兜住"最后一行没有换行符"的情况(read 在 EOF 会返回非零)
while IFS= read -r path || [[ -n "${path}" ]]; do
# 去掉 git 输出里可能带的空白
path="${path#"${path%%[![:space:]]*}"}"
path="${path%"${path##*[![:space:]]}"}"
[[ -n "${path}" ]] || continue
case "${path}" in
# 只改测试:生产镜像里不会执行任何 *.test.ts(tsconfig.build.json 把它们排除在 dist 之外,
# 且生产不会跑 npm test),不需要为了一个测试文件重建镜像。
apps/backend/*.test.ts | apps/backend/*.test.js) ;;
apps/frontend/*.test.ts | apps/frontend/*.test.tsx | apps/frontend/*.test.js) ;;
# 明确的单端改动
apps/backend/*) backend=1 ;;
apps/frontend/*) web=1 ;;
deploy/docker/*dev*.Dockerfile) ;; # 开发用镜像,与生产镜像无关
deploy/docker/backend*.Dockerfile) backend=1 ;;
deploy/docker/frontend*.Dockerfile) web=1 ;;
deploy/caddy/*) web=1 ;; # Caddyfile 会被 COPY 进 web 镜像
# 进不了任何生产镜像的改动:文档、本地脚本、部署脚手架
docs/* | *.md | LICENSE) ;;
.gitignore | .editorconfig | .prettierignore | .prettierrc.json) ;;
.vscode/* | scripts/* | .githooks/*) ;;
deploy/lib/* | deploy/*.sh) ;;
# 其余(compose 文件、根目录杂项、未知路径)一律两端重建
*) backend=1; web=1 ;;
esac
done
(( backend == 1 )) && out+=("backend")
(( web == 1 )) && out+=("web")
printf '%s\n' "${out[*]:-}"
}
classify_changed_paths