#!/bin/bash # Clean Homebrew caches and remove orphaned dependencies # Skips if run within 7 days, runs cleanup/autoremove in parallel with 120s timeout # Env: MO_BREW_TIMEOUT, DRY_RUN clean_homebrew() { command -v brew > /dev/null 2>&1 || return 0 # Dry run mode - just indicate what would happen if [[ "${DRY_RUN:-false}" == "true" ]]; then echo -e " ${YELLOW}→${NC} Homebrew (would cleanup and autoremove)" return 0 fi # Smart caching: check if brew cleanup was run recently (within 7 days) # Extended from 2 days to 7 days to reduce cleanup frequency local brew_cache_file="${HOME}/.cache/mole/brew_last_cleanup" local cache_valid_days=7 local should_skip=false if [[ -f "$brew_cache_file" ]]; then local last_cleanup last_cleanup=$(cat "$brew_cache_file" 2> /dev/null || echo "0") local current_time current_time=$(date +%s) local time_diff=$((current_time - last_cleanup)) local days_diff=$((time_diff / 86400)) if [[ $days_diff -lt $cache_valid_days ]]; then should_skip=true echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew (cleaned ${days_diff}d ago, skipped)" fi fi [[ "$should_skip" == "true" ]] && return 0 # Quick pre-check: determine if cleanup is needed based on cache size (<50MB) # Use timeout to prevent slow du on very large caches # If timeout occurs, assume cache is large and run cleanup local skip_cleanup=false local brew_cache_size=0 if [[ -d ~/Library/Caches/Homebrew ]]; then brew_cache_size=$(run_with_timeout 3 du -sk ~/Library/Caches/Homebrew 2> /dev/null | awk '{print $1}') local du_exit=$? # Skip cleanup (but still run autoremove) if cache is small if [[ $du_exit -eq 0 && -n "$brew_cache_size" && "$brew_cache_size" -lt 51200 ]]; then skip_cleanup=true fi fi # Display appropriate spinner message if [[ -t 1 ]]; then if [[ "$skip_cleanup" == "true" ]]; then MOLE_SPINNER_PREFIX=" " start_inline_spinner "Homebrew autoremove (cleanup skipped)..." else MOLE_SPINNER_PREFIX=" " start_inline_spinner "Homebrew cleanup and autoremove..." fi fi local timeout_seconds=${MO_BREW_TIMEOUT:-120} # Run brew cleanup and/or autoremove based on cache size local brew_tmp_file autoremove_tmp_file local brew_pid autoremove_pid if [[ "$skip_cleanup" == "false" ]]; then brew_tmp_file=$(create_temp_file) (brew cleanup > "$brew_tmp_file" 2>&1) & brew_pid=$! fi autoremove_tmp_file=$(create_temp_file) (brew autoremove > "$autoremove_tmp_file" 2>&1) & autoremove_pid=$! local elapsed=0 local brew_done=false local autoremove_done=false # Mark cleanup as done if it was skipped [[ "$skip_cleanup" == "true" ]] && brew_done=true # Wait for both to complete or timeout while [[ "$brew_done" == "false" ]] || [[ "$autoremove_done" == "false" ]]; do if [[ $elapsed -ge $timeout_seconds ]]; then [[ -n "$brew_pid" ]] && kill -TERM $brew_pid 2> /dev/null || true kill -TERM $autoremove_pid 2> /dev/null || true break fi [[ -n "$brew_pid" ]] && { kill -0 $brew_pid 2> /dev/null || brew_done=true; } kill -0 $autoremove_pid 2> /dev/null || autoremove_done=true sleep 1 ((elapsed++)) done # Wait for processes to finish local brew_success=false if [[ "$skip_cleanup" == "false" && -n "$brew_pid" ]]; then if wait $brew_pid 2> /dev/null; then brew_success=true fi fi local autoremove_success=false if wait $autoremove_pid 2> /dev/null; then autoremove_success=true fi if [[ -t 1 ]]; then stop_inline_spinner; fi # Process cleanup output and extract metrics if [[ "$skip_cleanup" == "true" ]]; then # Cleanup was skipped due to small cache size local size_mb=$((brew_cache_size / 1024)) echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup (cache ${size_mb}MB, skipped)" elif [[ "$brew_success" == "true" && -f "$brew_tmp_file" ]]; then local brew_output brew_output=$(cat "$brew_tmp_file" 2> /dev/null || echo "") local removed_count freed_space removed_count=$(printf '%s\n' "$brew_output" | grep -c "Removing:" 2> /dev/null || true) freed_space=$(printf '%s\n' "$brew_output" | grep -o "[0-9.]*[KMGT]B freed" 2> /dev/null | tail -1 || true) if [[ $removed_count -gt 0 ]] || [[ -n "$freed_space" ]]; then if [[ -n "$freed_space" ]]; then echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup ${GREEN}($freed_space)${NC}" else echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup (${removed_count} items)" fi fi elif [[ $elapsed -ge $timeout_seconds ]]; then echo -e " ${YELLOW}${ICON_WARNING}${NC} Homebrew cleanup timed out (run ${GRAY}brew cleanup${NC} manually)" fi # Process autoremove output - only show if packages were removed if [[ "$autoremove_success" == "true" && -f "$autoremove_tmp_file" ]]; then local autoremove_output autoremove_output=$(cat "$autoremove_tmp_file" 2> /dev/null || echo "") local removed_packages removed_packages=$(printf '%s\n' "$autoremove_output" | grep -c "^Uninstalling" 2> /dev/null || true) if [[ $removed_packages -gt 0 ]]; then echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed orphaned dependencies (${removed_packages} packages)" fi elif [[ $elapsed -ge $timeout_seconds ]]; then echo -e " ${YELLOW}${ICON_WARNING}${NC} Autoremove timed out (run ${GRAY}brew autoremove${NC} manually)" fi # Update cache timestamp on successful completion or when cleanup was intelligently skipped # This prevents repeated cache size checks within the 7-day window if [[ "$skip_cleanup" == "true" ]] || [[ "$brew_success" == "true" ]] || [[ "$autoremove_success" == "true" ]]; then ensure_user_file "$brew_cache_file" date +%s > "$brew_cache_file" fi }