From 15d18f61230564acb20200591aaa00e7ce40adf4 Mon Sep 17 00:00:00 2001 From: Orion Date: Fri, 24 Jul 2026 08:54:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(homebrew):=20=E4=BF=AE=E5=A4=8D=20Homebrew?= =?UTF-8?q?=20=E4=BC=AA=E7=BB=88=E7=AB=AF=E8=BE=93=E5=85=A5=E5=92=8C?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=A4=84=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 colorize_line 函数中处理光标控制序列,避免它们破坏版本着色 - 添加 run_brew_in_pseudoterminal 函数,从控制终端读取输入而不是关闭的管道 - 将主脚本通过 curl 管道启动时的输入重定向到 /dev/tty - 修复启动器中 sudo 密码读取时的输入源问题 - 更新文档说明启动器如何处理 curl 管道输入和确认提示 --- homebrew/README.md | 2 +- homebrew/brew-upgrade-manager-bootstrap.sh | 14 ++++++++-- homebrew/brew-upgrade-manager.sh | 32 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/homebrew/README.md b/homebrew/README.md index ece078d..747c999 100644 --- a/homebrew/README.md +++ b/homebrew/README.md @@ -198,7 +198,7 @@ BREWUP_CONNECT_TIMEOUT_SECONDS=20 BREWUP_LOW_SPEED_TIME_SECONDS=45 brewup 为了兼顾性能,Formula 和 Cask 的批量阶段都只执行一次,只有仍然过期的失败项进入单项重试。单项升级保持串行,因为并行运行多个 Homebrew 写操作会争用 Homebrew 锁和 `/Applications`。用于错误分类的临时日志会在命令结束或收到信号后删除。 -在交互式终端中,主脚本会通过 macOS 自带的 `script(1)` 为 Homebrew 创建伪终端,以便 Homebrew 6 实时重绘并行下载进度。后续的流式着色器会立即透传进度条的回车刷新,并只对完整文本行应用包名、版本和状态配色。需要记录错误以供重试判断时,`script(1)` 也会同步保存日志。如果将 `brewup` 输出重定向到文件或 CI,则自动切换为稳定的逐行输出,不绘制动态进度条。 +在交互式终端中,主脚本会通过 macOS 自带的 script(1) 为 Homebrew 创建伪终端,以便 Homebrew 6 实时重绘并行下载进度。后续的流式着色器会立即透传进度条的回车刷新,并只对完整文本行应用包名、版本和状态配色。启动器还会将主脚本的输入从 curl 管道切换到控制终端,保证 Homebrew 的 y/n 确认提示可正常读取输入,也避免伪终端收到 ^D。需要记录错误以供重试判断时,script(1) 也会同步保存日志。如果将 brewup 输出重定向到文件或 CI,则自动切换为稳定的逐行输出,不绘制动态进度条。 ## Cask 与清理策略 diff --git a/homebrew/brew-upgrade-manager-bootstrap.sh b/homebrew/brew-upgrade-manager-bootstrap.sh index 0fd1826..4f69fb9 100755 --- a/homebrew/brew-upgrade-manager-bootstrap.sh +++ b/homebrew/brew-upgrade-manager-bootstrap.sh @@ -107,7 +107,11 @@ EOF 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" - IFS= read -r -s BREWUP_SUDO_PASSWORD + 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 @@ -125,4 +129,10 @@ EOF setup_sudo_askpass export COLUMNS="${COLUMNS:-130}" -bash "$TEMP" "$@" +if [[ -r /dev/tty ]]; then + # 启动器是通过 curl 管道执行时,不让主脚本继承已关闭的管道输入。 + # 这样 Homebrew 的确认提示和 script(1) 的伪终端都能从当前终端正常读取输入。 + bash "$TEMP" "$@" < /dev/tty +else + bash "$TEMP" "$@" +fi diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index 6521168..de1b45f 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -639,6 +639,14 @@ sub print_heading { sub colorize_line { my ($line) = @_; + # Homebrew 的进度队列会在普通输出前留下光标控制序列(例如显示光标)。 + # 先原样输出这些序列,再匹配文本,避免它们成为包名的一部分而破坏版本着色。 + my $control_prefix = ""; + while ($line =~ s/\A(\e\[[0-?]*[ -\/]*[@-~])//) { + $control_prefix .= $1; + } + print $control_prefix; + return if print_version_transition($line); if ($line =~ /^==> /) { @@ -706,13 +714,33 @@ if (length $buffer) { ' } +run_brew_in_pseudoterminal() { + local log_file="$1" + shift + + # brewup 通常通过 curl 管道启动,因而主脚本的 stdin 可能是已关闭的管道。 + # script(1) 若继承它,会向伪终端传入 ^D,使 Homebrew 的 y/n 提示直接失败。 + # 应从控制终端读取输入,继续支持升级确认与实时进度。 + if [[ -n "$log_file" ]]; then + if [[ -r /dev/tty ]]; then + /usr/bin/script -qF "$log_file" env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" < /dev/tty + else + /usr/bin/script -qF "$log_file" env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" + fi + elif [[ -r /dev/tty ]]; then + /usr/bin/script -q /dev/null env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" < /dev/tty + else + /usr/bin/script -q /dev/null env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" + fi +} + run_brew_colored() { local pipeline_status=() # 伪终端保留 Homebrew 的实时进度,流式着色器只处理完整文本行, # 不会缓冲或改写用 CR 刷新的进度输出。 if can_stream_colorize_tty_output; then - if /usr/bin/script -q /dev/null env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" | colorize_brew_tty_stream; then + if run_brew_in_pseudoterminal "" "$@" | colorize_brew_tty_stream; then return 0 fi pipeline_status=("${PIPESTATUS[@]}") @@ -851,7 +879,7 @@ run_brew_with_retries() { : > "$ACTIVE_LOG_FILE" if can_stream_colorize_tty_output; then # 为错误分类保留转录,同时通过伪终端和流式着色兼顾进度条与版本配色。 - if /usr/bin/script -qF "$ACTIVE_LOG_FILE" env -u HOMEBREW_COLOR HOMEBREW_NO_COLOR=1 brew "$@" | colorize_brew_tty_stream; then + if run_brew_in_pseudoterminal "$ACTIVE_LOG_FILE" "$@" | colorize_brew_tty_stream; then status=0 else pipeline_status=("${PIPESTATUS[@]}")