diff --git a/lib/clean/system.sh b/lib/clean/system.sh index 02802b8..06c3bae 100644 --- a/lib/clean/system.sh +++ b/lib/clean/system.sh @@ -11,7 +11,6 @@ clean_deep_system() { safe_sudo_find_delete "/Library/Caches" "*.tmp" "$MOLE_TEMP_FILE_AGE_DAYS" "f" && cache_cleaned=1 || true safe_sudo_find_delete "/Library/Caches" "*.log" "$MOLE_LOG_AGE_DAYS" "f" && cache_cleaned=1 || true [[ $cache_cleaned -eq 1 ]] && log_success "System caches" - # Clean temporary files (macOS /tmp is a symlink to /private/tmp) local tmp_cleaned=0 safe_sudo_find_delete "/private/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" && tmp_cleaned=1 || true safe_sudo_find_delete "/private/var/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" && tmp_cleaned=1 || true @@ -19,22 +18,17 @@ clean_deep_system() { # Clean crash reports safe_sudo_find_delete "/Library/Logs/DiagnosticReports" "*" "$MOLE_CRASH_REPORT_AGE_DAYS" "f" || true log_success "System crash reports" - # Clean system logs (macOS /var is a symlink to /private/var) safe_sudo_find_delete "/private/var/log" "*.log" "$MOLE_LOG_AGE_DAYS" "f" || true safe_sudo_find_delete "/private/var/log" "*.gz" "$MOLE_LOG_AGE_DAYS" "f" || true log_success "System logs" - # Clean Library Updates safely (skip if SIP is enabled) if [[ -d "/Library/Updates" && ! -L "/Library/Updates" ]]; then if ! is_sip_enabled; then - # SIP is disabled, attempt cleanup with restricted flag check local updates_cleaned=0 while IFS= read -r -d '' item; do - # Validate path format (must be direct child of /Library/Updates) if [[ -z "$item" ]] || [[ ! "$item" =~ ^/Library/Updates/[^/]+$ ]]; then debug_log "Skipping malformed path: $item" continue fi - # Skip system-protected files (restricted flag) local item_flags item_flags=$($STAT_BSD -f%Sf "$item" 2> /dev/null || echo "") if [[ "$item_flags" == *"restricted"* ]]; then @@ -47,7 +41,6 @@ clean_deep_system() { [[ $updates_cleaned -gt 0 ]] && log_success "System library updates" fi fi - # Clean macOS Install Data (legacy upgrade leftovers) if [[ -d "/macOS Install Data" ]]; then local mtime=$(get_file_mtime "/macOS Install Data") local age_days=$((($(date +%s) - mtime) / 86400)) @@ -65,19 +58,16 @@ clean_deep_system() { debug_log "Keeping macOS Install Data (only ${age_days} days old, needs 30+)" fi fi - # Clean browser code signature caches start_section_spinner "Scanning system caches..." local code_sign_cleaned=0 local found_count=0 local last_update_time=$(date +%s) - local update_interval=2 # Update spinner every 2 seconds instead of every 50 files - # Efficient stream processing for large directories + local update_interval=2 while IFS= read -r -d '' cache_dir; do if safe_remove "$cache_dir" true; then ((code_sign_cleaned++)) fi ((found_count++)) - # Update progress spinner periodically based on time, not count local current_time=$(date +%s) if [[ $((current_time - last_update_time)) -ge $update_interval ]]; then start_section_spinner "Scanning system caches... ($found_count found)" @@ -86,20 +76,14 @@ clean_deep_system() { done < <(run_with_timeout 5 command find /private/var/folders -type d -name "*.code_sign_clone" -path "*/X/*" -print0 2> /dev/null || true) stop_section_spinner [[ $code_sign_cleaned -gt 0 ]] && log_success "Browser code signature caches ($code_sign_cleaned items)" - # Clean system diagnostics logs safe_sudo_find_delete "/private/var/db/diagnostics/Special" "*" "$MOLE_LOG_AGE_DAYS" "f" || true safe_sudo_find_delete "/private/var/db/diagnostics/Persist" "*" "$MOLE_LOG_AGE_DAYS" "f" || true safe_sudo_find_delete "/private/var/db/DiagnosticPipeline" "*" "$MOLE_LOG_AGE_DAYS" "f" || true log_success "System diagnostic logs" - # Clean power logs safe_sudo_find_delete "/private/var/db/powerlog" "*" "$MOLE_LOG_AGE_DAYS" "f" || true log_success "Power logs" - # Clean memory exception reports (can accumulate to 1-2GB, thousands of files) - # These track app memory limit violations, safe to clean old ones safe_sudo_find_delete "/private/var/db/reportmemoryexception/MemoryLimitViolations" "*" "30" "f" || true log_success "Memory exception reports" - # Clean system diagnostic tracev3 logs (can be 1-2GB) - # System generates these continuously, safe to clean old ones start_section_spinner "Cleaning diagnostic trace logs..." local diag_logs_cleaned=0 safe_sudo_find_delete "/private/var/db/diagnostics/Persist" "*.tracev3" "30" "f" && diag_logs_cleaned=1 || true diff --git a/lib/clean/user.sh b/lib/clean/user.sh index 7a68bfb..f4b810f 100644 --- a/lib/clean/user.sh +++ b/lib/clean/user.sh @@ -1,13 +1,11 @@ #!/bin/bash # User Data Cleanup Module set -euo pipefail -# Clean user essentials (caches, logs, trash) clean_user_essentials() { start_section_spinner "Scanning caches..." safe_clean ~/Library/Caches/* "User app cache" stop_section_spinner safe_clean ~/Library/Logs/* "User app logs" - # Check if Trash directory is whitelisted if is_path_whitelisted "$HOME/.Trash"; then note_activity echo -e " ${GREEN}${ICON_EMPTY}${NC} Trash ยท whitelist protected" @@ -15,19 +13,13 @@ clean_user_essentials() { safe_clean ~/.Trash/* "Trash" fi } -# Helper: Scan external volumes for cleanup (Trash & DS_Store) scan_external_volumes() { [[ -d "/Volumes" ]] || return 0 - # Fast pre-check: collect non-system external volumes and detect network volumes local -a candidate_volumes=() local -a network_volumes=() for volume in /Volumes/*; do - # Basic checks (directory, writable, not a symlink) [[ -d "$volume" && -w "$volume" && ! -L "$volume" ]] || continue - # Skip system root if it appears in /Volumes [[ "$volume" == "/" || "$volume" == "/Volumes/Macintosh HD" ]] && continue - # Use diskutil to intelligently detect network volumes (SMB/NFS/AFP) - # Timeout protection: 1s per volume to avoid slow network responses local protocol="" protocol=$(run_with_timeout 1 command diskutil info "$volume" 2> /dev/null | grep -i "Protocol:" | awk '{print $2}' || echo "") case "$protocol" in @@ -36,7 +28,6 @@ scan_external_volumes() { continue ;; esac - # Fallback: Check filesystem type via df if diskutil didn't identify protocol local fs_type="" fs_type=$(run_with_timeout 1 command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}' || echo "") case "$fs_type" in @@ -47,33 +38,24 @@ scan_external_volumes() { esac candidate_volumes+=("$volume") done - # If no external volumes found, return immediately (zero overhead) local volume_count=${#candidate_volumes[@]} local network_count=${#network_volumes[@]} if [[ $volume_count -eq 0 ]]; then - # Show info if network volumes were skipped if [[ $network_count -gt 0 ]]; then echo -e " ${GRAY}${ICON_LIST}${NC} External volumes (${network_count} network volume(s) skipped)" note_activity fi return 0 fi - # We have local external volumes, now perform full scan start_section_spinner "Scanning $volume_count external volume(s)..." for volume in "${candidate_volumes[@]}"; do - # Re-verify volume is still accessible (may have been unmounted since initial scan) - # Use simple directory check instead of slow mount command for better performance [[ -d "$volume" && -r "$volume" ]] || continue - # 1. Clean Trash on volume local volume_trash="$volume/.Trashes" - # Check if external volume Trash is whitelisted if [[ -d "$volume_trash" && "$DRY_RUN" != "true" ]] && ! is_path_whitelisted "$volume_trash"; then - # Safely iterate and remove each item while IFS= read -r -d '' item; do safe_remove "$item" true || true done < <(command find "$volume_trash" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true) fi - # 2. Clean .DS_Store if [[ "$PROTECT_FINDER_METADATA" != "true" ]]; then clean_ds_store_tree "$volume" "$(basename "$volume") volume (.DS_Store)" fi @@ -104,22 +86,18 @@ clean_macos_system_caches() { safe_clean ~/Library/Caches/com.apple.QuickLook.thumbnailcache "QuickLook thumbnails" || true safe_clean ~/Library/Caches/Quick\ Look/* "QuickLook cache" || true safe_clean ~/Library/Caches/com.apple.iconservices* "Icon services cache" || true - # Clean incomplete downloads safe_clean ~/Downloads/*.download "Safari incomplete downloads" || true safe_clean ~/Downloads/*.crdownload "Chrome incomplete downloads" || true safe_clean ~/Downloads/*.part "Partial incomplete downloads" || true - # Additional user-level caches safe_clean ~/Library/Autosave\ Information/* "Autosave information" || true safe_clean ~/Library/IdentityCaches/* "Identity caches" || true safe_clean ~/Library/Suggestions/* "Siri suggestions cache" || true safe_clean ~/Library/Calendars/Calendar\ Cache "Calendar cache" || true safe_clean ~/Library/Application\ Support/AddressBook/Sources/*/Photos.cache "Address Book photo cache" || true } -# Clean recent items lists clean_recent_items() { stop_section_spinner local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist" - # Target only the global recent item lists to avoid touching per-app/System Settings SFL files local -a recent_lists=( "$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl2" "$shared_dir/com.apple.LSSharedFileList.RecentDocuments.sfl2" @@ -135,10 +113,8 @@ clean_recent_items() { [[ -e "$sfl_file" ]] && safe_clean "$sfl_file" "Recent items list" || true done fi - # Clean recent items preferences safe_clean ~/Library/Preferences/com.apple.recentitems.plist "Recent items preferences" || true } -# Clean old mail downloads clean_mail_downloads() { stop_section_spinner local mail_age_days=${MOLE_MAIL_AGE_DAYS:-30} @@ -153,14 +129,11 @@ clean_mail_downloads() { local cleaned_kb=0 for target_path in "${mail_dirs[@]}"; do if [[ -d "$target_path" ]]; then - # Check directory size threshold local dir_size_kb=0 dir_size_kb=$(get_path_size_kb "$target_path") - # Skip if below threshold if [[ $dir_size_kb -lt ${MOLE_MAIL_DOWNLOADS_MIN_KB:-5120} ]]; then continue fi - # Find and remove files older than specified days while IFS= read -r -d '' file_path; do if [[ -f "$file_path" ]]; then local file_size_kb=$(get_path_size_kb "$file_path")