From 854aceeae58b5d9bb8ca3bf8b1c5800a0fa3dd3e Mon Sep 17 00:00:00 2001 From: Orion Date: Fri, 17 Jul 2026 09:15:23 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(core):=20=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?Homebrew=20=E7=BC=93=E5=AD=98=E6=B8=85=E7=90=86=E7=AD=96?= =?UTF-8?q?=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 禁用安装步骤中的自动清理,统一在升级流程结束后执行 * 清理 Homebrew prefix 失效链接及日期早于今天的缓存 * 保留当天生成的下载缓存,并支持失败后继续清理 * 扩展全量清理参数支持并更新相关文档说明 --- homebrew/README.md | 20 ++++++------ homebrew/brew-upgrade-manager.sh | 54 +++++++++++++++++++++++++++----- substore/alphaair-rename.js | 4 +-- substore/yinyun-rename.js | 2 +- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/homebrew/README.md b/homebrew/README.md index ff2440c..1548d6e 100644 --- a/homebrew/README.md +++ b/homebrew/README.md @@ -20,7 +20,7 @@ - 默认使用 `brew upgrade --cask --greedy`,不强制覆盖已有 App;需要时可显式开启 `--force`。 - 网络、下载、HTTP 429/5xx 等瞬时错误会自动退避重试;权限、root、证书、checksum 和磁盘空间错误不会盲目重试。 - 同一用户只能运行一个主脚本实例,避免并发升级争用 Homebrew 锁和 `/Applications`。 -- 升级全部成功后执行常规 `brew cleanup`;存在失败时默认保留缓存,方便下次恢复。 +- 升级结束后清理 Homebrew prefix 中的失效链接,再按本地自然日清理日期早于今天的缓存,同时保留今天生成的文件。 - 支持 `--width`、环境变量宽度和终端动态宽度。 - 启动器支持通过 macOS Keychain 保存并读取 sudo 密码,用于 `sudo -A -v` 预刷新 sudo 凭据。 @@ -189,18 +189,20 @@ BREWUP_CONNECT_TIMEOUT_SECONDS=20 BREWUP_LOW_SPEED_TIME_SECONDS=45 brewup HB_CASK_FORCE=1 brewup ``` -升级成功后默认执行常规 `brew cleanup`。可指定缓存保留天数,或明确清空全部缓存: +默认在升级流程的最后先执行: ```bash -HB_CLEANUP_DAYS=30 brewup -HB_CLEANUP_ALL=1 brewup -HB_SKIP_CLEANUP=1 brewup +brew cleanup --prune-prefix ``` -存在升级失败时默认跳过 cleanup,以保留下载缓存。仍希望清理时: +该命令只处理 prefix 中的失效链接,不删除下载缓存。随后脚本以本地时区的“昨天 23:59:59”为截止点,只删除 Homebrew 缓存目录中修改日期早于今天的文件。它不是滚动 24 小时:无论今天几点运行,今天 00:00 之后生成的缓存都会保留,昨天及更早的缓存会清理。主脚本同时设置了 `HOMEBREW_NO_INSTALL_CLEANUP=1`,避免 Homebrew 在每个安装步骤后提前清理。 + +如需调整缓存保留天数、彻底清理,或完全跳过清理: ```bash -HB_CLEANUP_ON_FAILURE=1 brewup +HB_CLEANUP_DAYS=30 brewup # 改用 Homebrew 的滚动天数策略 +HB_CLEANUP_ALL=1 brewup +HB_SKIP_CLEANUP=1 brewup ``` 如果 `brew doctor` 明显影响执行时间,也可以跳过: @@ -217,8 +219,8 @@ HB_SKIP_DOCTOR=1 brewup 2. `brew doctor` 3. `brew upgrade --formula`;失败时仅重试仍然过期的 Formula 4. `brew upgrade --cask --greedy`;失败时仅重试仍然过期的 Cask -5. 全部成功时执行 `brew cleanup`,否则默认保留缓存 -6. 汇总失败的 Formula/Cask 并返回最终状态码 +5. `brew cleanup --prune-prefix`,再删除日期早于今天的缓存并保留今天的下载 +6. 汇总失败项并返回最终状态码 ## 常见问题 diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index 00b4d61..3d35997 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -16,6 +16,9 @@ prepend_path_once "/usr/local/sbin" prepend_path_once "/opt/homebrew/sbin" export PATH +# 禁止 Homebrew 在每个安装步骤后自动清理;统一在最后一步按缓存年龄清理。 +export HOMEBREW_NO_INSTALL_CLEANUP=1 + if [[ -t 1 ]]; then GREEN='\033[1;32m' YELLOW='\033[1;33m' @@ -33,6 +36,7 @@ fi TERMINAL_WIDTH="" ACTIVE_LOG_FILE="" +ACTIVE_CLEANUP_MARKER="" LOCK_ACQUIRED=0 LOCK_DIR="${HB_LOCK_DIR:-${TMPDIR:-/tmp}/brewup-$(id -u).lock}" FAILED_FORMULAE=() @@ -113,6 +117,9 @@ cleanup_runtime() { if [[ -n "${ACTIVE_LOG_FILE:-}" && -f "$ACTIVE_LOG_FILE" ]]; then rm -f "$ACTIVE_LOG_FILE" fi + if [[ -n "${ACTIVE_CLEANUP_MARKER:-}" && -f "$ACTIVE_CLEANUP_MARKER" ]]; then + rm -f "$ACTIVE_CLEANUP_MARKER" + fi if ((LOCK_ACQUIRED)); then rm -f "$LOCK_DIR/pid" rmdir "$LOCK_DIR" 2>/dev/null || true @@ -354,6 +361,36 @@ run_cask_upgrade() { return 1 } +prune_cache_before_today() { + local cache_dir cutoff_stamp cutoff_date removed_count + + cache_dir="$(brew --cache)" + if [[ ! -d "$cache_dir" || "$cache_dir" == "/" || "$cache_dir" == "$HOME" ]]; then + echo "Error: Homebrew returned an unsafe cache path: $cache_dir" >&2 + return 1 + fi + + # macOS date -v 使用本地时区。以昨天 23:59:59 为边界,确保今天零点 + # 之后生成的缓存全部保留,而不是使用滚动 24 小时窗口。 + cutoff_stamp="$(date -v-1d '+%Y%m%d2359.59')" + cutoff_date="$(date '+%Y-%m-%d')" + ACTIVE_CLEANUP_MARKER="$(mktemp "${TMPDIR:-/tmp}/brewup-cleanup-cutoff.XXXXXX")" + touch -t "$cutoff_stamp" "$ACTIVE_CLEANUP_MARKER" + + if ! removed_count="$( + find "$cache_dir" -mindepth 1 \( -type f -o -type l \) \ + ! -newer "$ACTIVE_CLEANUP_MARKER" -print -delete \ + | awk 'END { print NR + 0 }' + )"; then + echo "Error: failed to prune Homebrew cache by calendar date." >&2 + return 1 + fi + + rm -f "$ACTIVE_CLEANUP_MARKER" + ACTIVE_CLEANUP_MARKER="" + echo "Removed ${removed_count} Homebrew cache file(s) dated before ${cutoff_date}; today's cache was kept." +} + run_cleanup() { local cleanup_days="${HB_CLEANUP_DAYS:-}" @@ -361,7 +398,7 @@ run_cleanup() { echo -e "${YELLOW}Cleanup skipped because HB_SKIP_CLEANUP is enabled.${NC}" return 0 fi - if is_enabled "${HB_CLEANUP_ALL:-0}"; then + if is_enabled "${HB_CLEANUP_ALL:-0}" || [[ "$cleanup_days" == "all" ]]; then brew cleanup --prune=all return fi @@ -373,7 +410,12 @@ run_cleanup() { brew cleanup --prune="$cleanup_days" return fi - brew cleanup + + # 普通 brew cleanup 可能按版本状态删除今天生成的下载,无法严格保留 + # 今日缓存。这里只让 Homebrew 清理 prefix 中的失效链接,再按自然日 + # 精确清理缓存文件。 + brew cleanup --prune-prefix + prune_cache_before_today } print_summary() { @@ -436,14 +478,12 @@ separator printf '\n' separator -print_header "Step 4: Cleaning up old files and caches" -if ((overall_status != 0)) && ! is_enabled "${HB_CLEANUP_ON_FAILURE:-0}"; then - echo -e "${YELLOW}Cleanup skipped because upgrades failed; cached downloads are preserved for the next retry.${NC}" -elif ! run_cleanup; then +print_header "Step 4: Cleaning cache dated before today (keeping today's downloads)" +if ! run_cleanup; then overall_status=1 fi separator printf '\n' print_summary "$overall_status" -exit "$overall_status" +exit "$overall_status" \ No newline at end of file diff --git a/substore/alphaair-rename.js b/substore/alphaair-rename.js index 21c6f36..98ba3c4 100644 --- a/substore/alphaair-rename.js +++ b/substore/alphaair-rename.js @@ -11,7 +11,7 @@ const COMMON_SCRIPT_URL = `https://git.orionc.me/orion/script/raw/branch/main/su const hasCurrentRenameUtils = utils => Boolean( utils && utils.version === COMMON_SCRIPT_VERSION ); - +const async function loadRenameUtils() { if (typeof globalThis !== 'undefined' && hasCurrentRenameUtils(globalThis.SubStoreRenameUtils)) { return globalThis.SubStoreRenameUtils; @@ -164,4 +164,4 @@ async function operator(proxies) { ].filter(Boolean).join(' ').trim(); return item.proxy; }); -} +} \ No newline at end of file diff --git a/substore/yinyun-rename.js b/substore/yinyun-rename.js index aa291fe..6566b1e 100644 --- a/substore/yinyun-rename.js +++ b/substore/yinyun-rename.js @@ -119,4 +119,4 @@ async function operator(proxies) { return proxy; }); -} +} \ No newline at end of file