|
1 | 1 | #!/bin/bash |
2 | | -# Homebrew Cleanup Module |
3 | | - |
4 | | -set -euo pipefail |
5 | | - |
6 | | -# Clean orphaned cask records (apps manually deleted but cask record remains) |
7 | | -# Uses 2-day cache to avoid expensive brew info calls |
8 | | -# Cache format: cask_name|AppName.app |
9 | | -clean_orphaned_casks() { |
10 | | - command -v brew > /dev/null 2>&1 || return 0 |
11 | | - |
12 | | - if [[ -t 1 ]]; then |
13 | | - MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning orphaned casks..." |
14 | | - fi |
15 | | - |
16 | | - local cache_dir="$HOME/.cache/mole" |
17 | | - local cask_cache="$cache_dir/cask_apps.cache" |
18 | | - local use_cache=false |
19 | | - |
20 | | - # Check if cache is valid (less than 2 days old) |
21 | | - if [[ -f "$cask_cache" ]]; then |
22 | | - local cache_age=$(($(date +%s) - $(get_file_mtime "$cask_cache"))) |
23 | | - if [[ $cache_age -lt 172800 ]]; then |
24 | | - use_cache=true |
25 | | - fi |
26 | | - fi |
27 | | - |
28 | | - local orphaned_casks=() |
29 | | - if [[ "$use_cache" == "true" ]]; then |
30 | | - # Use cached cask → app mapping to avoid expensive brew info calls |
31 | | - while IFS='|' read -r cask app_name; do |
32 | | - [[ ! -e "/Applications/$app_name" ]] && orphaned_casks+=("$cask") |
33 | | - done < "$cask_cache" |
34 | | - else |
35 | | - # Rebuild cache: query all installed casks and extract app names |
36 | | - mkdir -p "$cache_dir" |
37 | | - # Remove stale cache if it exists but has permission issues |
38 | | - rm -f "$cask_cache" 2> /dev/null || true |
39 | | - true > "$cask_cache" |
40 | | - |
41 | | - while IFS= read -r cask; do |
42 | | - # Get app path from cask info with timeout protection (expensive call, hence caching) |
43 | | - local cask_info |
44 | | - cask_info=$(run_with_timeout 10 brew info --cask "$cask" 2> /dev/null || true) |
45 | | - |
46 | | - # SAFETY: Skip if cask contains non-App artifacts (Screen Savers, Plugins, etc.) |
47 | | - # This prevents accidental deletion of casks that don't primarily install to /Applications |
48 | | - if echo "$cask_info" | grep -qE '\((Screen Saver|Preference Pane|Audio Unit|VST|VST3|Component|QuickLook|Spotlight|Artifact)\)'; then |
49 | | - continue |
50 | | - fi |
51 | | - |
52 | | - # Extract app name from "AppName.app (App)" format in cask info output |
53 | | - local app_name |
54 | | - app_name=$(echo "$cask_info" | grep -E '\.app \(App\)' | head -1 | sed -E 's/^[[:space:]]*//' | sed -E 's/ \(App\).*//' || true) |
55 | | - |
56 | | - # Skip if no app artifact (might be a utility package like fonts or just drivers) |
57 | | - [[ -z "$app_name" ]] && continue |
58 | | - |
59 | | - # Save to cache for future runs |
60 | | - echo "$cask|$app_name" >> "$cask_cache" |
61 | | - |
62 | | - # Check if app exists into common locations |
63 | | - # We must check both /Applications and ~/Applications |
64 | | - if [[ ! -e "/Applications/$app_name" ]] && [[ ! -e "$HOME/Applications/$app_name" ]]; then |
65 | | - orphaned_casks+=("$cask") |
66 | | - fi |
67 | | - done < <(brew list --cask 2> /dev/null || true) |
68 | | - fi |
69 | | - |
70 | | - # Remove orphaned casks if found and sudo session is still valid |
71 | | - if [[ ${#orphaned_casks[@]} -gt 0 ]]; then |
72 | | - # Check if sudo session is still valid (without prompting) |
73 | | - if sudo -n true 2> /dev/null; then |
74 | | - if [[ -t 1 ]]; then |
75 | | - stop_inline_spinner |
76 | | - echo -e " ${BLUE}${ICON_ARROW}${NC} Removing orphaned Homebrew casks (may require password for certain apps)" |
77 | | - MOLE_SPINNER_PREFIX=" " start_inline_spinner "Cleaning..." |
78 | | - fi |
79 | | - |
80 | | - local removed_casks=0 |
81 | | - for cask in "${orphaned_casks[@]}"; do |
82 | | - if brew uninstall --cask "$cask" --force > /dev/null 2>&1; then |
83 | | - ((removed_casks++)) |
84 | | - fi |
85 | | - done |
86 | | - |
87 | | - if [[ -t 1 ]]; then stop_inline_spinner; fi |
88 | | - |
89 | | - [[ $removed_casks -gt 0 ]] && echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $removed_casks orphaned cask(s)" |
90 | | - else |
91 | | - # Sudo session expired - inform user to run brew manually |
92 | | - if [[ -t 1 ]]; then stop_inline_spinner; fi |
93 | | - echo -e " ${YELLOW}${ICON_WARNING}${NC} Found ${#orphaned_casks[@]} orphaned casks (sudo expired, run ${GRAY}brew list --cask${NC} to check)" |
94 | | - fi |
95 | | - else |
96 | | - if [[ -t 1 ]]; then stop_inline_spinner; fi |
97 | | - fi |
98 | | -} |
99 | 2 |
|
100 | 3 | # Clean Homebrew caches and remove orphaned dependencies |
101 | 4 | # Skips if run within 2 days, runs cleanup/autoremove in parallel with 120s timeout |
|
0 commit comments