feat(homebrew): 添加彩色列表打印和改进升级管理功能

- 新增 print_colored_list 函数用于彩色化列表输出
- 在 brew 命令中禁用 HOMEBREW_COLOR 环境变量
- 添加 run_brew_doctor 函数处理 brew doctor 输出并着色
- 添加初始公式和应用更新前的状态检查与显示
- 改进缓存清理功能,区分下载文件和内部文件
- 更新清理结果显示,仅显示下载文件并统计隐藏文件数量
- 使用 run_brew_doctor 替代原始 brew doctor 调用
- 优化升级流程中的状态提示信息显示
This commit is contained in:
2026-07-18 13:53:32 +08:00
parent 722c43e323
commit 1f221fd2fc
+137 -33
View File
@@ -129,6 +129,16 @@ print_info() {
printf '%b%s%b\n' "$CYAN" "$*" "$NC" printf '%b%s%b\n' "$CYAN" "$*" "$NC"
} }
print_colored_list() {
local label="$1" color="$2" items="$3" item
printf '%b%s%b\n' "$color" "$label" "$NC"
while IFS= read -r item; do
[[ -n "$item" ]] || continue
printf ' %b%s%b\n' "$color" "$item" "$NC"
done <<< "$items"
}
is_enabled() { is_enabled() {
case "${1:-}" in case "${1:-}" in
1|true|TRUE|yes|YES|on|ON) return 0 ;; 1|true|TRUE|yes|YES|on|ON) return 0 ;;
@@ -279,19 +289,58 @@ run_brew_with_retries() {
} }
get_outdated_formulae() { get_outdated_formulae() {
brew outdated --formula 2>/dev/null env -u HOMEBREW_COLOR brew outdated --formula 2>/dev/null
} }
get_outdated_casks() { get_outdated_casks() {
brew outdated --cask --greedy 2>/dev/null env -u HOMEBREW_COLOR brew outdated --cask --greedy 2>/dev/null
}
run_brew_doctor() {
local output status=0 line
if output="$(env -u HOMEBREW_COLOR brew doctor 2>&1)"; then
status=0
else
status=$?
fi
while IFS= read -r line; do
case "$line" in
Warning:*|*deprecated*|*disabled*|*pre-release*|*"find replacements"*|*"do not provide support"*|*"Tier 2 configuration"*)
printf '%b%s%b\n' "$YELLOW" "$line" "$NC"
;;
' '*|' https://'*)
printf '%b%s%b\n' "$CYAN" "$line" "$NC"
;;
*)
printf '%s\n' "$line"
;;
esac
done <<< "$output"
return "$status"
} }
run_formula_upgrade() { run_formula_upgrade() {
local bulk_status=0 outdated_formulae remaining_formulae formula local bulk_status=0 initial_formulae="" initial_formulae_known=1
local outdated_formulae remaining_formulae formula
local failed_formulae=() local failed_formulae=()
if ! initial_formulae="$(get_outdated_formulae)"; then
initial_formulae_known=0
print_warning "unable to list Formulae requiring updates before upgrade."
elif [[ -n "$initial_formulae" ]]; then
print_colored_list "Formulae requiring updates:" "$YELLOW" "$initial_formulae"
else
print_success "No Formulae require updates."
fi
brew upgrade --formula || bulk_status=$? brew upgrade --formula || bulk_status=$?
if ((bulk_status == 0)); then if ((bulk_status == 0)); then
if ((initial_formulae_known)) && [[ -n "$initial_formulae" ]]; then
print_colored_list "Updated Formulae:" "$GREEN" "$initial_formulae"
else
print_success "Formula upgrade completed."
fi
return 0 return 0
fi fi
@@ -301,11 +350,14 @@ run_formula_upgrade() {
return "$bulk_status" return "$bulk_status"
fi fi
if [[ -z "$outdated_formulae" ]]; then if [[ -z "$outdated_formulae" ]]; then
print_notice "No outdated Formulae remain; treating the bulk error as recovered." if [[ -n "$initial_formulae" ]]; then
print_colored_list "Updated Formulae:" "$GREEN" "$initial_formulae"
else
print_success "No outdated Formulae remain; the bulk error was recovered."
fi
return 0 return 0
fi fi
printf '%s\n' "$outdated_formulae"
while IFS= read -r formula; do while IFS= read -r formula; do
[[ -n "$formula" ]] || continue [[ -n "$formula" ]] || continue
print_info "Retrying Formula: ${formula}" print_info "Retrying Formula: ${formula}"
@@ -322,20 +374,24 @@ run_formula_upgrade() {
return 1 return 1
fi fi
if [[ -z "$remaining_formulae" ]]; then if [[ -z "$remaining_formulae" ]]; then
print_success "All outdated Formulae were upgraded after isolated retries." if [[ -n "$initial_formulae" ]]; then
print_colored_list "Updated Formulae after isolated retries:" "$GREEN" "$initial_formulae"
else
print_success "All outdated Formulae were upgraded after isolated retries."
fi
return 0 return 0
fi fi
while IFS= read -r formula; do while IFS= read -r formula; do
[[ -n "$formula" ]] && FAILED_FORMULAE+=("$formula") [[ -n "$formula" ]] && FAILED_FORMULAE+=("$formula")
done <<< "$remaining_formulae" done <<< "$remaining_formulae"
print_warning "Formulae still outdated after retries:" print_colored_list "Outdated Formulae remaining after retries:" "$RED" "$remaining_formulae"
printf '%s\n' "$remaining_formulae"
return 1 return 1
} }
run_cask_upgrade() { run_cask_upgrade() {
local bulk_status=0 outdated_casks remaining_casks cask local bulk_status=0 initial_casks="" initial_casks_known=1
local outdated_casks remaining_casks cask
local bulk_args=(upgrade --cask --greedy) local bulk_args=(upgrade --cask --greedy)
local failed_casks=() local failed_casks=()
@@ -344,8 +400,22 @@ run_cask_upgrade() {
print_warning "HB_CASK_FORCE is enabled; existing Cask files may be overwritten." print_warning "HB_CASK_FORCE is enabled; existing Cask files may be overwritten."
fi fi
if ! initial_casks="$(get_outdated_casks)"; then
initial_casks_known=0
print_warning "unable to list Casks requiring updates before upgrade."
elif [[ -n "$initial_casks" ]]; then
print_colored_list "Casks requiring updates:" "$YELLOW" "$initial_casks"
else
print_success "No Casks require updates."
fi
brew "${bulk_args[@]}" || bulk_status=$? brew "${bulk_args[@]}" || bulk_status=$?
if ((bulk_status == 0)); then if ((bulk_status == 0)); then
if ((initial_casks_known)) && [[ -n "$initial_casks" ]]; then
print_colored_list "Updated Casks:" "$GREEN" "$initial_casks"
else
print_success "Cask upgrade completed."
fi
return 0 return 0
fi fi
@@ -355,11 +425,14 @@ run_cask_upgrade() {
return "$bulk_status" return "$bulk_status"
fi fi
if [[ -z "$outdated_casks" ]]; then if [[ -z "$outdated_casks" ]]; then
print_notice "No outdated Casks remain; treating the bulk error as recovered." if [[ -n "$initial_casks" ]]; then
print_colored_list "Updated Casks:" "$GREEN" "$initial_casks"
else
print_success "No outdated Casks remain; the bulk error was recovered."
fi
return 0 return 0
fi fi
printf '%s\n' "$outdated_casks"
while IFS= read -r cask; do while IFS= read -r cask; do
[[ -n "$cask" ]] || continue [[ -n "$cask" ]] || continue
print_info "Retrying Cask: ${cask} (without --force)" print_info "Retrying Cask: ${cask} (without --force)"
@@ -376,15 +449,18 @@ run_cask_upgrade() {
return 1 return 1
fi fi
if [[ -z "$remaining_casks" ]]; then if [[ -z "$remaining_casks" ]]; then
print_success "All outdated Casks were upgraded after isolated retries." if [[ -n "$initial_casks" ]]; then
print_colored_list "Updated Casks after isolated retries:" "$GREEN" "$initial_casks"
else
print_success "All outdated Casks were upgraded after isolated retries."
fi
return 0 return 0
fi fi
while IFS= read -r cask; do while IFS= read -r cask; do
[[ -n "$cask" ]] && FAILED_CASKS+=("$cask") [[ -n "$cask" ]] && FAILED_CASKS+=("$cask")
done <<< "$remaining_casks" done <<< "$remaining_casks"
print_warning "Casks still outdated after retries:" print_colored_list "Outdated Casks remaining after retries:" "$RED" "$remaining_casks"
printf '%s\n' "$remaining_casks"
return 1 return 1
} }
@@ -405,9 +481,23 @@ format_bytes() {
printf '%d.%d %s' "$((tenths / 10))" "$((tenths % 10))" "${units[unit]}" printf '%d.%d %s' "$((tenths / 10))" "$((tenths % 10))" "${units[unit]}"
} }
is_cache_download_artifact() {
[[ "$1" == downloads/* ]]
}
cache_artifact_display_name() {
local name="${1#downloads/}"
# Homebrew 下载文件通常以 SHA-256-- 开头;终端只显示有意义的原文件名。
[[ "$name" == *--* ]] && name="${name#*--}"
printf '%s' "$name"
}
prune_cache_before_today() { prune_cache_before_today() {
local cache_dir cutoff_stamp cutoff_date file file_size relative_path local cache_dir cutoff_stamp cutoff_date file file_size relative_path display_name hidden_count
local removed_count=0 removed_bytes=0 kept_count=0 kept_bytes=0 cleanup_failed=0 local removed_count=0 removed_bytes=0 kept_count=0 kept_bytes=0 cleanup_failed=0
local removed_artifact_count=0 removed_artifact_bytes=0
local kept_artifact_count=0 kept_artifact_bytes=0
cache_dir="$(brew --cache)" cache_dir="$(brew --cache)"
if [[ ! -d "$cache_dir" || "$cache_dir" == "/" || "$cache_dir" == "$HOME" ]]; then if [[ ! -d "$cache_dir" || "$cache_dir" == "/" || "$cache_dir" == "$HOME" ]]; then
@@ -429,7 +519,7 @@ prune_cache_before_today() {
return 1 return 1
fi fi
printf '%bRemoved cache files:%b\n' "$YELLOW" "$NC" printf '%bRemoved download artifacts:%b\n' "$YELLOW" "$NC"
while IFS= read -r -d '' file; do while IFS= read -r -d '' file; do
if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then
file_size=0 file_size=0
@@ -438,18 +528,25 @@ prune_cache_before_today() {
if rm -f -- "$file"; then if rm -f -- "$file"; then
removed_count=$((removed_count + 1)) removed_count=$((removed_count + 1))
removed_bytes=$((removed_bytes + file_size)) removed_bytes=$((removed_bytes + file_size))
printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$relative_path" if is_cache_download_artifact "$relative_path"; then
removed_artifact_count=$((removed_artifact_count + 1))
removed_artifact_bytes=$((removed_artifact_bytes + file_size))
display_name="$(cache_artifact_display_name "$relative_path")"
printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$display_name"
fi
else else
print_warning "failed to remove cache file: $file" >&2 print_warning "failed to remove cache file: $file" >&2
cleanup_failed=1 cleanup_failed=1
fi fi
done < "$ACTIVE_CLEANUP_MANIFEST" done < "$ACTIVE_CLEANUP_MANIFEST"
if ((removed_count == 0)); then ((removed_artifact_count > 0)) || printf ' (none)\n'
printf ' (none)\n' hidden_count=$((removed_count - removed_artifact_count))
fi printf '%bRemoved:%b %b%d files / %s%b before %b%s%b.\n' \
printf '%bRemoved%b %b%d%b Homebrew cache file(s) dated before %b%s%b (%b%s total%b).\n' \ "$YELLOW" "$NC" "$CYAN" "$removed_count" "$(format_bytes "$removed_bytes")" "$NC" \
"$YELLOW" "$NC" "$CYAN" "$removed_count" "$NC" "$BLUE" "$cutoff_date" "$NC" \ "$BLUE" "$cutoff_date" "$NC"
"$CYAN" "$(format_bytes "$removed_bytes")" "$NC" printf ' Downloads shown: %b%d / %s%b; internal files hidden: %b%d%b.\n' \
"$CYAN" "$removed_artifact_count" "$(format_bytes "$removed_artifact_bytes")" "$NC" \
"$CYAN" "$hidden_count" "$NC"
if ! find "$cache_dir" -mindepth 1 \( -type f -o -type l \) \ if ! find "$cache_dir" -mindepth 1 \( -type f -o -type l \) \
-newer "$ACTIVE_CLEANUP_MARKER" -print0 > "$ACTIVE_CLEANUP_MANIFEST"; then -newer "$ACTIVE_CLEANUP_MARKER" -print0 > "$ACTIVE_CLEANUP_MANIFEST"; then
@@ -457,7 +554,7 @@ prune_cache_before_today() {
return 1 return 1
fi fi
printf '%bRetained cache files dated today or later:%b\n' "$GREEN" "$NC" printf '%bRetained download artifacts from today:%b\n' "$GREEN" "$NC"
while IFS= read -r -d '' file; do while IFS= read -r -d '' file; do
if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then
file_size=0 file_size=0
@@ -465,14 +562,21 @@ prune_cache_before_today() {
relative_path="${file#"$cache_dir"/}" relative_path="${file#"$cache_dir"/}"
kept_count=$((kept_count + 1)) kept_count=$((kept_count + 1))
kept_bytes=$((kept_bytes + file_size)) kept_bytes=$((kept_bytes + file_size))
printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$relative_path" if is_cache_download_artifact "$relative_path"; then
kept_artifact_count=$((kept_artifact_count + 1))
kept_artifact_bytes=$((kept_artifact_bytes + file_size))
display_name="$(cache_artifact_display_name "$relative_path")"
printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$display_name"
fi
done < "$ACTIVE_CLEANUP_MANIFEST" done < "$ACTIVE_CLEANUP_MANIFEST"
if ((kept_count == 0)); then ((kept_artifact_count > 0)) || printf ' (none)\n'
printf ' (none)\n' hidden_count=$((kept_count - kept_artifact_count))
fi printf '%bRetained:%b %b%d files / %s%b dated %b%s%b or later.\n' \
printf '%bRetained%b %b%d%b Homebrew cache file(s) dated %b%s%b or later (%b%s total%b).\n' \ "$GREEN" "$NC" "$CYAN" "$kept_count" "$(format_bytes "$kept_bytes")" "$NC" \
"$GREEN" "$NC" "$CYAN" "$kept_count" "$NC" "$BLUE" "$cutoff_date" "$NC" \ "$BLUE" "$cutoff_date" "$NC"
"$CYAN" "$(format_bytes "$kept_bytes")" "$NC" printf ' Downloads shown: %b%d / %s%b; internal files hidden: %b%d%b.\n' \
"$CYAN" "$kept_artifact_count" "$(format_bytes "$kept_artifact_bytes")" "$NC" \
"$CYAN" "$hidden_count" "$NC"
rm -f "$ACTIVE_CLEANUP_MARKER" rm -f "$ACTIVE_CLEANUP_MARKER"
ACTIVE_CLEANUP_MARKER="" ACTIVE_CLEANUP_MARKER=""
@@ -543,7 +647,7 @@ separator
print_header "Step 2: Performing health check (brew doctor)" print_header "Step 2: Performing health check (brew doctor)"
if is_enabled "${HB_SKIP_DOCTOR:-0}"; then if is_enabled "${HB_SKIP_DOCTOR:-0}"; then
print_notice "brew doctor skipped because HB_SKIP_DOCTOR is enabled." print_notice "brew doctor skipped because HB_SKIP_DOCTOR is enabled."
elif ! brew doctor; then elif ! run_brew_doctor; then
print_warning "'brew doctor' detected issues. Manual review is recommended." print_warning "'brew doctor' detected issues. Manual review is recommended."
else else
print_success "Homebrew environment is in good health." print_success "Homebrew environment is in good health."
@@ -570,7 +674,7 @@ separator
printf '\n' printf '\n'
separator separator
print_header "Step 4: Cleaning cache dated before today (keeping today's downloads)" print_header "Step 4: Cleaning old cache (showing download artifacts only)"
if ! run_cleanup; then if ! run_cleanup; then
overall_status=1 overall_status=1
fi fi