#!/usr/bin/env bash # 目标:下载远程升级脚本、执行、销毁临时文件 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" ASKPASS_TEMP="$TEMP_DIR/brewup-askpass.sh" KEYCHAIN_SERVICE="${BREWUP_KEYCHAIN_SERVICE:-brewup-sudo-password}" CURRENT_USER="${USER:-$(id -un)}" cleanup() { rm -f "$TEMP" "$ASKPASS_TEMP" rmdir "$TEMP_DIR" 2>/dev/null || true } on_interrupt() { exit 130 } on_terminate() { exit 143 } trap cleanup EXIT trap on_interrupt INT trap on_terminate TERM PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin" export PATH download_retries="${BREWUP_DOWNLOAD_RETRIES:-3}" download_retry_delay="${BREWUP_DOWNLOAD_RETRY_DELAY_SECONDS:-2}" connect_timeout="${BREWUP_CONNECT_TIMEOUT_SECONDS:-15}" speed_time="${BREWUP_LOW_SPEED_TIME_SECONDS:-30}" [[ "$download_retries" =~ ^[1-9][0-9]*$ ]] || download_retries=3 [[ "$download_retry_delay" =~ ^[0-9]+$ ]] || download_retry_delay=2 [[ "$connect_timeout" =~ ^[1-9][0-9]*$ ]] || connect_timeout=15 [[ "$speed_time" =~ ^[1-9][0-9]*$ ]] || speed_time=30 curl_retry_all_errors=() if curl --help all 2>/dev/null | grep -q -- '--retry-all-errors'; then curl_retry_all_errors+=(--retry-all-errors) fi print_info "正在下载远程脚本..." curl -f -sSL \ --connect-timeout "$connect_timeout" \ --speed-limit 1024 \ --speed-time "$speed_time" \ --retry "$download_retries" \ --retry-delay "$download_retry_delay" \ "${curl_retry_all_errors[@]}" \ "$REMOTE" -o "$TEMP" [[ -s "$TEMP" ]] || { print_error "远程脚本为空,已停止执行。"; exit 1; } if [[ -n "${BREWUP_SHA256:-}" ]]; then print_info "正在校验脚本 SHA256..." actual_sha256="$(shasum -a 256 "$TEMP")" actual_sha256="${actual_sha256%% *}" if [[ "$actual_sha256" != "$BREWUP_SHA256" ]]; then 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 bash -n "$TEMP" if [[ "${BREWUP_DEBUG:-0}" == "1" ]]; then print_info "Downloaded script first line:" head -n 1 "$TEMP" fi setup_sudo_askpass() { cat > "$ASKPASS_TEMP" <<'EOF' #!/usr/bin/env bash exec /usr/bin/security find-generic-password \ -a "${BREWUP_KEYCHAIN_ACCOUNT:-${USER:-$(id -un)}}" \ -s "${BREWUP_KEYCHAIN_SERVICE:-brewup-sudo-password}" -w EOF chmod 700 "$ASKPASS_TEMP" export SUDO_ASKPASS="$ASKPASS_TEMP" export BREWUP_KEYCHAIN_SERVICE="$KEYCHAIN_SERVICE" 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 '%b首次使用:请输入一次 sudo 密码,将保存到 macOS Keychain:%b' "$YELLOW" "$NC" if [[ -r /dev/tty ]]; then IFS= read -r -s BREWUP_SUDO_PASSWORD < /dev/tty else IFS= read -r -s BREWUP_SUDO_PASSWORD fi 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 print_info "正在通过 Keychain 准备 sudo 凭据..." if ! sudo -A -v; then 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 } setup_sudo_askpass export COLUMNS="${COLUMNS:-130}" if [[ -r /dev/tty ]]; then # 启动器是通过 curl 管道执行时,不让主脚本继承已关闭的管道输入。 # 这样 Homebrew 的确认提示和 script(1) 的伪终端都能从当前终端正常读取输入。 bash "$TEMP" "$@" < /dev/tty else bash "$TEMP" "$@" fi