From 722c43e323f65f9d042b2fb42eea7d04743584c8 Mon Sep 17 00:00:00 2001 From: Orion Date: Sat, 18 Jul 2026 13:40:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(homebrew):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=B8=AD=E7=9A=84=E9=A2=9C=E8=89=B2=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E5=92=8C=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了 RED 颜色常量定义和相应的颜色代码调整 - 替换了原有的 echo 命令为新的打印函数(如 print_error、print_warning 等) - 实现了统一的错误消息输出格式,使用 print_error 函数 - 优化了颜色输出的实现方式,使用 printf 替代 echo -e - 在 bootstrap 脚本中也实现了类似的颜色输出和错误处理函数 - 统一了脚本中的消息输出格式和样式 --- homebrew/brew-upgrade-manager-bootstrap.sh | 43 +++++-- homebrew/brew-upgrade-manager.sh | 141 ++++++++++++--------- 2 files changed, 116 insertions(+), 68 deletions(-) diff --git a/homebrew/brew-upgrade-manager-bootstrap.sh b/homebrew/brew-upgrade-manager-bootstrap.sh index 5156036..0fd1826 100755 --- a/homebrew/brew-upgrade-manager-bootstrap.sh +++ b/homebrew/brew-upgrade-manager-bootstrap.sh @@ -2,6 +2,26 @@ # 目标:下载远程升级脚本、执行、销毁临时文件 set -euo pipefail +if [[ -t 1 ]]; then + RED='\033[31m' + YELLOW='\033[33m' + CYAN='\033[36m' + NC='\033[0m' +else + RED='' + YELLOW='' + CYAN='' + NC='' +fi + +print_info() { + printf '%b%s%b\n' "$CYAN" "$*" "$NC" +} + +print_error() { + printf '%bError:%b %s\n' "$RED" "$NC" "$*" >&2 +} + REMOTE="https://git.orionc.me/orion/script/raw/branch/main/homebrew/brew-upgrade-manager.sh" TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/brew-upgrade-manager.XXXXXX")" TEMP="$TEMP_DIR/brew-upgrade-manager.sh" @@ -43,7 +63,7 @@ if curl --help all 2>/dev/null | grep -q -- '--retry-all-errors'; then curl_retry_all_errors+=(--retry-all-errors) fi -echo "正在下载远程脚本..." +print_info "正在下载远程脚本..." curl -f -sSL \ --connect-timeout "$connect_timeout" \ --speed-limit 1024 \ @@ -52,16 +72,16 @@ curl -f -sSL \ --retry-delay "$download_retry_delay" \ "${curl_retry_all_errors[@]}" \ "$REMOTE" -o "$TEMP" -[[ -s "$TEMP" ]] || { echo "远程脚本为空,已停止执行。" >&2; exit 1; } +[[ -s "$TEMP" ]] || { print_error "远程脚本为空,已停止执行。"; exit 1; } if [[ -n "${BREWUP_SHA256:-}" ]]; then - echo "正在校验脚本 SHA256..." + print_info "正在校验脚本 SHA256..." actual_sha256="$(shasum -a 256 "$TEMP")" actual_sha256="${actual_sha256%% *}" if [[ "$actual_sha256" != "$BREWUP_SHA256" ]]; then - echo "脚本 SHA256 不匹配,已停止执行。" >&2 - echo "Expected: $BREWUP_SHA256" >&2 - echo "Actual: $actual_sha256" >&2 + print_error "脚本 SHA256 不匹配,已停止执行。" + printf '%bExpected:%b %s\n' "$YELLOW" "$NC" "$BREWUP_SHA256" >&2 + printf '%bActual:%b %s\n' "$RED" "$NC" "$actual_sha256" >&2 exit 1 fi fi @@ -69,7 +89,7 @@ fi bash -n "$TEMP" if [[ "${BREWUP_DEBUG:-0}" == "1" ]]; then - echo "Downloaded script first line:" + print_info "Downloaded script first line:" head -n 1 "$TEMP" fi @@ -86,17 +106,18 @@ EOF export BREWUP_KEYCHAIN_ACCOUNT="$CURRENT_USER" if ! /usr/bin/security find-generic-password -a "$CURRENT_USER" -s "$KEYCHAIN_SERVICE" -w >/dev/null 2>&1; then - printf "首次使用:请输入一次 sudo 密码,将保存到 macOS Keychain:" + printf '%b首次使用:请输入一次 sudo 密码,将保存到 macOS Keychain:%b' "$YELLOW" "$NC" IFS= read -r -s BREWUP_SUDO_PASSWORD printf '\n' /usr/bin/security add-generic-password -U -a "$CURRENT_USER" -s "$KEYCHAIN_SERVICE" -w "$BREWUP_SUDO_PASSWORD" >/dev/null unset BREWUP_SUDO_PASSWORD fi - echo "正在通过 Keychain 准备 sudo 凭据..." + print_info "正在通过 Keychain 准备 sudo 凭据..." if ! sudo -A -v; then - echo "Keychain 中的 sudo 密码不可用,请删除后重新保存:" >&2 - echo " security delete-generic-password -a \"$CURRENT_USER\" -s \"$KEYCHAIN_SERVICE\"" >&2 + print_error "Keychain 中的 sudo 密码不可用,请删除后重新保存:" + printf ' %bsecurity delete-generic-password -a %q -s %q%b\n' \ + "$CYAN" "$CURRENT_USER" "$KEYCHAIN_SERVICE" "$NC" >&2 exit 1 fi } diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index f2a8c65..51cf905 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -20,13 +20,15 @@ export PATH export HOMEBREW_NO_INSTALL_CLEANUP=1 if [[ -t 1 ]]; then - GREEN='\033[1;32m' - YELLOW='\033[1;33m' - BLUE='\033[1;34m' - CYAN='\033[1;36m' + RED='\033[31m' + GREEN='\033[32m' + YELLOW='\033[33m' + BLUE='\033[34m' + CYAN='\033[36m' NC='\033[0m' export HOMEBREW_COLOR=1 else + RED='' GREEN='' YELLOW='' BLUE='' @@ -57,7 +59,7 @@ parse_args() { while (($# > 0)); do case "$1" in --width) - [[ $# -ge 2 ]] || { echo "Error: --width requires a value." >&2; exit 2; } + [[ $# -ge 2 ]] || { print_error "--width requires a value."; exit 2; } TERMINAL_WIDTH="$2" shift 2 ;; @@ -70,7 +72,7 @@ parse_args() { exit 0 ;; *) - echo "Error: Unknown argument: $1" >&2 + print_error "Unknown argument: $1" usage >&2 exit 2 ;; @@ -88,7 +90,7 @@ resolve_terminal_width() { [[ -n "$TERMINAL_WIDTH" ]] || TERMINAL_WIDTH=130 if [[ ! "$TERMINAL_WIDTH" =~ ^[0-9]+$ ]] || ((TERMINAL_WIDTH < 40 || TERMINAL_WIDTH > 500)); then - echo "Error: terminal width must be an integer between 40 and 500." >&2 + print_error "terminal width must be an integer between 40 and 500." exit 2 fi export COLUMNS="$TERMINAL_WIDTH" @@ -96,14 +98,35 @@ resolve_terminal_width() { separator() { local i + printf '%b' "$BLUE" for ((i = 0; i < TERMINAL_WIDTH; i++)); do printf '=' done - printf '\n' + printf '%b\n' "$NC" } print_header() { - echo -e "${BLUE}$1${NC}" + printf '%b%s%b\n' "$BLUE" "$1" "$NC" +} + +print_error() { + printf '%bError:%b %s\n' "$RED" "$NC" "$*" >&2 +} + +print_warning() { + printf '%bWarning:%b %s\n' "$YELLOW" "$NC" "$*" +} + +print_notice() { + printf '%b%s%b\n' "$YELLOW" "$*" "$NC" +} + +print_success() { + printf '%b%s%b\n' "$GREEN" "$*" "$NC" +} + +print_info() { + printf '%b%s%b\n' "$CYAN" "$*" "$NC" } is_enabled() { @@ -157,17 +180,17 @@ acquire_lock() { IFS= read -r existing_pid < "$LOCK_DIR/pid" || true fi if [[ "$existing_pid" =~ ^[0-9]+$ ]] && kill -0 "$existing_pid" 2>/dev/null; then - echo "Error: another brewup process is already running (PID ${existing_pid})." >&2 + print_error "another brewup process is already running (PID ${existing_pid})." exit 75 fi rm -f "$LOCK_DIR/pid" 2>/dev/null || true rmdir "$LOCK_DIR" 2>/dev/null || { - echo "Error: unable to remove stale lock directory: $LOCK_DIR" >&2 + print_error "unable to remove stale lock directory: $LOCK_DIR" exit 75 } if ! mkdir "$LOCK_DIR" 2>/dev/null; then - echo "Error: another brewup process acquired the lock." >&2 + print_error "another brewup process acquired the lock." exit 75 fi LOCK_ACQUIRED=1 @@ -216,7 +239,7 @@ run_brew_with_retries() { for ((attempt = 1; attempt <= attempts; attempt++)); do if ((attempt > 1)); then - echo -e "${YELLOW}Retrying ${label} (${attempt}/${attempts}) after ${delay}s...${NC}" + print_info "Retrying ${label} (${attempt}/${attempts}) after ${delay}s..." sleep "$delay" next_delay=$((delay * 2)) ((next_delay > 300)) && next_delay=300 @@ -230,7 +253,7 @@ run_brew_with_retries() { pipeline_status=("${PIPESTATUS[@]}") status="${pipeline_status[0]}" if ((${pipeline_status[1]:-0} != 0)); then - echo "Error: failed to write retry log." >&2 + print_error "failed to write retry log." status="${pipeline_status[1]}" fi fi @@ -251,7 +274,7 @@ run_brew_with_retries() { ACTIVE_LOG_FILE="" return "$status" fi - echo -e "${YELLOW}${label} failed with a retryable error.${NC}" + print_notice "${label} failed with a retryable error." done } @@ -272,20 +295,20 @@ run_formula_upgrade() { return 0 fi - echo -e "${YELLOW}Warning: Formula upgrade returned ${bulk_status}; checking remaining Formulae...${NC}" + print_warning "Formula upgrade returned ${bulk_status}; checking remaining Formulae..." if ! outdated_formulae="$(get_outdated_formulae)"; then - echo -e "${YELLOW}Warning: unable to verify outdated Formulae.${NC}" + print_warning "unable to verify outdated Formulae." return "$bulk_status" fi if [[ -z "$outdated_formulae" ]]; then - echo -e "${YELLOW}No outdated Formulae remain; treating the bulk error as recovered.${NC}" + print_notice "No outdated Formulae remain; treating the bulk error as recovered." return 0 fi printf '%s\n' "$outdated_formulae" while IFS= read -r formula; do [[ -n "$formula" ]] || continue - echo -e "${CYAN}Retrying Formula: ${formula}${NC}" + print_info "Retrying Formula: ${formula}" if run_brew_with_retries "Formula ${formula}" upgrade --formula "$formula"; then : else @@ -294,19 +317,19 @@ run_formula_upgrade() { done <<< "$outdated_formulae" if ! remaining_formulae="$(get_outdated_formulae)"; then - echo -e "${YELLOW}Warning: unable to verify Formulae after individual retries.${NC}" + print_warning "unable to verify Formulae after individual retries." FAILED_FORMULAE=("${failed_formulae[@]}") return 1 fi if [[ -z "$remaining_formulae" ]]; then - echo -e "${GREEN}All outdated Formulae were upgraded after isolated retries.${NC}" + print_success "All outdated Formulae were upgraded after isolated retries." return 0 fi while IFS= read -r formula; do [[ -n "$formula" ]] && FAILED_FORMULAE+=("$formula") done <<< "$remaining_formulae" - echo -e "${YELLOW}Formulae still outdated after retries:${NC}" + print_warning "Formulae still outdated after retries:" printf '%s\n' "$remaining_formulae" return 1 } @@ -318,7 +341,7 @@ run_cask_upgrade() { if is_enabled "${HB_CASK_FORCE:-0}"; then bulk_args+=(--force) - echo -e "${YELLOW}HB_CASK_FORCE is enabled; existing Cask files may be overwritten.${NC}" + print_warning "HB_CASK_FORCE is enabled; existing Cask files may be overwritten." fi brew "${bulk_args[@]}" || bulk_status=$? @@ -326,20 +349,20 @@ run_cask_upgrade() { return 0 fi - echo -e "${YELLOW}Warning: Cask upgrade returned ${bulk_status}; checking remaining Casks...${NC}" + print_warning "Cask upgrade returned ${bulk_status}; checking remaining Casks..." if ! outdated_casks="$(get_outdated_casks)"; then - echo -e "${YELLOW}Warning: unable to verify outdated Casks.${NC}" + print_warning "unable to verify outdated Casks." return "$bulk_status" fi if [[ -z "$outdated_casks" ]]; then - echo -e "${YELLOW}No outdated Casks remain; treating the bulk error as recovered.${NC}" + print_notice "No outdated Casks remain; treating the bulk error as recovered." return 0 fi printf '%s\n' "$outdated_casks" while IFS= read -r cask; do [[ -n "$cask" ]] || continue - echo -e "${CYAN}Retrying Cask: ${cask} (without --force)${NC}" + print_info "Retrying Cask: ${cask} (without --force)" if run_brew_with_retries "Cask ${cask}" upgrade --cask --greedy "$cask"; then : else @@ -348,19 +371,19 @@ run_cask_upgrade() { done <<< "$outdated_casks" if ! remaining_casks="$(get_outdated_casks)"; then - echo -e "${YELLOW}Warning: unable to verify Casks after individual retries.${NC}" + print_warning "unable to verify Casks after individual retries." FAILED_CASKS=("${failed_casks[@]}") return 1 fi if [[ -z "$remaining_casks" ]]; then - echo -e "${GREEN}All outdated Casks were upgraded after isolated retries.${NC}" + print_success "All outdated Casks were upgraded after isolated retries." return 0 fi while IFS= read -r cask; do [[ -n "$cask" ]] && FAILED_CASKS+=("$cask") done <<< "$remaining_casks" - echo -e "${YELLOW}Casks still outdated after retries:${NC}" + print_warning "Casks still outdated after retries:" printf '%s\n' "$remaining_casks" return 1 } @@ -388,7 +411,7 @@ prune_cache_before_today() { cache_dir="$(brew --cache)" if [[ ! -d "$cache_dir" || "$cache_dir" == "/" || "$cache_dir" == "$HOME" ]]; then - echo "Error: Homebrew returned an unsafe cache path: $cache_dir" >&2 + print_error "Homebrew returned an unsafe cache path: $cache_dir" return 1 fi @@ -402,11 +425,11 @@ prune_cache_before_today() { if ! find "$cache_dir" -mindepth 1 \( -type f -o -type l \) \ ! -newer "$ACTIVE_CLEANUP_MARKER" -print0 > "$ACTIVE_CLEANUP_MANIFEST"; then - echo "Error: failed to enumerate Homebrew cache files to remove." >&2 + print_error "failed to enumerate Homebrew cache files to remove." return 1 fi - echo "Removed cache files:" + printf '%bRemoved cache files:%b\n' "$YELLOW" "$NC" while IFS= read -r -d '' file; do if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then file_size=0 @@ -415,25 +438,26 @@ prune_cache_before_today() { if rm -f -- "$file"; then removed_count=$((removed_count + 1)) removed_bytes=$((removed_bytes + file_size)) - printf ' %10s %q\n' "$(format_bytes "$file_size")" "$relative_path" + printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$relative_path" else - echo "Warning: failed to remove cache file: $file" >&2 + print_warning "failed to remove cache file: $file" >&2 cleanup_failed=1 fi done < "$ACTIVE_CLEANUP_MANIFEST" if ((removed_count == 0)); then - echo " (none)" + printf ' (none)\n' fi - printf 'Removed %d Homebrew cache file(s) dated before %s (%s total).\n' \ - "$removed_count" "$cutoff_date" "$(format_bytes "$removed_bytes")" + 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" "$NC" "$BLUE" "$cutoff_date" "$NC" \ + "$CYAN" "$(format_bytes "$removed_bytes")" "$NC" if ! find "$cache_dir" -mindepth 1 \( -type f -o -type l \) \ -newer "$ACTIVE_CLEANUP_MARKER" -print0 > "$ACTIVE_CLEANUP_MANIFEST"; then - echo "Error: failed to enumerate today's retained Homebrew cache files." >&2 + print_error "failed to enumerate today's retained Homebrew cache files." return 1 fi - echo "Retained cache files dated today or later:" + printf '%bRetained cache files dated today or later:%b\n' "$GREEN" "$NC" while IFS= read -r -d '' file; do if ! file_size="$(stat -f '%z' -- "$file" 2>/dev/null)"; then file_size=0 @@ -441,13 +465,14 @@ prune_cache_before_today() { relative_path="${file#"$cache_dir"/}" kept_count=$((kept_count + 1)) kept_bytes=$((kept_bytes + file_size)) - printf ' %10s %q\n' "$(format_bytes "$file_size")" "$relative_path" + printf ' %b%10s%b %q\n' "$CYAN" "$(format_bytes "$file_size")" "$NC" "$relative_path" done < "$ACTIVE_CLEANUP_MANIFEST" if ((kept_count == 0)); then - echo " (none)" + printf ' (none)\n' fi - printf "Retained %d Homebrew cache file(s) dated %s or later (%s total).\n" \ - "$kept_count" "$cutoff_date" "$(format_bytes "$kept_bytes")" + 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" "$NC" "$BLUE" "$cutoff_date" "$NC" \ + "$CYAN" "$(format_bytes "$kept_bytes")" "$NC" rm -f "$ACTIVE_CLEANUP_MARKER" ACTIVE_CLEANUP_MARKER="" @@ -460,7 +485,7 @@ run_cleanup() { local cleanup_days="${HB_CLEANUP_DAYS:-}" if is_enabled "${HB_SKIP_CLEANUP:-0}"; then - echo -e "${YELLOW}Cleanup skipped because HB_SKIP_CLEANUP is enabled.${NC}" + print_notice "Cleanup skipped because HB_SKIP_CLEANUP is enabled." return 0 fi if is_enabled "${HB_CLEANUP_ALL:-0}" || [[ "$cleanup_days" == "all" ]]; then @@ -469,7 +494,7 @@ run_cleanup() { fi if [[ -n "$cleanup_days" ]]; then if [[ ! "$cleanup_days" =~ ^[0-9]+$ ]] || ((cleanup_days > 3650)); then - echo "Error: HB_CLEANUP_DAYS must be an integer between 0 and 3650." >&2 + print_error "HB_CLEANUP_DAYS must be an integer between 0 and 3650." return 2 fi brew cleanup --prune="$cleanup_days" @@ -488,14 +513,14 @@ print_summary() { separator if ((overall_status == 0)); then - echo -e "${GREEN}All operations completed successfully.${NC}" + print_success "All operations completed successfully." else - echo -e "${YELLOW}Homebrew upgrade completed with unresolved failures.${NC}" + printf '%bHomebrew upgrade completed with unresolved failures.%b\n' "$RED" "$NC" if ((${#FAILED_FORMULAE[@]} > 0)); then - printf 'Failed Formulae: %s\n' "${FAILED_FORMULAE[*]}" + printf '%bFailed Formulae:%b %s\n' "$RED" "$NC" "${FAILED_FORMULAE[*]}" fi if ((${#FAILED_CASKS[@]} > 0)); then - printf 'Failed Casks: %s\n' "${FAILED_CASKS[*]}" + printf '%bFailed Casks:%b %s\n' "$RED" "$NC" "${FAILED_CASKS[*]}" fi fi separator @@ -504,8 +529,8 @@ print_summary() { parse_args "$@" resolve_terminal_width -command -v brew >/dev/null 2>&1 || { echo "Error: Homebrew is not installed or not in PATH." >&2; exit 127; } -command -v tee >/dev/null 2>&1 || { echo "Error: tee is required." >&2; exit 127; } +command -v brew >/dev/null 2>&1 || { print_error "Homebrew is not installed or not in PATH."; exit 127; } +command -v tee >/dev/null 2>&1 || { print_error "tee is required."; exit 127; } acquire_lock separator @@ -517,11 +542,11 @@ printf '\n' separator print_header "Step 2: Performing health check (brew doctor)" if is_enabled "${HB_SKIP_DOCTOR:-0}"; then - echo -e "${YELLOW}brew doctor skipped because HB_SKIP_DOCTOR is enabled.${NC}" + print_notice "brew doctor skipped because HB_SKIP_DOCTOR is enabled." elif ! brew doctor; then - echo -e "${YELLOW}Warning: 'brew doctor' detected issues. Manual review is recommended.${NC}" + print_warning "'brew doctor' detected issues. Manual review is recommended." else - echo "Homebrew environment is in good health." + print_success "Homebrew environment is in good health." fi separator printf '\n' @@ -530,12 +555,14 @@ separator print_header "Step 3: Upgrading Formulae and Casks with failure isolation" overall_status=0 -echo -e "\n${CYAN}>>> [1/2] Upgrading CLI Formulae...${NC}" +printf '\n' +print_info ">>> [1/2] Upgrading CLI Formulae..." if ! run_formula_upgrade; then overall_status=1 fi -echo -e "\n${CYAN}>>> [2/2] Upgrading GUI Casks...${NC}" +printf '\n' +print_info ">>> [2/2] Upgrading GUI Casks..." if ! run_cask_upgrade; then overall_status=1 fi