♻️ refactor(core): 优化 Homebrew 升级管理器的错误重试与隔离机制

* 重构 Cask 升级逻辑,引入失败隔离与针对性重试机制
* 新增通过环境变量自定义重试次数与延迟间隔的功能
* 优化启动器下载流程,集成 curl 自动退避重试策略
* 改进 Cask 失败处理,仅对未成功项进行二次重试并移除强制标志
* 更新文档,详细说明网络瞬时错误处理与新版本升级逻辑
* 增强脚本健壮性,完善针对数值参数的合法性校验机制
This commit is contained in:
2026-07-17 00:10:37 +08:00
parent 505dc71cdb
commit 3e7063b30a
3 changed files with 142 additions and 13 deletions
+37 -5
View File
@@ -16,8 +16,10 @@
- 执行 `brew update -v` 更新 Homebrew 仓库。
- 执行 `brew doctor` 做健康检查;发现问题时给出警告,但不中断后续流程。
- 使用 `brew upgrade --formula` 升级命令行工具。
- 使用 `brew upgrade --cask --greedy --force` 强制升级 GUI 应用。
- 当 Cask 升级命令因瞬时下载错误返回失败,但复核确认已无过期 Cask 时,继续执行后续清理
- 使用 `brew upgrade --cask --greedy --force` 批量升级 GUI 应用。
- Homebrew 更新、Formula 升级和 Cask 升级遇到网络、下载、HTTP 429/5xx 等瞬时错误时,会自动退避重试
- 批量 Cask 升级只执行一次,以利用 Homebrew 的批量下载能力;失败后只对仍然过期的 Cask 逐个重试,并在重试时去掉 `--force`,避免重复处理已成功的应用。
- 单个 Cask 的失败不会掩盖其他 Cask 的升级结果;最终会再次核对过期列表,仅在仍有失败项时返回非零状态。
- 执行 `brew cleanup --prune=all` 清理旧版本和缓存。
- 支持固定终端宽度,避免非交互环境下输出宽度异常。
- 启动器支持通过 macOS Keychain 保存并读取 sudo 密码,用于 `sudo -A -v` 预刷新 sudo 凭据。
@@ -153,6 +155,24 @@ BREWUP_SHA256=<sha256> brewup
BREWUP_DEBUG=1 brewup
```
## 重试设置
主脚本默认对可识别的瞬时错误最多尝试 3 次,等待时间从 5 秒开始并按倍数增加:
```bash
HB_RETRY_ATTEMPTS=4 HB_RETRY_DELAY_SECONDS=3 brewup
```
启动器下载远程主脚本时默认最多重试 3 次,每次间隔 2 秒:
```bash
BREWUP_DOWNLOAD_RETRIES=5 BREWUP_DOWNLOAD_RETRY_DELAY_SECONDS=3 brewup
```
权限不足、以 root 运行 Homebrew、代码签名失败等确定性错误不会盲目重复重试。批量 Cask 升级遇到这类错误后,脚本仍会尝试对剩余 Cask 逐个执行一次,以隔离失败项并避开 `--force`
为了兼顾性能,正常命令的 stdout 会直接输出,不写入重试日志;脚本只临时记录通常包含错误信息的 stderr。Cask 批量阶段不会整体重试,只有仍然过期的失败项会进入单项重试。单项升级保持串行,因为并行运行多个 Homebrew 写操作会争用 Homebrew 锁和 `/Applications`,通常不会更快,也更容易产生安装冲突。
## 执行流程
主脚本执行顺序:
@@ -160,7 +180,7 @@ BREWUP_DEBUG=1 brewup
1. `brew update -v`
2. `brew doctor`
3. `brew upgrade --formula`
4. `brew upgrade --cask --greedy --force`
4. `brew upgrade --cask --greedy --force`;失败时对剩余 Cask 逐个执行 `brew upgrade --cask --greedy <cask>`
5. `brew cleanup --prune=all`
## 常见问题
@@ -236,13 +256,25 @@ Warning: 'brew doctor' detected issues. Manual review and resolution are recomme
`brew upgrade --cask --greedy --force` 有时会在下载阶段出现 `curl: (18) Transferred a partial file` 之类的瞬时错误,随后 Homebrew 又重试并完成安装。此时命令仍可能返回非零退出码。
脚本会在 Cask 升级命令失败后执行一次
脚本会在批量 Cask 升级命令失败后执行:
```bash
brew outdated --cask --greedy
```
如果没有剩余过期 Cask,脚本会把前一次错误视为瞬时失败并继续执行 `brew cleanup --prune=all`。如果仍有过期 Cask,脚本会保留失败退出状态,方便发现真实失败项。
如果没有剩余过期 Cask,脚本会把前一次错误视为已恢复并继续执行 `brew cleanup --prune=all`。如果仍有过期 Cask,脚本会逐个重试;网络类错误按配置退避重试,其他错误执行一次。最终仍有过期 Cask 时,脚本保留失败退出状态,方便发现真实失败项。
### Cask 报 `Running Homebrew as root`
某些 macOS/Homebrew 组合在覆盖现有 App、复制扩展属性时,会从 Cask 内部通过 `sudo` 调用 `brew ruby`,随后被 Homebrew 自身的 root 安全检查拒绝。这不代表整个 `brewup` 是通过 `sudo brew` 启动的。
新版脚本会在批量升级失败后,对仍然过期的 Cask 去掉 `--force` 单独重试。例如:
```bash
brew upgrade --cask --greedy visual-studio-code
```
如果单独重试仍失败,请到“系统设置 → 隐私与安全性 → App 管理”中允许当前终端管理应用,然后再次运行。不要使用 `sudo brew upgrade`。
## 注意事项