♻️ refactor(core): 优化 Homebrew 升级管理器的错误重试与隔离机制

* 重构 Cask 升级逻辑,引入失败隔离与针对性重试机制
* 新增通过环境变量自定义重试次数与延迟间隔的功能
* 优化启动器下载流程,集成 curl 自动退避重试策略
* 改进 Cask 失败处理,仅对未成功项进行二次重试并移除强制标志
* 更新文档,详细说明网络瞬时错误处理与新版本升级逻辑
* 增强脚本健壮性,完善针对数值参数的合法性校验机制
This commit is contained in:
2026-07-17 00:10:37 +08:00
parent 505dc71cdb
commit 3e7063b30a
3 changed files with 142 additions and 13 deletions
+90 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Homebrew 智能升级脚本(强制 Cask 更新增强版 v5.4 - 容忍 Cask 瞬时下载失败)
# Homebrew 智能升级脚本(Cask 失败隔离与重试版
# ================== 脚本环境设置 ==================
@@ -46,9 +46,68 @@ terminal_width() {
separator() { local width; width=$(terminal_width); printf '=%.0s' $(seq 1 "$width"); printf "\n"; }
print_header() { echo -e "${BLUE}$1${NC}"; }
run_cask_upgrade() {
local status=0 outdated_casks
retry_delay_seconds() {
local delay="${HB_RETRY_DELAY_SECONDS:-5}"
[[ "$delay" =~ ^[0-9]+$ ]] || delay=5
printf '%s' "$delay"
}
retry_attempts() {
local attempts="${HB_RETRY_ATTEMPTS:-3}"
[[ "$attempts" =~ ^[1-9][0-9]*$ ]] || attempts=3
printf '%s' "$attempts"
}
is_retryable_brew_error() {
local log_file="$1"
# 网络、下载和服务端短暂错误适合重试;权限、root、签名和配置错误
# 通常是确定性失败,重试只会浪费时间并重复修改应用。
grep -Eiq \
'curl: \([0-9]+\)|Transferred a partial file|Could not resolve host|timed out|timeout|connection (reset|refused|closed)|network|HTTP (5[0-9]{2}|429)|temporarily unavailable|failed to download|download.*failed' \
"$log_file"
}
run_brew_with_retries() {
local label="$1"
shift
local attempts delay attempt status log_file
attempts="$(retry_attempts)"
delay="$(retry_delay_seconds)"
log_file="$(mktemp "${TMPDIR:-/tmp}/brewup-retry.XXXXXX.log")"
for ((attempt = 1; attempt <= attempts; attempt++)); do
if ((attempt > 1)); then
echo -e "${YELLOW}Retrying ${label} (${attempt}/${attempts}) after ${delay}s...${NC}"
sleep "$delay"
delay=$((delay * 2))
fi
# stdout 直接输出,避免正常升级时把大量进度信息落盘;只复制通常包含
# 错误详情的 stderr,用于失败分类,同时保持实时终端输出。
: > "$log_file"
if brew "$@" 2> >(tee "$log_file" >&2); then
rm -f "$log_file"
return 0
else
status=$?
fi
if ((attempt == attempts)) || ! is_retryable_brew_error "$log_file"; then
rm -f "$log_file"
return "$status"
fi
echo -e "${YELLOW}${label} failed with a retryable error.${NC}"
done
}
run_cask_upgrade() {
local status=0 outdated_casks remaining_casks cask
local failed_casks=()
# 批量阶段只执行一次,让 Homebrew 并发获取下载;失败后只处理仍然过期
# 的 Cask,避免重跑整个批次和重复检查已经升级成功的应用。
brew upgrade --cask --greedy --force || status=$?
if [[ "$status" -eq 0 ]]; then
return 0
@@ -66,8 +125,32 @@ run_cask_upgrade() {
return 0
fi
echo -e "${YELLOW}The following casks still appear to be outdated:${NC}"
echo -e "${YELLOW}The following casks still appear to be outdated; retrying individually:${NC}"
printf '%s\n' "$outdated_casks"
while IFS= read -r cask; do
[[ -n "$cask" ]] || continue
echo -e "${CYAN}Retrying Cask: ${cask} (without --force)${NC}"
if ! run_brew_with_retries "Cask ${cask}" upgrade --cask --greedy "$cask"; then
failed_casks+=("$cask")
fi
done <<< "$outdated_casks"
if ! remaining_casks="$(brew outdated --cask --greedy 2>/dev/null)"; then
echo -e "${YELLOW}Warning: Unable to verify outdated Casks after individual retries.${NC}"
return "$status"
fi
if [[ -z "$remaining_casks" ]]; then
echo -e "${GREEN}All outdated Casks were upgraded after retrying individually.${NC}"
return 0
fi
echo -e "${YELLOW}The following Casks remain outdated after retries:${NC}"
printf '%s\n' "$remaining_casks"
if ((${#failed_casks[@]} > 0)); then
echo -e "${YELLOW}These Casks failed during individual retries: ${failed_casks[*]}${NC}"
fi
return "$status"
}
@@ -75,7 +158,7 @@ run_cask_upgrade() {
separator
print_header "Step 1: Updating Homebrew repositories (brew update -v)"
brew update -v
run_brew_with_retries "Homebrew repository update" update -v
separator
printf "\n"
@@ -95,10 +178,10 @@ echo -e "${NC}"
# 1. 升级命令行工具 (Formulae)
echo -e "\n${CYAN}>>> [1/2] Upgrading CLI Formulae (brew upgrade --formula)...${NC}"
brew upgrade --formula
run_brew_with_retries "Formula upgrade" upgrade --formula
# 2. 升级图形界面软件 (Casks)
echo -e "\n${CYAN}>>> [2/2] Upgrading GUI Casks (brew upgrade --cask --greedy --force)...${NC}"
echo -e "\n${CYAN}>>> [2/2] Upgrading GUI Casks (bulk --force, then isolated retries)...${NC}"
# 强制注入环境变量,确保终端输出依然保留 ANSI 颜色格式
export HOMEBREW_COLOR=1