#!/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