#!/bin/bash # Whitelist management functionality # Shows actual files that would be deleted by dry-run set -euo pipefail # Get script directory and source dependencies SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/common.sh" source "$SCRIPT_DIR/paginated_menu.sh" # Config file path WHITELIST_CONFIG="$HOME/.config/mole/whitelist" # Default whitelist patterns (preselected on first run) declare -a DEFAULT_WHITELIST_PATTERNS=( "$HOME/Library/Caches/ms-playwright*" "$HOME/.cache/huggingface*" ) # Save whitelist patterns to config save_whitelist_patterns() { local -a patterns patterns=("$@") mkdir -p "$(dirname "$WHITELIST_CONFIG")" cat > "$WHITELIST_CONFIG" << 'EOF' # Mole Whitelist - Protected paths won't be deleted # Default protections: Playwright browsers, HuggingFace models (can be disabled) # Add one pattern per line to keep items safe. EOF if [[ ${#patterns[@]} -gt 0 ]]; then local -a unique_patterns=() for pattern in "${patterns[@]}"; do local duplicate="false" if [[ ${#unique_patterns[@]} -gt 0 ]]; then for existing in "${unique_patterns[@]}"; do if patterns_equivalent "$pattern" "$existing"; then duplicate="true" break fi done fi [[ "$duplicate" == "true" ]] && continue unique_patterns+=("$pattern") done if [[ ${#unique_patterns[@]} -gt 0 ]]; then printf '\n' >> "$WHITELIST_CONFIG" for pattern in "${unique_patterns[@]}"; do echo "$pattern" >> "$WHITELIST_CONFIG" done fi fi } # Get all cache items with their patterns get_all_cache_items() { # Format: "display_name|pattern|category" cat << 'EOF' Gradle build cache (Android Studio, Gradle projects)|$HOME/.gradle/caches/*|ide_cache Gradle daemon processes cache|$HOME/.gradle/daemon/*|ide_cache Xcode DerivedData (build outputs, indexes)|$HOME/Library/Developer/Xcode/DerivedData/*|ide_cache Xcode internal cache files|$HOME/Library/Caches/com.apple.dt.Xcode/*|ide_cache Xcode iOS device support symbols|$HOME/Library/Developer/Xcode/iOS DeviceSupport/*/Symbols/System/Library/Caches/*|ide_cache Maven local repository (Java dependencies)|$HOME/.m2/repository/*|ide_cache JetBrains IDEs cache (IntelliJ, PyCharm, WebStorm)|$HOME/Library/Caches/JetBrains/*|ide_cache Android Studio cache and indexes|$HOME/Library/Caches/Google/AndroidStudio*/*|ide_cache VS Code runtime cache|$HOME/Library/Application Support/Code/Cache/*|ide_cache VS Code extension and update cache|$HOME/Library/Application Support/Code/CachedData/*|ide_cache VS Code system cache (Cursor, VSCodium)|$HOME/Library/Caches/com.microsoft.VSCode/*|ide_cache Cursor editor cache|$HOME/Library/Caches/com.todesktop.230313mzl4w4u92/*|ide_cache Bazel build cache|$HOME/.cache/bazel/*|compiler_cache Go build cache and module cache|$HOME/Library/Caches/go-build/*|compiler_cache Rust Cargo registry cache|$HOME/.cargo/registry/cache/*|compiler_cache Rustup toolchain downloads|$HOME/.rustup/downloads/*|compiler_cache ccache compiler cache|$HOME/.ccache/*|compiler_cache sccache distributed compiler cache|$HOME/.cache/sccache/*|compiler_cache CocoaPods cache (iOS dependencies)|$HOME/Library/Caches/CocoaPods/*|package_manager npm package cache|$HOME/.npm/_cacache/*|package_manager pip Python package cache|$HOME/.cache/pip/*|package_manager Homebrew downloaded packages|$HOME/Library/Caches/Homebrew/*|package_manager Yarn package manager cache|$HOME/.cache/yarn/*|package_manager pnpm package store|$HOME/.pnpm-store/*|package_manager Composer PHP dependencies cache|$HOME/.composer/cache/*|package_manager RubyGems cache|$HOME/.gem/cache/*|package_manager Go module cache|$HOME/go/pkg/mod/cache/*|package_manager PyTorch model cache|$HOME/.cache/torch/*|ai_ml_cache TensorFlow model and dataset cache|$HOME/.cache/tensorflow/*|ai_ml_cache HuggingFace models and datasets|$HOME/.cache/huggingface/*|ai_ml_cache Playwright browser binaries|$HOME/Library/Caches/ms-playwright*|ai_ml_cache Selenium WebDriver binaries|$HOME/.cache/selenium/*|ai_ml_cache Ollama local AI models|$HOME/.ollama/models/*|ai_ml_cache Safari web browser cache|$HOME/Library/Caches/com.apple.Safari/*|browser_cache Chrome browser cache|$HOME/Library/Caches/Google/Chrome/*|browser_cache Firefox browser cache|$HOME/Library/Caches/Firefox/*|browser_cache Brave browser cache|$HOME/Library/Caches/BraveSoftware/Brave-Browser/*|browser_cache Docker Desktop image cache|$HOME/Library/Containers/com.docker.docker/Data/*|container_cache Podman container cache|$HOME/.local/share/containers/cache/*|container_cache Font cache|$HOME/Library/Caches/com.apple.FontRegistry/*|system_cache Spotlight metadata cache|$HOME/Library/Caches/com.apple.spotlight/*|system_cache CloudKit cache|$HOME/Library/Caches/CloudKit/*|system_cache EOF } patterns_equivalent() { local first="${1/#~/$HOME}" local second="${2/#~/$HOME}" # Only exact string match, no glob expansion [[ "$first" == "$second" ]] && return 0 return 1 } load_whitelist() { local -a patterns=() if [[ -f "$WHITELIST_CONFIG" ]]; then while IFS= read -r line; do line="${line#${line%%[![:space:]]*}}" line="${line%${line##*[![:space:]]}}" [[ -z "$line" || "$line" =~ ^# ]] && continue patterns+=("$line") done < "$WHITELIST_CONFIG" else patterns=("${DEFAULT_WHITELIST_PATTERNS[@]}") fi if [[ ${#patterns[@]} -gt 0 ]]; then local -a unique_patterns=() for pattern in "${patterns[@]}"; do local duplicate="false" if [[ ${#unique_patterns[@]} -gt 0 ]]; then for existing in "${unique_patterns[@]}"; do if patterns_equivalent "$pattern" "$existing"; then duplicate="true" break fi done fi [[ "$duplicate" == "true" ]] && continue unique_patterns+=("$pattern") done CURRENT_WHITELIST_PATTERNS=("${unique_patterns[@]}") else CURRENT_WHITELIST_PATTERNS=() fi } is_whitelisted() { local pattern="$1" local check_pattern="${pattern/#\~/$HOME}" if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -eq 0 ]]; then return 1 fi for existing in "${CURRENT_WHITELIST_PATTERNS[@]}"; do local existing_expanded="${existing/#\~/$HOME}" if [[ "$check_pattern" == "$existing_expanded" ]]; then return 0 fi if [[ "$check_pattern" == $existing_expanded ]]; then return 0 fi done return 1 } manage_whitelist() { manage_whitelist_categories } manage_whitelist_categories() { clear echo "" echo -e "${PURPLE}Whitelist Manager${NC}" echo "" echo "" # Load currently enabled patterns from both sources load_whitelist # Build cache items list local -a cache_items=() local -a cache_patterns=() local -a menu_options=() local index=0 while IFS='|' read -r display_name pattern category; do # Expand $HOME in pattern pattern="${pattern/\$HOME/$HOME}" cache_items+=("$display_name") cache_patterns+=("$pattern") menu_options+=("$display_name") ((index++)) done < <(get_all_cache_items) # Prioritize already-selected items to appear first local -a selected_cache_items=() local -a selected_cache_patterns=() local -a selected_menu_options=() local -a remaining_cache_items=() local -a remaining_cache_patterns=() local -a remaining_menu_options=() for ((i = 0; i < ${#cache_patterns[@]}; i++)); do if is_whitelisted "${cache_patterns[i]}"; then selected_cache_items+=("${cache_items[i]}") selected_cache_patterns+=("${cache_patterns[i]}") selected_menu_options+=("${menu_options[i]}") else remaining_cache_items+=("${cache_items[i]}") remaining_cache_patterns+=("${cache_patterns[i]}") remaining_menu_options+=("${menu_options[i]}") fi done cache_items=() cache_patterns=() menu_options=() if [[ ${#selected_cache_items[@]} -gt 0 ]]; then cache_items=("${selected_cache_items[@]}") cache_patterns=("${selected_cache_patterns[@]}") menu_options=("${selected_menu_options[@]}") fi if [[ ${#remaining_cache_items[@]} -gt 0 ]]; then cache_items+=("${remaining_cache_items[@]}") cache_patterns+=("${remaining_cache_patterns[@]}") menu_options+=("${remaining_menu_options[@]}") fi if [[ ${#selected_cache_patterns[@]} -gt 0 ]]; then local -a preselected_indices=() for ((i = 0; i < ${#selected_cache_patterns[@]}; i++)); do preselected_indices+=("$i") done local IFS=',' MOLE_PRESELECTED_INDICES="${preselected_indices[*]}" else unset MOLE_PRESELECTED_INDICES fi MOLE_SELECTION_RESULT="" paginated_multi_select "Whitelist Manager – Select caches to protect" "${menu_options[@]}" unset MOLE_PRESELECTED_INDICES local exit_code=$? if [[ $exit_code -ne 0 ]]; then echo "" echo -e "${YELLOW}Cancelled${NC}" return 1 fi # Convert selected indices to patterns local -a selected_patterns=() if [[ -n "$MOLE_SELECTION_RESULT" ]]; then local -a selected_indices IFS=',' read -ra selected_indices <<< "$MOLE_SELECTION_RESULT" for idx in "${selected_indices[@]}"; do if [[ $idx -ge 0 && $idx -lt ${#cache_patterns[@]} ]]; then local pattern="${cache_patterns[$idx]}" # Convert back to portable format with ~ pattern="${pattern/#$HOME/~}" selected_patterns+=("$pattern") fi done fi # Save to whitelist config (bash 3.2 + set -u safe) if [[ ${#selected_patterns[@]} -gt 0 ]]; then save_whitelist_patterns "${selected_patterns[@]}" else save_whitelist_patterns fi print_summary_block "success" \ "Protected ${#selected_patterns[@]} cache(s)" \ "Saved to ${WHITELIST_CONFIG}" printf '\n' } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then manage_whitelist fi