首次真机验证按需重建时踩到:`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 时该用例会失败,已实测复现)。
77 lines
3.0 KiB
Bash
77 lines
3.0 KiB
Bash
#!/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
|