fix(deploy): --backend-only/--all 不该连带跳过"改动范围"的计算

真机验证时发现:显式指定 --backend-only 会直接 return,没算 git diff,
于是 DIFF_KNOWN 一直是 false —— "无 migrations 改动就自动跳过备份"的判断随之失效,
一次只想重建后端的部署照样会跑 pg_dump + 传 OSS(生产上约 12 分钟)。

修复:拆成两步
- compute_changed_files:只回答"自上次成功部署以来改了什么"(CHANGED_FILES / DIFF_KNOWN /
  ENV_CHANGED / LAST_DEPLOYED_SHA),在任何分支下都先算;
- detect_changed_services:只决定"重建哪一端",显式参数(--all/--backend-only/--web-only/
  --reset-db)只覆盖重建范围,不覆盖改动范围。
- print_plan 改用 LAST_DEPLOYED_SHA,不再重复读状态文件。

补一条顺序守卫用例:compute_changed_files 必须在 detect_changed_services 之前调用。
This commit is contained in:
yml2213
2026-10-03 16:42:55 +08:00
parent 9b6aa8e2fd
commit ccbcb7cbae
2 changed files with 62 additions and 44 deletions
+55 -44
View File
@@ -32,6 +32,8 @@ BUILD_WEB=false
CHANGED_FILES=""
CHANGE_REASON=""
DIFF_KNOWN=false
ENV_CHANGED=false
LAST_DEPLOYED_SHA=""
DEPLOY_STARTED_AT=${SECONDS}
TIMING_LABELS=()
TIMING_SECONDS=()
@@ -205,64 +207,37 @@ write_deploy_state() {
log "已记录部署状态:${DEPLOY_STATE_FILE}(commit=${head_sha:0:12})"
}
# 决定这次要重建哪一端:--all/--backend-only/--web-only > .deploy-state + git diff > 保守兜底。
detect_changed_services() {
local head_sha last_sha env_hash last_env_hash services
# 只回答"自上次成功部署以来改了什么",不决定重建范围。
# 拆成两步的原因:--backend-only/--all 这类显式参数只该覆盖"重建哪一端",
# 不该把"改动范围未知"一起带进来——否则会连"无迁移改动就跳过备份"的判断也一起退化成"必须备份"。
compute_changed_files() {
local head_sha last_sha env_hash last_env_hash
BUILD_BACKEND=false
BUILD_WEB=false
CHANGED_FILES=""
CHANGE_REASON=""
DIFF_KNOWN=false
if [[ "${FORCE_BACKEND}" == "true" || "${FORCE_WEB}" == "true" ]]; then
[[ "${FORCE_BACKEND}" == "true" ]] && BUILD_BACKEND=true
[[ "${FORCE_WEB}" == "true" ]] && BUILD_WEB=true
CHANGE_REASON="命令行指定了 --backend-only/--web-only"
return
fi
if [[ "${FORCE_ALL}" == "true" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="命令行指定了 --all(强制全量重建)"
return
fi
if [[ "${RESET_DB}" == "true" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="--reset-db 需要整套服务重建"
return
fi
ENV_CHANGED=false
LAST_DEPLOYED_SHA=""
head_sha="$(git -C "${PROJECT_ROOT}" rev-parse HEAD 2>/dev/null || true)"
last_sha="$(read_state_value commit)"
if [[ -z "${head_sha}" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="项目不是 git 仓库或读不到 HEAD,无法判断改动"
return
fi
if [[ -z "${last_sha}" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="没有 ${DEPLOY_STATE_FILE##*/} 记录(首次使用按需重建),先全量重建一次"
return
fi
if ! git -C "${PROJECT_ROOT}" cat-file -e "${last_sha}^{commit}" 2>/dev/null; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="上次部署的提交 ${last_sha:0:12} 已不在仓库中(force push?),全量重建"
return
fi
if ! git -C "${PROJECT_ROOT}" merge-base --is-ancestor "${last_sha}" "${head_sha}" 2>/dev/null; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="上次部署的提交 ${last_sha:0:12} 不是 HEAD 的祖先(回滚/分支切换?),全量重建"
return
fi
@@ -271,13 +246,55 @@ detect_changed_services() {
# 那里关掉了 core.quotePath,中文路径(docs/xxx.md)不会被转义成 "\351..." 而匹配不上规则。
CHANGED_FILES="$(bash "${PROJECT_ROOT}/deploy/lib/classify-changes.sh" --git-diff "${last_sha}" "${head_sha}" 2>/dev/null || true)"
DIFF_KNOWN=true
LAST_DEPLOYED_SHA="${last_sha}"
env_hash="$(file_sha256 "${ENV_FILE}")"
last_env_hash="$(read_state_value env_sha256)"
if [[ -n "${env_hash}" && -n "${last_env_hash}" && "${env_hash}" != "${last_env_hash}" ]]; then
ENV_CHANGED=true
CHANGE_REASON=".env 与上次部署时不一致(运行时环境/构建参数可能变化)"
return
fi
if [[ -z "${CHANGED_FILES}" ]]; then
CHANGE_REASON="自 ${last_sha:0:12} 以来没有代码改动"
else
CHANGE_REASON="自 ${last_sha:0:12} 以来改动 $(printf '%s\n' "${CHANGED_FILES}" | grep -c .) 个文件"
fi
}
# 只决定"重建哪一端":显式参数 > 改动范围 > 保守兜底。
detect_changed_services() {
local services
BUILD_BACKEND=false
BUILD_WEB=false
if [[ "${FORCE_BACKEND}" == "true" || "${FORCE_WEB}" == "true" ]]; then
[[ "${FORCE_BACKEND}" == "true" ]] && BUILD_BACKEND=true
[[ "${FORCE_WEB}" == "true" ]] && BUILD_WEB=true
CHANGE_REASON="${CHANGE_REASON};命令行指定了 --backend-only/--web-only"
return
fi
if [[ "${FORCE_ALL}" == "true" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="${CHANGE_REASON};命令行指定了 --all"
return
fi
if [[ "${RESET_DB}" == "true" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON="${CHANGE_REASON};--reset-db 需要整套服务重建"
return
fi
# 算不出改动范围、或 .env 变了:两端都重建(保守兜底)
if [[ "${DIFF_KNOWN}" != "true" || "${ENV_CHANGED}" == "true" ]]; then
BUILD_BACKEND=true
BUILD_WEB=true
CHANGE_REASON=".env 与上次部署时不一致(运行时环境/构建参数可能变化),两端全量重建"
return
fi
@@ -288,12 +305,6 @@ detect_changed_services() {
if [[ " ${services} " == *" web "* ]]; then
BUILD_WEB=true
fi
if [[ -z "${CHANGED_FILES}" ]]; then
CHANGE_REASON="自 ${last_sha:0:12} 以来没有代码改动"
else
CHANGE_REASON="自 ${last_sha:0:12} 以来改动 $(printf '%s\n' "${CHANGED_FILES}" | grep -c . ) 个文件"
fi
}
migrations_changed() {
@@ -302,12 +313,11 @@ migrations_changed() {
}
print_plan() {
local head_sha last_sha targets="(无)" skipped
local head_sha targets="(无)" skipped
head_sha="$(git -C "${PROJECT_ROOT}" rev-parse HEAD 2>/dev/null || true)"
last_sha="$(read_state_value commit)"
log "部署计划:"
printf ' 上次成功部署:%s\n' "${last_sha:-(无记录)}"
printf ' 上次成功部署:%s\n' "${LAST_DEPLOYED_SHA:-(无记录)}"
printf ' 本次 HEAD :%s\n' "${head_sha:-(非 git 仓库)}"
printf ' 改动依据 :%s\n' "${CHANGE_REASON:-未判断}"
@@ -828,6 +838,7 @@ main() {
ensure_data_dir
ensure_disk_space
compute_changed_files
detect_changed_services
print_plan