15d18f6123
- 在 colorize_line 函数中处理光标控制序列,避免它们破坏版本着色 - 添加 run_brew_in_pseudoterminal 函数,从控制终端读取输入而不是关闭的管道 - 将主脚本通过 curl 管道启动时的输入重定向到 /dev/tty - 修复启动器中 sudo 密码读取时的输入源问题 - 更新文档说明启动器如何处理 curl 管道输入和确认提示
139 lines
4.3 KiB
Bash
Executable File
139 lines
4.3 KiB
Bash
Executable File
#!/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
|