Files
order_site/deploy/lib/classify-changes.sh
yml2213 9b6aa8e2fd fix(deploy): 中文路径被 git 转义 → 按需重建误判为"两端全建"
首次真机验证按需重建时踩到:`git diff --name-only` 默认 core.quotePath=true,
docs/部署说明.md 会输出成 "docs/\351\203\250...",匹配不到 docs/* 规则,
一路掉进"拿不准就两端都重建"的兜底分支——只改文档却重建了 backend + web。

修复:
- 新增 classify-changes.sh --git-diff <from> <to> [repo],内部用
  `git -c core.quotePath=false diff --name-only`,部署脚本改走它;
- 分类时顺带剥掉可能残留的首尾引号;
- 补回归用例:临时 git 仓库里提交中文文件名,断言改动列表保留中文原文、
  且最终分类为空(不加 --git-diff 时该用例会失败,已实测复现)。
2026-10-03 16:34:18 +08:00

77 lines
3.0 KiB
Bash
Raw Permalink 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 --git-diff <from> <to> [repo] # 打印改动的文件列表
#
# 输出:空格分隔的服务名(backend / web),两端都不用重建时输出空行。
#
# 规则刻意保守——只有"明确属于单端、且进不了另一侧镜像"的改动才允许只重建一端,
# 其余一律两端都重建,宁可多建一次也不允许"改了却没生效"。
# 判断依据是各 Dockerfile 的 COPY 范围:
# backend 镜像 = apps/backend/**(排除 .dockerignore 里的 node_modules/dist/data)
# web 镜像 = apps/frontend/** + deploy/caddy/Caddyfile
# 取改动文件列表。必须关掉 core.quotePath:
# 否则中文路径会输出成 "docs/\351\203\250..." 这种 C 转义带引号的形式,
# 匹配不到 docs/* 之类的规则、一路掉进"两端都重建"的兜底分支
# (2026-10-03 真实踩过一次:只改文档却重建了 backend + web)。
print_git_diff() {
local from="$1" to="$2" repo="${3:-.}"
git -C "${repo}" -c core.quotePath=false diff --name-only "${from}" "${to}"
}
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:]]}"}"
path="${path#\"}"
path="${path%\"}"
[[ -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[*]:-}"
}
case "${1:-}" in
--git-diff)
shift
print_git_diff "$@"
;;
*)
classify_changed_paths
;;
esac