Files
script/homebrew/brew-upgrade-manager.sh
Orion 3c980ec87a refactor(core): ♻️ 移除 brew 脚本中的 sudo 密码注入并重构代码
移除 Homebrew 升级脚本中硬编码的 sudo 密码处理逻辑,改为依赖用户预先认证(sudo -v),提高安全性并简化 PTY 转发逻辑。同步更新文档说明及 sntp 脚本的类型校验。

- 脚本: 移除 SUDO_PWD 相关变量及自动注入逻辑,升级版本至 v5.3
- 文档: 更新 README 建议使用 sudo -v 刷新凭据,重写交互说明
- 工具: 在 sntp-rename.js 中增加对非字符串名称的防御性校验
2026-05-04 01:50:00 +08:00

196 lines
5.7 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Homebrew 智能升级脚本(强制 Cask 更新增强版 v5.3 - 移除 sudo 密码注入)
# ================== 脚本环境设置 ==================
# set -e当命令返回非零退出状态表示失败脚本会立即退出。
set -e
# set -o pipefail在管道命令中如果任何一个子命令失败整个管道即为失败。
set -o pipefail
# --- 颜色定义 (自动检测终端是否支持) ---
if [ -t 1 ]; then
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
CYAN='\033[1;36m'
NC='\033[0m'
else
GREEN=''
YELLOW=''
BLUE=''
CYAN=''
NC=''
fi
# --- 终端宽度和打印函数 ---
DEFAULT_FALLBACK_WIDTH="130"
TERMINAL_WIDTH_OVERRIDE=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
--width)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
TERMINAL_WIDTH_OVERRIDE="$2"
shift 2
else
echo -e "${YELLOW}Error: '--width' parameter requires a valid numeric value.${NC}"
exit 1
fi
;;
--width=*)
TERMINAL_WIDTH_OVERRIDE="${1#*=}"
if ! [[ "$TERMINAL_WIDTH_OVERRIDE" =~ ^[0-9]+$ ]]; then
echo -e "${YELLOW}Error: '--width' parameter requires a valid numeric value.${NC}"
exit 1
fi
shift
;;
*)
shift
;;
esac
done
# 确定最终的 TERMINAL_WIDTH
if [[ -n "$TERMINAL_WIDTH_OVERRIDE" ]]; then
TERMINAL_WIDTH="$TERMINAL_WIDTH_OVERRIDE"
elif [[ -n "$HB_TERMINAL_WIDTH" && "$HB_TERMINAL_WIDTH" =~ ^[0-9]+$ ]]; then
TERMINAL_WIDTH="$HB_TERMINAL_WIDTH"
elif command -v stty &>/dev/null && stty size &>/dev/null; then
TERMINAL_WIDTH=$(stty size 2>/dev/null | awk '{print $2}')
if [[ -z "$TERMINAL_WIDTH" || "$TERMINAL_WIDTH" -le 0 ]]; then
TERMINAL_WIDTH=$(tput cols 2>/dev/null || echo "$DEFAULT_FALLBACK_WIDTH")
fi
else
TERMINAL_WIDTH=$(tput cols 2>/dev/null || echo "$DEFAULT_FALLBACK_WIDTH")
fi
separator() { printf '=%.0s' $(seq 1 "$TERMINAL_WIDTH"); printf "\n"; }
print_header() { echo -e "${BLUE}$1${NC}"; }
# ================== 流程开始 ==================
separator
print_header "Step 1: Updating Homebrew repositories (brew update -v)"
brew update -v
separator
printf "\n"
separator
print_header "Step 2: Performing health check (brew doctor)"
if ! brew doctor; then
echo -e "${YELLOW}Warning: 'brew doctor' detected issues. Manual review and resolution are recommended.${NC}"
else
echo "Homebrew environment is in good health."
fi
separator
printf "\n"
separator
print_header "Step 3: Verifying brew-cu extension for GUI Apps"
if ! brew tap | grep -q "buo/cask-upgrade"; then
echo -e "${YELLOW}Extension 'brew-cu' not found. Installing now...${NC}"
brew tap buo/cask-upgrade
else
echo -e "${GREEN}Extension 'brew-cu' is already active.${NC}"
fi
separator
printf "\n"
separator
print_header "Step 4: Executing comprehensive upgrades (Formulae & Casks)"
echo -e "${NC}"
# 1. 升级命令行工具 (Formulae)
echo -e "\n${CYAN}>>> [1/2] Upgrading CLI Formulae (brew upgrade --formula)...${NC}"
brew upgrade --formula
# 2. 升级图形界面软件 (Casks)
echo -e "\n${CYAN}>>> [2/2] Upgrading GUI Casks (brew cu -yaq)...${NC}"
# 强制注入环境变量,确保终端输出依然保留 ANSI 颜色格式
export HOMEBREW_COLOR=1
# --- 核心修复:环境变量宽度兜底 ---
# 强制将 Bash 计算好的真实终端宽度传递给底层,防止 Ruby 绘表时发生 negative argument 崩溃
export COLUMNS="$TERMINAL_WIDTH"
# 使用 Python 原生 PTY 自建极简转发引擎。它只转发用户输入,不保存、不注入 sudo 密码。
python3 -c '
import pty, os, sys, select, fcntl, termios, struct, tty
cmd = sys.argv[1:]
pid, fd = pty.fork()
if pid == 0:
os.execvp(cmd[0], cmd)
else:
try:
s = struct.pack("HHHH", 0, 0, 0, 0)
winsize = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s)
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)
except Exception:
pass
stdin_fd = sys.stdin.fileno()
stdout_fd = sys.stdout.fileno()
forward_stdin = sys.stdin.isatty()
old_stdin_attrs = None
if forward_stdin:
old_stdin_attrs = termios.tcgetattr(stdin_fd)
tty.setraw(stdin_fd)
exit_code = 0
try:
while True:
try:
watch = [fd]
if forward_stdin:
watch.append(stdin_fd)
rfds, _, _ = select.select(watch, [], [], 0.1)
if fd in rfds:
data = os.read(fd, 8192)
if not data:
break
os.write(stdout_fd, data)
if forward_stdin and stdin_fd in rfds:
data = os.read(stdin_fd, 8192)
if data:
os.write(fd, data)
except OSError:
break
try:
wpid, wstatus = os.waitpid(pid, os.WNOHANG)
if wpid == pid:
if os.WIFEXITED(wstatus):
exit_code = os.WEXITSTATUS(wstatus)
else:
exit_code = 1
break
except ChildProcessError:
break
finally:
if old_stdin_attrs is not None:
termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_stdin_attrs)
sys.exit(exit_code)
' brew cu -yaq
separator
printf "\n"
separator
print_header "Step 5: Cleaning up old files and caches (brew cleanup --prune=all)"
brew cleanup --prune=all
separator
printf "\n"
echo -e "${GREEN}All operations completed!${NC}"
printf "\n"