mirror of
https://github.com/tw93/Mole.git
synced 2026-02-15 18:40:05 +00:00
Simpler and faster
This commit is contained in:
14
bin/clean.sh
14
bin/clean.sh
@@ -271,7 +271,7 @@ safe_clean() {
|
|||||||
for path in "${existing_paths[@]}"; do
|
for path in "${existing_paths[@]}"; do
|
||||||
(
|
(
|
||||||
local size
|
local size
|
||||||
# Timeout protection: prevent du from hanging on problematic paths
|
# Get size quickly with depth limit
|
||||||
size=$(get_path_size_kb "$path")
|
size=$(get_path_size_kb "$path")
|
||||||
[[ -z "$size" || ! "$size" =~ ^[0-9]+$ ]] && size=0
|
[[ -z "$size" || ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||||
local count
|
local count
|
||||||
@@ -338,7 +338,7 @@ safe_clean() {
|
|||||||
|
|
||||||
for path in "${existing_paths[@]}"; do
|
for path in "${existing_paths[@]}"; do
|
||||||
local size_bytes count
|
local size_bytes count
|
||||||
# Get size quickly - du is fast
|
# Get size quickly with depth limit
|
||||||
size_bytes=$(get_path_size_kb "$path")
|
size_bytes=$(get_path_size_kb "$path")
|
||||||
[[ -z "$size_bytes" || ! "$size_bytes" =~ ^[0-9]+$ ]] && size_bytes=0
|
[[ -z "$size_bytes" || ! "$size_bytes" =~ ^[0-9]+$ ]] && size_bytes=0
|
||||||
# Quick file count for display - limit for performance
|
# Quick file count for display - limit for performance
|
||||||
@@ -695,11 +695,11 @@ perform_cleanup() {
|
|||||||
grep -v "^$" >> "$installed_bundles" || true
|
grep -v "^$" >> "$installed_bundles" || true
|
||||||
done
|
done
|
||||||
|
|
||||||
# Get running applications - no timeout needed for fast osascript
|
# Get running applications - timeout protection for osascript
|
||||||
osascript -e 'tell application "System Events" to get bundle identifier of every application process' 2> /dev/null |
|
run_with_timeout 5 osascript -e 'tell application "System Events" to get bundle identifier of every application process' 2> /dev/null |
|
||||||
tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' >> "$installed_bundles" || true
|
tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' >> "$installed_bundles" || true
|
||||||
|
|
||||||
# Get LaunchAgents - fast operation, no timeout needed
|
# Get LaunchAgents
|
||||||
find ~/Library/LaunchAgents /Library/LaunchAgents -name "*.plist" -type f 2> /dev/null |
|
find ~/Library/LaunchAgents /Library/LaunchAgents -name "*.plist" -type f 2> /dev/null |
|
||||||
xargs -I {} basename {} .plist >> "$installed_bundles" 2> /dev/null || true
|
xargs -I {} basename {} .plist >> "$installed_bundles" 2> /dev/null || true
|
||||||
|
|
||||||
@@ -817,9 +817,9 @@ perform_cleanup() {
|
|||||||
bundle_id="${bundle_id%.binarycookies}"
|
bundle_id="${bundle_id%.binarycookies}"
|
||||||
|
|
||||||
if is_orphaned "$bundle_id" "$match"; then
|
if is_orphaned "$bundle_id" "$match"; then
|
||||||
# Use timeout to prevent du from hanging on large/problematic directories
|
# Use timeout to prevent du from hanging on network mounts or problematic paths
|
||||||
local size_kb
|
local size_kb
|
||||||
size_kb=$(run_with_timeout 2 get_path_size_kb "$match")
|
size_kb=$(run_with_timeout 5 get_path_size_kb "$match")
|
||||||
if [[ -z "$size_kb" || "$size_kb" == "0" ]]; then
|
if [[ -z "$size_kb" || "$size_kb" == "0" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ clean_orphaned_app_data() {
|
|||||||
|
|
||||||
for app_dir in "${app_dirs[@]}"; do
|
for app_dir in "${app_dirs[@]}"; do
|
||||||
[[ -d "$app_dir" ]] || continue
|
[[ -d "$app_dir" ]] || continue
|
||||||
command find "$app_dir" -name "*.app" -maxdepth 3 -type d 2> /dev/null | while IFS= read -r app_path; do
|
find "$app_dir" -name "*.app" -maxdepth 3 -type d 2> /dev/null | while IFS= read -r app_path; do
|
||||||
local bundle_id
|
local bundle_id
|
||||||
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2> /dev/null || echo "")
|
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2> /dev/null || echo "")
|
||||||
[[ -n "$bundle_id" ]] && echo "$bundle_id"
|
[[ -n "$bundle_id" ]] && echo "$bundle_id"
|
||||||
@@ -112,9 +112,8 @@ clean_orphaned_app_data() {
|
|||||||
echo "$running_apps" | tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' >> "$installed_bundles"
|
echo "$running_apps" | tr ',' '\n' | sed -e 's/^ *//;s/ *$//' -e '/^$/d' >> "$installed_bundles"
|
||||||
|
|
||||||
run_with_timeout 5 find ~/Library/LaunchAgents /Library/LaunchAgents \
|
run_with_timeout 5 find ~/Library/LaunchAgents /Library/LaunchAgents \
|
||||||
-name "*.plist" -type f 2> /dev/null | while IFS= read -r plist; do
|
-name "*.plist" -type f 2> /dev/null |
|
||||||
basename "$plist" .plist
|
xargs -I {} basename {} .plist >> "$installed_bundles" 2> /dev/null || true
|
||||||
done >> "$installed_bundles" 2> /dev/null || true
|
|
||||||
|
|
||||||
# Deduplicate
|
# Deduplicate
|
||||||
sort -u "$installed_bundles" -o "$installed_bundles"
|
sort -u "$installed_bundles" -o "$installed_bundles"
|
||||||
@@ -230,9 +229,9 @@ clean_orphaned_app_data() {
|
|||||||
bundle_id="${bundle_id%.binarycookies}"
|
bundle_id="${bundle_id%.binarycookies}"
|
||||||
|
|
||||||
if is_orphaned "$bundle_id" "$match"; then
|
if is_orphaned "$bundle_id" "$match"; then
|
||||||
# Use timeout to prevent du from hanging on large/problematic directories
|
# Use timeout to prevent du from hanging on network mounts or problematic paths
|
||||||
local size_kb
|
local size_kb
|
||||||
size_kb=$(run_with_timeout 2 get_path_size_kb "$match")
|
size_kb=$(run_with_timeout 5 get_path_size_kb "$match")
|
||||||
if [[ -z "$size_kb" || "$size_kb" == "0" ]]; then
|
if [[ -z "$size_kb" || "$size_kb" == "0" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ clean_orphaned_casks() {
|
|||||||
true > "$cask_cache"
|
true > "$cask_cache"
|
||||||
|
|
||||||
while IFS= read -r cask; do
|
while IFS= read -r cask; do
|
||||||
# Get app path from cask info (expensive call, hence caching)
|
# Get app path from cask info with timeout protection (expensive call, hence caching)
|
||||||
local cask_info
|
local cask_info
|
||||||
cask_info=$(brew info --cask "$cask" 2> /dev/null || true)
|
cask_info=$(run_with_timeout 10 brew info --cask "$cask" 2> /dev/null || true)
|
||||||
|
|
||||||
# Extract app name from "AppName.app (App)" format in cask info output
|
# Extract app name from "AppName.app (App)" format in cask info output
|
||||||
local app_name
|
local app_name
|
||||||
|
|||||||
@@ -97,13 +97,29 @@ clean_service_worker_cache() {
|
|||||||
done < <(command find "$cache_path" -type d -depth 2 2> /dev/null)
|
done < <(command find "$cache_path" -type d -depth 2 2> /dev/null)
|
||||||
|
|
||||||
if [[ $cleaned_size -gt 0 ]]; then
|
if [[ $cleaned_size -gt 0 ]]; then
|
||||||
|
# Temporarily stop spinner for clean output
|
||||||
|
local spinner_was_running=false
|
||||||
|
if [[ -t 1 && -n "${INLINE_SPINNER_PID:-}" ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
spinner_was_running=true
|
||||||
|
fi
|
||||||
|
|
||||||
local cleaned_mb=$((cleaned_size / 1024))
|
local cleaned_mb=$((cleaned_size / 1024))
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
if [[ "$DRY_RUN" != "true" ]]; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker cache (${cleaned_mb}MB cleaned, $protected_count protected)"
|
if [[ $protected_count -gt 0 ]]; then
|
||||||
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker (${cleaned_mb}MB, ${protected_count} protected)"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}→${NC} $browser_name Service Worker cache (would clean ${cleaned_mb}MB, $protected_count protected)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker (${cleaned_mb}MB)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}→${NC} $browser_name Service Worker (would clean ${cleaned_mb}MB, ${protected_count} protected)"
|
||||||
fi
|
fi
|
||||||
note_activity
|
note_activity
|
||||||
|
|
||||||
|
# Restart spinner if it was running
|
||||||
|
if [[ "$spinner_was_running" == "true" ]]; then
|
||||||
|
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning browser Service Worker caches..."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,20 +38,18 @@ clean_broken_preferences() {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
# Validate plist using plutil
|
# Validate plist using plutil
|
||||||
if ! plutil -lint "$plist_file" > /dev/null 2>&1; then
|
plutil -lint "$plist_file" > /dev/null 2>&1 && continue
|
||||||
|
|
||||||
local size_kb
|
local size_kb
|
||||||
size_kb=$(get_path_size_kb "$plist_file")
|
size_kb=$(get_path_size_kb "$plist_file")
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
[[ "$DRY_RUN" != "true" ]] && rm -f "$plist_file" 2> /dev/null || true
|
||||||
rm -f "$plist_file" 2> /dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
((total_size_kb += size_kb))
|
((total_size_kb += size_kb))
|
||||||
fi
|
|
||||||
done < <(command find "$prefs_dir" -maxdepth 1 -name "*.plist" -type f 2> /dev/null || true)
|
done < <(command find "$prefs_dir" -maxdepth 1 -name "*.plist" -type f 2> /dev/null || true)
|
||||||
|
|
||||||
# Check ByHost preferences
|
# Check ByHost preferences with timeout protection
|
||||||
local byhost_dir="$prefs_dir/ByHost"
|
local byhost_dir="$prefs_dir/ByHost"
|
||||||
if [[ -d "$byhost_dir" ]]; then
|
if [[ -d "$byhost_dir" ]]; then
|
||||||
while IFS= read -r plist_file; do
|
while IFS= read -r plist_file; do
|
||||||
@@ -65,17 +63,15 @@ clean_broken_preferences() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if ! plutil -lint "$plist_file" > /dev/null 2>&1; then
|
plutil -lint "$plist_file" > /dev/null 2>&1 && continue
|
||||||
|
|
||||||
local size_kb
|
local size_kb
|
||||||
size_kb=$(get_path_size_kb "$plist_file")
|
size_kb=$(get_path_size_kb "$plist_file")
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
[[ "$DRY_RUN" != "true" ]] && rm -f "$plist_file" 2> /dev/null || true
|
||||||
rm -f "$plist_file" 2> /dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
((total_size_kb += size_kb))
|
((total_size_kb += size_kb))
|
||||||
fi
|
|
||||||
done < <(command find "$byhost_dir" -name "*.plist" -type f 2> /dev/null || true)
|
done < <(command find "$byhost_dir" -name "*.plist" -type f 2> /dev/null || true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -146,7 +142,6 @@ clean_broken_login_items() {
|
|||||||
size_kb=$(get_path_size_kb "$plist_file")
|
size_kb=$(get_path_size_kb "$plist_file")
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
if [[ "$DRY_RUN" != "true" ]]; then
|
||||||
# Unload first if loaded
|
|
||||||
launchctl unload "$plist_file" 2> /dev/null || true
|
launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
rm -f "$plist_file" 2> /dev/null || true
|
rm -f "$plist_file" 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ clean_deep_system() {
|
|||||||
|
|
||||||
# Clean old temp files
|
# Clean old temp files
|
||||||
local tmp_cleaned=0
|
local tmp_cleaned=0
|
||||||
local tmp_count=$(sudo command find /tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
local tmp_count=$(sudo find /tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||||
if [[ "$tmp_count" -gt 0 ]]; then
|
if [[ "$tmp_count" -gt 0 ]]; then
|
||||||
safe_sudo_find_delete "/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
safe_sudo_find_delete "/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
||||||
tmp_cleaned=1
|
tmp_cleaned=1
|
||||||
fi
|
fi
|
||||||
local var_tmp_count=$(sudo command find /var/tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
local var_tmp_count=$(sudo find /var/tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||||
if [[ "$var_tmp_count" -gt 0 ]]; then
|
if [[ "$var_tmp_count" -gt 0 ]]; then
|
||||||
safe_sudo_find_delete "/var/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
safe_sudo_find_delete "/var/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
||||||
tmp_cleaned=1
|
tmp_cleaned=1
|
||||||
@@ -42,7 +42,7 @@ clean_deep_system() {
|
|||||||
# These files are system-protected and cannot be removed
|
# These files are system-protected and cannot be removed
|
||||||
: # No-op, silently skip
|
: # No-op, silently skip
|
||||||
else
|
else
|
||||||
# SIP is disabled, attempt cleanup with restricted flag check
|
# SIP is disabled, attempt cleanup with restricted flag check and timeout protection
|
||||||
local updates_cleaned=0
|
local updates_cleaned=0
|
||||||
while IFS= read -r -d '' item; do
|
while IFS= read -r -d '' item; do
|
||||||
# Skip system-protected files (restricted flag)
|
# Skip system-protected files (restricted flag)
|
||||||
@@ -55,7 +55,7 @@ clean_deep_system() {
|
|||||||
if safe_sudo_remove "$item"; then
|
if safe_sudo_remove "$item"; then
|
||||||
((updates_cleaned++))
|
((updates_cleaned++))
|
||||||
fi
|
fi
|
||||||
done < <(command find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
done < <(command find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||||
[[ $updates_cleaned -gt 0 ]] && log_success "System library updates"
|
[[ $updates_cleaned -gt 0 ]] && log_success "System library updates"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -124,13 +124,25 @@ clean_time_machine_failed_backups() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local size_kb=$(get_path_size_kb "$inprogress_file")
|
local size_kb=$(get_path_size_kb "$inprogress_file")
|
||||||
if [[ "$size_kb" -gt 0 ]]; then
|
[[ "$size_kb" -le 0 ]] && continue
|
||||||
local backup_name=$(basename "$inprogress_file")
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
local backup_name=$(basename "$inprogress_file")
|
||||||
if command -v tmutil > /dev/null 2>&1; then
|
|
||||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo -e " ${YELLOW}→${NC} Failed backup: $backup_name ${YELLOW}($size_human dry)${NC}"
|
||||||
|
((tm_cleaned++))
|
||||||
|
note_activity
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Real deletion
|
||||||
|
if ! command -v tmutil > /dev/null 2>&1; then
|
||||||
|
echo -e " ${YELLOW}!${NC} tmutil not available, skipping: $backup_name"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Failed backup: $backup_name ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Failed backup: $backup_name ${GREEN}($size_human)${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
((files_cleaned++))
|
((files_cleaned++))
|
||||||
@@ -140,17 +152,7 @@ clean_time_machine_failed_backups() {
|
|||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Could not delete: $backup_name (try manually with sudo)"
|
echo -e " ${YELLOW}!${NC} Could not delete: $backup_name (try manually with sudo)"
|
||||||
fi
|
fi
|
||||||
else
|
done < <(run_with_timeout 15 find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||||
echo -e " ${YELLOW}!${NC} tmutil not available, skipping: $backup_name"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
|
||||||
echo -e " ${YELLOW}→${NC} Failed backup: $backup_name ${YELLOW}($size_human dry)${NC}"
|
|
||||||
((tm_cleaned++))
|
|
||||||
note_activity
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done < <(command find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# APFS style backups (.backupbundle or .sparsebundle)
|
# APFS style backups (.backupbundle or .sparsebundle)
|
||||||
@@ -176,13 +178,24 @@ clean_time_machine_failed_backups() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local size_kb=$(get_path_size_kb "$inprogress_file")
|
local size_kb=$(get_path_size_kb "$inprogress_file")
|
||||||
if [[ "$size_kb" -gt 0 ]]; then
|
[[ "$size_kb" -le 0 ]] && continue
|
||||||
local backup_name=$(basename "$inprogress_file")
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
local backup_name=$(basename "$inprogress_file")
|
||||||
if command -v tmutil > /dev/null 2>&1; then
|
|
||||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo -e " ${YELLOW}→${NC} Failed APFS backup in $bundle_name: $backup_name ${YELLOW}($size_human dry)${NC}"
|
||||||
|
((tm_cleaned++))
|
||||||
|
note_activity
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Real deletion
|
||||||
|
if ! command -v tmutil > /dev/null 2>&1; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Failed APFS backup in $bundle_name: $backup_name ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Failed APFS backup in $bundle_name: $backup_name ${GREEN}($size_human)${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
((files_cleaned++))
|
((files_cleaned++))
|
||||||
@@ -192,15 +205,7 @@ clean_time_machine_failed_backups() {
|
|||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Could not delete from bundle: $backup_name"
|
echo -e " ${YELLOW}!${NC} Could not delete from bundle: $backup_name"
|
||||||
fi
|
fi
|
||||||
fi
|
done < <(run_with_timeout 15 find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||||
else
|
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
|
||||||
echo -e " ${YELLOW}→${NC} Failed APFS backup in $bundle_name: $backup_name ${YELLOW}($size_human dry)${NC}"
|
|
||||||
((tm_cleaned++))
|
|
||||||
note_activity
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done < <(command find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ clean_user_essentials() {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
# Verify volume is mounted and not a symlink
|
# Verify volume is mounted and not a symlink
|
||||||
if mount | grep -q "on $volume " && [[ ! -L "$volume/.Trashes" ]]; then
|
mount | grep -q "on $volume " || continue
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
[[ -L "$volume/.Trashes" ]] && continue
|
||||||
|
[[ "$DRY_RUN" == "true" ]] && continue
|
||||||
|
|
||||||
# Safely iterate and remove each item
|
# Safely iterate and remove each item
|
||||||
while IFS= read -r -d '' item; do
|
while IFS= read -r -d '' item; do
|
||||||
safe_remove "$item" true || true
|
safe_remove "$item" true || true
|
||||||
done < <(command find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
done < <(command find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -55,8 +55,7 @@ clean_user_essentials() {
|
|||||||
clean_finder_metadata() {
|
clean_finder_metadata() {
|
||||||
if [[ "$PROTECT_FINDER_METADATA" == "true" ]]; then
|
if [[ "$PROTECT_FINDER_METADATA" == "true" ]]; then
|
||||||
note_activity
|
note_activity
|
||||||
echo -e " ${YELLOW}☻${NC} Finder metadata protected by whitelist"
|
echo -e " ${GRAY}${ICON_SUCCESS}${NC} Finder metadata (whitelisted)"
|
||||||
echo -e " ${YELLOW}☻${NC} Run ${GRAY}mo clean --whitelist${NC} to allow cleaning .DS_Store files"
|
|
||||||
else
|
else
|
||||||
clean_ds_store_tree "$HOME" "Home directory (.DS_Store)"
|
clean_ds_store_tree "$HOME" "Home directory (.DS_Store)"
|
||||||
|
|
||||||
@@ -99,26 +98,7 @@ clean_sandboxed_app_caches() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Clean browser caches (Safari, Chrome, Edge, Firefox, etc.)
|
# Clean browser caches (Safari, Chrome, Edge, Firefox, etc.)
|
||||||
# Warns if browsers are running (some cache files may be locked)
|
|
||||||
clean_browsers() {
|
clean_browsers() {
|
||||||
# Check for running browsers and warn user
|
|
||||||
local running_browsers=""
|
|
||||||
|
|
||||||
# Check each browser with case-insensitive process name matching
|
|
||||||
pgrep -i "safari" > /dev/null 2>&1 && running_browsers="Safari"
|
|
||||||
pgrep -i "chrome" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Chrome"
|
|
||||||
pgrep -i "firefox" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Firefox"
|
|
||||||
pgrep -i "edge" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Edge"
|
|
||||||
pgrep -i "brave" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Brave"
|
|
||||||
pgrep -i "arc" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Arc"
|
|
||||||
pgrep -i "opera" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Opera"
|
|
||||||
pgrep -i "vivaldi" > /dev/null 2>&1 && running_browsers="${running_browsers:+$running_browsers, }Vivaldi"
|
|
||||||
|
|
||||||
if [[ -n "$running_browsers" ]]; then
|
|
||||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Running: $running_browsers (some files may be locked)"
|
|
||||||
note_activity
|
|
||||||
fi
|
|
||||||
|
|
||||||
safe_clean ~/Library/Caches/com.apple.Safari/* "Safari cache"
|
safe_clean ~/Library/Caches/com.apple.Safari/* "Safari cache"
|
||||||
|
|
||||||
# Chrome/Chromium
|
# Chrome/Chromium
|
||||||
@@ -140,7 +120,12 @@ clean_browsers() {
|
|||||||
safe_clean ~/Library/Application\ Support/Firefox/Profiles/*/cache2/* "Firefox profile cache"
|
safe_clean ~/Library/Application\ Support/Firefox/Profiles/*/cache2/* "Firefox profile cache"
|
||||||
|
|
||||||
# Service Worker CacheStorage (all profiles)
|
# Service Worker CacheStorage (all profiles)
|
||||||
# Limit search depth to prevent hanging on large profile directories
|
# Show loading indicator for potentially slow scan
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning browser Service Worker caches..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Scan for Service Worker caches with timeout protection
|
||||||
while IFS= read -r sw_path; do
|
while IFS= read -r sw_path; do
|
||||||
[[ -z "$sw_path" ]] && continue
|
[[ -z "$sw_path" ]] && continue
|
||||||
local profile_name=$(basename "$(dirname "$(dirname "$sw_path")")")
|
local profile_name=$(basename "$(dirname "$(dirname "$sw_path")")")
|
||||||
@@ -150,11 +135,16 @@ clean_browsers() {
|
|||||||
[[ "$sw_path" == *"Arc"* ]] && browser_name="Arc"
|
[[ "$sw_path" == *"Arc"* ]] && browser_name="Arc"
|
||||||
[[ "$profile_name" != "Default" ]] && browser_name="$browser_name ($profile_name)"
|
[[ "$profile_name" != "Default" ]] && browser_name="$browser_name ($profile_name)"
|
||||||
clean_service_worker_cache "$browser_name" "$sw_path"
|
clean_service_worker_cache "$browser_name" "$sw_path"
|
||||||
done < <(command find "$HOME/Library/Application Support/Google/Chrome" \
|
done < <(run_with_timeout 10 find "$HOME/Library/Application Support/Google/Chrome" \
|
||||||
"$HOME/Library/Application Support/Microsoft Edge" \
|
"$HOME/Library/Application Support/Microsoft Edge" \
|
||||||
"$HOME/Library/Application Support/BraveSoftware/Brave-Browser" \
|
"$HOME/Library/Application Support/BraveSoftware/Brave-Browser" \
|
||||||
"$HOME/Library/Application Support/Arc/User Data" \
|
"$HOME/Library/Application Support/Arc/User Data" \
|
||||||
-maxdepth 6 -type d -name "CacheStorage" -path "*/Service Worker/*" 2> /dev/null || true)
|
-maxdepth 6 -type d -name "CacheStorage" -path "*/Service Worker/*" 2> /dev/null || true)
|
||||||
|
|
||||||
|
# Stop spinner after scan completes
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Clean cloud storage app caches
|
# Clean cloud storage app caches
|
||||||
@@ -197,14 +187,21 @@ clean_application_support_logs() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Show loading indicator for this potentially slow operation
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning Application Support directories..."
|
||||||
|
fi
|
||||||
|
|
||||||
# Clean log directories and cache patterns with iteration limit
|
# Clean log directories and cache patterns with iteration limit
|
||||||
|
# Limit iterations to balance thoroughness and performance
|
||||||
local iteration_count=0
|
local iteration_count=0
|
||||||
local max_iterations=200
|
local max_iterations=100
|
||||||
|
local cleaned_any=false
|
||||||
|
|
||||||
for app_dir in ~/Library/Application\ Support/*; do
|
for app_dir in ~/Library/Application\ Support/*; do
|
||||||
[[ -d "$app_dir" ]] || continue
|
[[ -d "$app_dir" ]] || continue
|
||||||
|
|
||||||
# Safety: limit iterations
|
# Safety: limit iterations to avoid excessive scanning
|
||||||
((iteration_count++))
|
((iteration_count++))
|
||||||
if [[ $iteration_count -gt $max_iterations ]]; then
|
if [[ $iteration_count -gt $max_iterations ]]; then
|
||||||
break
|
break
|
||||||
@@ -244,15 +241,15 @@ clean_application_support_logs() {
|
|||||||
safe_clean "$app_dir/Crashpad/completed"/* "Crash reports: $app_name"
|
safe_clean "$app_dir/Crashpad/completed"/* "Crash reports: $app_name"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean Service Worker caches (CacheStorage and ScriptCache)
|
# Clean Service Worker caches (CacheStorage and ScriptCache) with timeout protection
|
||||||
while IFS= read -r -d '' sw_cache; do
|
while IFS= read -r -d '' sw_cache; do
|
||||||
local profile_path=$(dirname "$(dirname "$sw_cache")")
|
local profile_path=$(dirname "$(dirname "$sw_cache")")
|
||||||
local profile_name=$(basename "$profile_path")
|
local profile_name=$(basename "$profile_path")
|
||||||
[[ "$profile_name" == "User Data" ]] && profile_name=$(basename "$(dirname "$profile_path")")
|
[[ "$profile_name" == "User Data" ]] && profile_name=$(basename "$(dirname "$profile_path")")
|
||||||
clean_service_worker_cache "$app_name ($profile_name)" "$sw_cache"
|
clean_service_worker_cache "$app_name ($profile_name)" "$sw_cache"
|
||||||
done < <(command find "$app_dir" -maxdepth 4 -type d \( -name "CacheStorage" -o -name "ScriptCache" \) -path "*/Service Worker/*" 2> /dev/null || true)
|
done < <(find "$app_dir" -maxdepth 4 -type d \( -name "CacheStorage" -o -name "ScriptCache" \) -path "*/Service Worker/*" -print0 2> /dev/null || true)
|
||||||
|
|
||||||
# Clean stale update downloads (older than 7 days)
|
# Clean stale update downloads (older than 7 days) with timeout protection
|
||||||
if [[ -d "$app_dir/update" ]] && ls "$app_dir/update" > /dev/null 2>&1; then
|
if [[ -d "$app_dir/update" ]] && ls "$app_dir/update" > /dev/null 2>&1; then
|
||||||
while IFS= read -r update_dir; do
|
while IFS= read -r update_dir; do
|
||||||
local dir_age_days=$((($(date +%s) - $(get_file_mtime "$update_dir")) / 86400))
|
local dir_age_days=$((($(date +%s) - $(get_file_mtime "$update_dir")) / 86400))
|
||||||
@@ -263,19 +260,24 @@ clean_application_support_logs() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Clean Group Containers logs
|
# Clean Group Containers logs with timeout protection
|
||||||
if [[ -d "$HOME/Library/Group Containers" ]]; then
|
if [[ -d "$HOME/Library/Group Containers" ]]; then
|
||||||
while IFS= read -r logs_dir; do
|
while IFS= read -r logs_dir; do
|
||||||
local container_name=$(basename "$(dirname "$logs_dir")")
|
local container_name=$(basename "$(dirname "$logs_dir")")
|
||||||
safe_clean "$logs_dir"/* "Group container logs: $container_name"
|
safe_clean "$logs_dir"/* "Group container logs: $container_name"
|
||||||
done < <(command find "$HOME/Library/Group Containers" -maxdepth 2 -type d -name "Logs" 2> /dev/null || true)
|
done < <(command find "$HOME/Library/Group Containers" -maxdepth 2 -type d -name "Logs" 2> /dev/null || true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Stop loading indicator
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
stop_inline_spinner
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check and show iOS device backup info
|
# Check and show iOS device backup info
|
||||||
check_ios_device_backups() {
|
check_ios_device_backups() {
|
||||||
local backup_dir="$HOME/Library/Application Support/MobileSync/Backup"
|
local backup_dir="$HOME/Library/Application Support/MobileSync/Backup"
|
||||||
if [[ -d "$backup_dir" ]] && command find "$backup_dir" -mindepth 1 -maxdepth 1 | read -r _; then
|
if [[ -d "$backup_dir" ]] && command find "$backup_dir" -mindepth 1 -maxdepth 1 2> /dev/null | read -r _; then
|
||||||
local backup_kb=$(get_path_size_kb "$backup_dir")
|
local backup_kb=$(get_path_size_kb "$backup_dir")
|
||||||
if [[ -n "${backup_kb:-}" && "$backup_kb" -gt 102400 ]]; then
|
if [[ -n "${backup_kb:-}" && "$backup_kb" -gt 102400 ]]; then
|
||||||
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||||
|
|||||||
@@ -1201,48 +1201,36 @@ force_kill_app() {
|
|||||||
# Use executable name for precise matching, fallback to app name
|
# Use executable name for precise matching, fallback to app name
|
||||||
local match_pattern="${exec_name:-$app_name}"
|
local match_pattern="${exec_name:-$app_name}"
|
||||||
|
|
||||||
# Check if main process is running using exact match
|
# Check if process is running using exact match only
|
||||||
local has_main_process=false
|
if ! pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||||
if pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
|
||||||
has_main_process=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Also check for related processes using fuzzy match
|
|
||||||
local has_related_processes=false
|
|
||||||
if pgrep -i "$match_pattern" > /dev/null 2>&1; then
|
|
||||||
has_related_processes=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If nothing is running, return success
|
|
||||||
if [[ "$has_main_process" == false && "$has_related_processes" == false ]]; then
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Try graceful termination first for exact match
|
# Try graceful termination first
|
||||||
if [[ "$has_main_process" == true ]]; then
|
|
||||||
pkill -x "$match_pattern" 2> /dev/null || true
|
pkill -x "$match_pattern" 2> /dev/null || true
|
||||||
fi
|
|
||||||
|
|
||||||
# Also try graceful termination for related processes
|
|
||||||
if [[ "$has_related_processes" == true ]]; then
|
|
||||||
pkill -i "$match_pattern" 2> /dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
# Check again after graceful kill
|
# Check again after graceful kill
|
||||||
if ! pgrep -i "$match_pattern" > /dev/null 2>&1; then
|
if ! pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force kill if still running
|
# Force kill if still running
|
||||||
pkill -9 -i "$match_pattern" 2> /dev/null || true
|
pkill -9 -x "$match_pattern" 2> /dev/null || true
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
# If still running and sudo is available, try with sudo
|
||||||
|
if pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||||
|
if sudo -n true 2> /dev/null; then
|
||||||
|
sudo pkill -9 -x "$match_pattern" 2> /dev/null || true
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Final check with longer timeout for stubborn processes
|
# Final check with longer timeout for stubborn processes
|
||||||
local retries=3
|
local retries=3
|
||||||
while [[ $retries -gt 0 ]]; do
|
while [[ $retries -gt 0 ]]; do
|
||||||
if ! pgrep -i "$match_pattern" > /dev/null 2>&1; then
|
if ! pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -1250,7 +1238,7 @@ force_kill_app() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Still running after all attempts
|
# Still running after all attempts
|
||||||
pgrep -i "$match_pattern" > /dev/null 2>&1 && return 1 || return 0
|
pgrep -x "$match_pattern" > /dev/null 2>&1 && return 1 || return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove application icons from the Dock (best effort)
|
# Remove application icons from the Dock (best effort)
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ batch_uninstall_applications() {
|
|||||||
local -a app_details=()
|
local -a app_details=()
|
||||||
local -a dock_cleanup_paths=()
|
local -a dock_cleanup_paths=()
|
||||||
|
|
||||||
# Silent analysis without spinner output (avoid visual flicker)
|
# Analyze selected apps with progress indicator
|
||||||
|
if [[ -t 1 ]]; then start_inline_spinner "Scanning files..."; fi
|
||||||
for selected_app in "${selected_apps[@]}"; do
|
for selected_app in "${selected_apps[@]}"; do
|
||||||
[[ -z "$selected_app" ]] && continue
|
[[ -z "$selected_app" ]] && continue
|
||||||
IFS='|' read -r _ app_path app_name bundle_id _ _ <<< "$selected_app"
|
IFS='|' read -r _ app_path app_name bundle_id _ _ <<< "$selected_app"
|
||||||
@@ -161,6 +162,7 @@ batch_uninstall_applications() {
|
|||||||
encoded_system_files=$(printf '%s' "$system_files" | base64 | tr -d '\n')
|
encoded_system_files=$(printf '%s' "$system_files" | base64 | tr -d '\n')
|
||||||
app_details+=("$app_name|$app_path|$bundle_id|$total_kb|$encoded_files|$encoded_system_files")
|
app_details+=("$app_name|$app_path|$bundle_id|$total_kb|$encoded_files|$encoded_system_files")
|
||||||
done
|
done
|
||||||
|
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||||
|
|
||||||
# Format size display (convert KB to bytes for bytes_to_human())
|
# Format size display (convert KB to bytes for bytes_to_human())
|
||||||
local size_display=$(bytes_to_human "$((total_estimated_size * 1024))")
|
local size_display=$(bytes_to_human "$((total_estimated_size * 1024))")
|
||||||
@@ -292,7 +294,7 @@ batch_uninstall_applications() {
|
|||||||
reason="still running"
|
reason="still running"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove the application
|
# Remove the application only if not running
|
||||||
if [[ -z "$reason" ]]; then
|
if [[ -z "$reason" ]]; then
|
||||||
if [[ "$needs_sudo" == true ]]; then
|
if [[ "$needs_sudo" == true ]]; then
|
||||||
safe_sudo_remove "$app_path" || reason="remove failed"
|
safe_sudo_remove "$app_path" || reason="remove failed"
|
||||||
@@ -301,7 +303,7 @@ batch_uninstall_applications() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove user-level and system-level files
|
# Remove related files if app removal succeeded
|
||||||
if [[ -z "$reason" ]]; then
|
if [[ -z "$reason" ]]; then
|
||||||
# Remove user-level files
|
# Remove user-level files
|
||||||
remove_file_list "$related_files" "false" > /dev/null
|
remove_file_list "$related_files" "false" > /dev/null
|
||||||
|
|||||||
2
mole
2
mole
@@ -22,7 +22,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
source "$SCRIPT_DIR/lib/core/common.sh"
|
source "$SCRIPT_DIR/lib/core/common.sh"
|
||||||
|
|
||||||
# Version info
|
# Version info
|
||||||
VERSION="1.11.23"
|
VERSION="1.11.24"
|
||||||
MOLE_TAGLINE="can dig deep to clean your Mac."
|
MOLE_TAGLINE="can dig deep to clean your Mac."
|
||||||
|
|
||||||
# Check if Touch ID is already configured
|
# Check if Touch ID is already configured
|
||||||
|
|||||||
Reference in New Issue
Block a user