mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 18:30:08 +00:00
refactor(clean): merge app cache/user essentials flow
- reorganize perform_cleanup sections and naming - merge macOS + sandbox caches into clean_app_caches - move recent items and mail downloads into user essentials - update core/user tests for renamed internal helpers
This commit is contained in:
65
bin/clean.sh
65
bin/clean.sh
@@ -677,8 +677,6 @@ safe_clean() {
|
||||
echo "$display_path # $size_human" >> "$EXPORT_LIST_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f "$paths_temp"
|
||||
fi
|
||||
else
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label${NC}, ${GREEN}$size_human${NC}"
|
||||
@@ -895,9 +893,9 @@ perform_cleanup() {
|
||||
# Allow per-section failures without aborting the full run.
|
||||
set +e
|
||||
|
||||
# ===== 1. Deep system cleanup (if admin) =====
|
||||
# ===== 1. System =====
|
||||
if [[ "$SYSTEM_CLEAN" == "true" ]]; then
|
||||
start_section "Deep system"
|
||||
start_section "System"
|
||||
clean_deep_system
|
||||
clean_local_snapshots
|
||||
end_section
|
||||
@@ -910,84 +908,71 @@ perform_cleanup() {
|
||||
done
|
||||
fi
|
||||
|
||||
# ===== 2. User essentials =====
|
||||
start_section "User essentials"
|
||||
clean_user_essentials
|
||||
clean_finder_metadata
|
||||
scan_external_volumes
|
||||
end_section
|
||||
|
||||
start_section "Finder metadata"
|
||||
clean_finder_metadata
|
||||
# ===== 3. App caches (merged sandboxed and standard app caches) =====
|
||||
start_section "App caches"
|
||||
clean_app_caches
|
||||
end_section
|
||||
|
||||
# ===== 3. macOS system caches =====
|
||||
start_section "macOS system caches"
|
||||
clean_macos_system_caches
|
||||
clean_recent_items
|
||||
clean_mail_downloads
|
||||
end_section
|
||||
|
||||
# ===== 4. Sandboxed app caches =====
|
||||
start_section "Sandboxed app caches"
|
||||
clean_sandboxed_app_caches
|
||||
end_section
|
||||
|
||||
# ===== 5. Browsers =====
|
||||
# ===== 4. Browsers =====
|
||||
start_section "Browsers"
|
||||
clean_browsers
|
||||
end_section
|
||||
|
||||
# ===== 6. Cloud storage =====
|
||||
start_section "Cloud storage"
|
||||
# ===== 5. Cloud & Office =====
|
||||
start_section "Cloud & Office"
|
||||
clean_cloud_storage
|
||||
end_section
|
||||
|
||||
# ===== 7. Office applications =====
|
||||
start_section "Office applications"
|
||||
clean_office_applications
|
||||
end_section
|
||||
|
||||
# ===== 8. Developer tools =====
|
||||
# ===== 6. Developer tools (merged CLI and GUI tooling) =====
|
||||
start_section "Developer tools"
|
||||
clean_developer_tools
|
||||
end_section
|
||||
|
||||
# ===== 9. Development applications =====
|
||||
start_section "Development applications"
|
||||
# ===== 7. Applications =====
|
||||
start_section "Applications"
|
||||
clean_user_gui_applications
|
||||
end_section
|
||||
|
||||
# ===== 10. Virtualization tools =====
|
||||
start_section "Virtual machine tools"
|
||||
# ===== 8. Virtualization =====
|
||||
start_section "Virtualization"
|
||||
clean_virtualization_tools
|
||||
end_section
|
||||
|
||||
# ===== 11. Application Support logs and caches cleanup =====
|
||||
# ===== 9. Application Support =====
|
||||
start_section "Application Support"
|
||||
clean_application_support_logs
|
||||
end_section
|
||||
|
||||
# ===== 12. Orphaned app data cleanup (60+ days inactive, skip protected vendors) =====
|
||||
start_section "Uninstalled app data"
|
||||
# ===== 10. Orphaned data =====
|
||||
start_section "Orphaned data"
|
||||
clean_orphaned_app_data
|
||||
clean_orphaned_system_services
|
||||
clean_orphaned_launch_agents
|
||||
end_section
|
||||
|
||||
# ===== 13. Apple Silicon optimizations =====
|
||||
# ===== 11. Apple Silicon =====
|
||||
clean_apple_silicon_caches
|
||||
|
||||
# ===== 14. iOS device backups =====
|
||||
start_section "iOS device backups"
|
||||
# ===== 12. Device backups =====
|
||||
start_section "Device backups"
|
||||
check_ios_device_backups
|
||||
end_section
|
||||
|
||||
# ===== 15. Time Machine incomplete backups =====
|
||||
start_section "Time Machine incomplete backups"
|
||||
# ===== 13. Time Machine =====
|
||||
start_section "Time Machine"
|
||||
clean_time_machine_failed_backups
|
||||
end_section
|
||||
|
||||
# ===== 16. Large files to review (report only) =====
|
||||
start_section "Large files to review"
|
||||
# ===== 14. Large files =====
|
||||
start_section "Large files"
|
||||
check_large_file_candidates
|
||||
end_section
|
||||
|
||||
|
||||
@@ -35,6 +35,79 @@ clean_user_essentials() {
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · already empty"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Recent items
|
||||
_clean_recent_items
|
||||
|
||||
# Mail downloads
|
||||
_clean_mail_downloads
|
||||
}
|
||||
|
||||
# Internal: Remove recent items lists.
|
||||
_clean_recent_items() {
|
||||
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||
local -a recent_lists=(
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentDocuments.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentServers.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentHosts.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentDocuments.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentServers.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentHosts.sfl"
|
||||
)
|
||||
if [[ -d "$shared_dir" ]]; then
|
||||
for sfl_file in "${recent_lists[@]}"; do
|
||||
[[ -e "$sfl_file" ]] && safe_clean "$sfl_file" "Recent items list" || true
|
||||
done
|
||||
fi
|
||||
safe_clean ~/Library/Preferences/com.apple.recentitems.plist "Recent items preferences" || true
|
||||
}
|
||||
|
||||
# Internal: Clean old mail downloads.
|
||||
_clean_mail_downloads() {
|
||||
local mail_age_days=${MOLE_MAIL_AGE_DAYS:-}
|
||||
if ! [[ "$mail_age_days" =~ ^[0-9]+$ ]]; then
|
||||
mail_age_days=30
|
||||
fi
|
||||
local -a mail_dirs=(
|
||||
"$HOME/Library/Mail Downloads"
|
||||
"$HOME/Library/Containers/com.apple.mail/Data/Library/Mail Downloads"
|
||||
)
|
||||
local count=0
|
||||
local cleaned_kb=0
|
||||
for target_path in "${mail_dirs[@]}"; do
|
||||
if [[ -d "$target_path" ]]; then
|
||||
local dir_size_kb=0
|
||||
dir_size_kb=$(get_path_size_kb "$target_path")
|
||||
if ! [[ "$dir_size_kb" =~ ^[0-9]+$ ]]; then
|
||||
dir_size_kb=0
|
||||
fi
|
||||
local min_kb="${MOLE_MAIL_DOWNLOADS_MIN_KB:-}"
|
||||
if ! [[ "$min_kb" =~ ^[0-9]+$ ]]; then
|
||||
min_kb=5120
|
||||
fi
|
||||
if [[ "$dir_size_kb" -lt "$min_kb" ]]; then
|
||||
continue
|
||||
fi
|
||||
while IFS= read -r -d '' file_path; do
|
||||
if [[ -f "$file_path" ]]; then
|
||||
local file_size_kb
|
||||
file_size_kb=$(get_path_size_kb "$file_path")
|
||||
if safe_remove "$file_path" true; then
|
||||
((count++))
|
||||
((cleaned_kb += file_size_kb))
|
||||
fi
|
||||
fi
|
||||
done < <(command find "$target_path" -type f -mtime +"$mail_age_days" -print0 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
if [[ $count -gt 0 ]]; then
|
||||
local cleaned_mb
|
||||
cleaned_mb=$(echo "$cleaned_kb" | awk '{printf "%.1f", $1/1024}' || echo "0.0")
|
||||
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $count mail attachments, about ${cleaned_mb}MB"
|
||||
note_activity
|
||||
fi
|
||||
}
|
||||
|
||||
# Remove old Google Chrome versions while keeping Current.
|
||||
@@ -321,9 +394,9 @@ clean_finder_metadata() {
|
||||
fi
|
||||
clean_ds_store_tree "$HOME" "Home directory, .DS_Store"
|
||||
}
|
||||
# macOS system caches and user-level leftovers.
|
||||
clean_macos_system_caches() {
|
||||
# safe_clean already checks protected paths.
|
||||
# App caches (merged: macOS system caches + Sandboxed apps).
|
||||
clean_app_caches() {
|
||||
# macOS system caches (merged from clean_macos_system_caches)
|
||||
safe_clean ~/Library/Saved\ Application\ State/* "Saved application states" || true
|
||||
safe_clean ~/Library/Caches/com.apple.photoanalysisd "Photo analysis cache" || true
|
||||
safe_clean ~/Library/Caches/com.apple.akd "Apple ID cache" || true
|
||||
@@ -340,70 +413,8 @@ clean_macos_system_caches() {
|
||||
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() {
|
||||
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||
local -a recent_lists=(
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentDocuments.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentServers.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentHosts.sfl2"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentDocuments.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentServers.sfl"
|
||||
"$shared_dir/com.apple.LSSharedFileList.RecentHosts.sfl"
|
||||
)
|
||||
if [[ -d "$shared_dir" ]]; then
|
||||
for sfl_file in "${recent_lists[@]}"; do
|
||||
[[ -e "$sfl_file" ]] && safe_clean "$sfl_file" "Recent items list" || true
|
||||
done
|
||||
fi
|
||||
safe_clean ~/Library/Preferences/com.apple.recentitems.plist "Recent items preferences" || true
|
||||
}
|
||||
clean_mail_downloads() {
|
||||
local mail_age_days=${MOLE_MAIL_AGE_DAYS:-}
|
||||
if ! [[ "$mail_age_days" =~ ^[0-9]+$ ]]; then
|
||||
mail_age_days=30
|
||||
fi
|
||||
local -a mail_dirs=(
|
||||
"$HOME/Library/Mail Downloads"
|
||||
"$HOME/Library/Containers/com.apple.mail/Data/Library/Mail Downloads"
|
||||
)
|
||||
local count=0
|
||||
local cleaned_kb=0
|
||||
for target_path in "${mail_dirs[@]}"; do
|
||||
if [[ -d "$target_path" ]]; then
|
||||
local dir_size_kb=0
|
||||
dir_size_kb=$(get_path_size_kb "$target_path")
|
||||
if ! [[ "$dir_size_kb" =~ ^[0-9]+$ ]]; then
|
||||
dir_size_kb=0
|
||||
fi
|
||||
local min_kb="${MOLE_MAIL_DOWNLOADS_MIN_KB:-}"
|
||||
if ! [[ "$min_kb" =~ ^[0-9]+$ ]]; then
|
||||
min_kb=5120
|
||||
fi
|
||||
if [[ "$dir_size_kb" -lt "$min_kb" ]]; then
|
||||
continue
|
||||
fi
|
||||
while IFS= read -r -d '' file_path; do
|
||||
if [[ -f "$file_path" ]]; then
|
||||
local file_size_kb=$(get_path_size_kb "$file_path")
|
||||
if safe_remove "$file_path" true; then
|
||||
((count++))
|
||||
((cleaned_kb += file_size_kb))
|
||||
fi
|
||||
fi
|
||||
done < <(command find "$target_path" -type f -mtime +"$mail_age_days" -print0 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
if [[ $count -gt 0 ]]; then
|
||||
local cleaned_mb=$(echo "$cleaned_kb" | awk '{printf "%.1f", $1/1024}' || echo "0.0")
|
||||
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $count mail attachments, about ${cleaned_mb}MB"
|
||||
note_activity
|
||||
fi
|
||||
}
|
||||
# Sandboxed app caches.
|
||||
clean_sandboxed_app_caches() {
|
||||
|
||||
# Sandboxed app caches
|
||||
stop_section_spinner
|
||||
safe_clean ~/Library/Containers/com.apple.wallpaper.agent/Data/Library/Caches/* "Wallpaper agent cache"
|
||||
safe_clean ~/Library/Containers/com.apple.mediaanalysisd/Data/Library/Caches/* "Media analysis cache"
|
||||
@@ -415,7 +426,7 @@ clean_sandboxed_app_caches() {
|
||||
local total_size=0
|
||||
local cleaned_count=0
|
||||
local found_any=false
|
||||
# Use nullglob to avoid literal globs.
|
||||
|
||||
local _ng_state
|
||||
_ng_state=$(shopt -p nullglob || true)
|
||||
shopt -s nullglob
|
||||
@@ -424,8 +435,10 @@ clean_sandboxed_app_caches() {
|
||||
done
|
||||
eval "$_ng_state"
|
||||
stop_section_spinner
|
||||
|
||||
if [[ "$found_any" == "true" ]]; then
|
||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
local size_human
|
||||
size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Sandboxed app caches${NC}, ${YELLOW}$size_human dry${NC}"
|
||||
else
|
||||
@@ -444,7 +457,8 @@ process_container_cache() {
|
||||
local container_dir="$1"
|
||||
[[ -d "$container_dir" ]] || return 0
|
||||
[[ -L "$container_dir" ]] && return 0
|
||||
local bundle_id=$(basename "$container_dir")
|
||||
local bundle_id
|
||||
bundle_id=$(basename "$container_dir")
|
||||
if is_critical_system_component "$bundle_id"; then
|
||||
return 0
|
||||
fi
|
||||
@@ -456,23 +470,17 @@ process_container_cache() {
|
||||
[[ -L "$cache_dir" ]] && return 0
|
||||
# Fast non-empty check.
|
||||
if find "$cache_dir" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$cache_dir")
|
||||
local size
|
||||
size=$(get_path_size_kb "$cache_dir")
|
||||
((total_size += size))
|
||||
found_any=true
|
||||
((cleaned_count++))
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# For directories with many files, use find -delete for performance
|
||||
if ! find "$cache_dir" -mindepth 1 -delete 2> /dev/null; then
|
||||
# Fallback: try item-by-item if find fails
|
||||
local _ng_state
|
||||
_ng_state=$(shopt -p nullglob || true)
|
||||
shopt -s nullglob
|
||||
for item in "$cache_dir"/*; do
|
||||
local item
|
||||
while IFS= read -r -d '' item; do
|
||||
[[ -e "$item" ]] || continue
|
||||
safe_remove "$item" true || true
|
||||
done
|
||||
eval "$_ng_state"
|
||||
fi
|
||||
done < <(command find "$cache_dir" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -489,10 +497,9 @@ clean_group_container_caches() {
|
||||
local total_size=0
|
||||
local cleaned_count=0
|
||||
local found_any=false
|
||||
local _ng_state
|
||||
_ng_state=$(shopt -p nullglob || true)
|
||||
shopt -s nullglob
|
||||
|
||||
# Collect all non-Apple container directories first
|
||||
local -a containers=()
|
||||
local container_dir
|
||||
for container_dir in "$group_containers_dir"/*; do
|
||||
[[ -d "$container_dir" ]] || continue
|
||||
@@ -506,12 +513,18 @@ clean_group_container_caches() {
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
containers+=("$container_dir")
|
||||
done
|
||||
|
||||
# Process each container's candidate directories
|
||||
for container_dir in "${containers[@]}"; do
|
||||
local container_id
|
||||
container_id=$(basename "$container_dir")
|
||||
local normalized_id="$container_id"
|
||||
[[ "$normalized_id" == group.* ]] && normalized_id="${normalized_id#group.}"
|
||||
|
||||
local protected_container=false
|
||||
if should_protect_data "$container_id" || should_protect_data "$normalized_id"; then
|
||||
if should_protect_data "$container_id" 2> /dev/null || should_protect_data "$normalized_id" 2> /dev/null; then
|
||||
protected_container=true
|
||||
fi
|
||||
|
||||
@@ -532,39 +545,47 @@ clean_group_container_caches() {
|
||||
for candidate in "${candidates[@]}"; do
|
||||
[[ -d "$candidate" ]] || continue
|
||||
[[ -L "$candidate" ]] && continue
|
||||
if is_path_whitelisted "$candidate"; then
|
||||
continue
|
||||
fi
|
||||
if ! find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
if is_path_whitelisted "$candidate" 2> /dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local candidate_size_kb=0
|
||||
local candidate_changed=false
|
||||
# Build non-protected candidate items for cleanup.
|
||||
local -a items_to_clean=()
|
||||
local item
|
||||
while IFS= read -r -d '' item; do
|
||||
[[ -e "$item" ]] || continue
|
||||
[[ -L "$item" ]] && continue
|
||||
if should_protect_path "$item" || is_path_whitelisted "$item"; then
|
||||
if should_protect_path "$item" 2> /dev/null || is_path_whitelisted "$item" 2> /dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local item_size_kb
|
||||
item_size_kb=$(get_path_size_kb "$item")
|
||||
[[ "$item_size_kb" =~ ^[0-9]+$ ]] || item_size_kb=0
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
candidate_changed=true
|
||||
((candidate_size_kb += item_size_kb))
|
||||
continue
|
||||
fi
|
||||
|
||||
if safe_remove "$item" true; then
|
||||
candidate_changed=true
|
||||
((candidate_size_kb += item_size_kb))
|
||||
else
|
||||
items_to_clean+=("$item")
|
||||
fi
|
||||
done < <(command find "$candidate" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
|
||||
[[ ${#items_to_clean[@]} -gt 0 ]] || continue
|
||||
|
||||
local candidate_size_kb=0
|
||||
local candidate_changed=false
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
for item in "${items_to_clean[@]}"; do
|
||||
local item_size
|
||||
item_size=$(get_path_size_kb "$item" 2> /dev/null) || item_size=0
|
||||
[[ "$item_size" =~ ^[0-9]+$ ]] || item_size=0
|
||||
candidate_changed=true
|
||||
((candidate_size_kb += item_size))
|
||||
done
|
||||
else
|
||||
for item in "${items_to_clean[@]}"; do
|
||||
local item_size
|
||||
item_size=$(get_path_size_kb "$item" 2> /dev/null) || item_size=0
|
||||
[[ "$item_size" =~ ^[0-9]+$ ]] || item_size=0
|
||||
if safe_remove "$item" true 2> /dev/null; then
|
||||
candidate_changed=true
|
||||
((candidate_size_kb += item_size))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$candidate_changed" == "true" ]]; then
|
||||
((total_size += candidate_size_kb))
|
||||
((cleaned_count++))
|
||||
@@ -573,7 +594,6 @@ clean_group_container_caches() {
|
||||
done
|
||||
done
|
||||
|
||||
eval "$_ng_state"
|
||||
stop_section_spinner
|
||||
|
||||
if [[ "$found_any" == "true" ]]; then
|
||||
@@ -681,8 +701,10 @@ clean_application_support_logs() {
|
||||
shopt -s nullglob
|
||||
for app_dir in ~/Library/Application\ Support/*; do
|
||||
[[ -d "$app_dir" ]] || continue
|
||||
local app_name=$(basename "$app_dir")
|
||||
local app_name_lower=$(echo "$app_name" | LC_ALL=C tr '[:upper:]' '[:lower:]')
|
||||
local app_name
|
||||
app_name=$(basename "$app_dir")
|
||||
local app_name_lower
|
||||
app_name_lower=$(echo "$app_name" | LC_ALL=C tr '[:upper:]' '[:lower:]')
|
||||
local is_protected=false
|
||||
if should_protect_data "$app_name"; then
|
||||
is_protected=true
|
||||
@@ -699,20 +721,17 @@ clean_application_support_logs() {
|
||||
for candidate in "${start_candidates[@]}"; do
|
||||
if [[ -d "$candidate" ]]; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$candidate")
|
||||
local size
|
||||
size=$(get_path_size_kb "$candidate")
|
||||
((total_size += size))
|
||||
((cleaned_count++))
|
||||
found_any=true
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# For directories with many files, use find -delete for performance
|
||||
# This avoids shell expansion and individual safe_remove calls
|
||||
if ! find "$candidate" -mindepth 1 -delete 2> /dev/null; then
|
||||
# Fallback: try item-by-item if find fails
|
||||
for item in "$candidate"/*; do
|
||||
local item
|
||||
while IFS= read -r -d '' item; do
|
||||
[[ -e "$item" ]] || continue
|
||||
safe_remove "$item" true > /dev/null 2>&1 || true
|
||||
done
|
||||
fi
|
||||
done < <(command find "$candidate" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -728,20 +747,17 @@ clean_application_support_logs() {
|
||||
for candidate in "${gc_candidates[@]}"; do
|
||||
if [[ -d "$candidate" ]]; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$candidate")
|
||||
local size
|
||||
size=$(get_path_size_kb "$candidate")
|
||||
((total_size += size))
|
||||
((cleaned_count++))
|
||||
found_any=true
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# For directories with many files, use find -delete for performance
|
||||
# This avoids shell expansion and individual safe_remove calls
|
||||
if ! find "$candidate" -mindepth 1 -delete 2> /dev/null; then
|
||||
# Fallback: try item-by-item if find fails
|
||||
for item in "$candidate"/*; do
|
||||
local item
|
||||
while IFS= read -r -d '' item; do
|
||||
[[ -e "$item" ]] || continue
|
||||
safe_remove "$item" true > /dev/null 2>&1 || true
|
||||
done
|
||||
fi
|
||||
done < <(command find "$candidate" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -750,7 +766,8 @@ clean_application_support_logs() {
|
||||
eval "$_ng_state"
|
||||
stop_section_spinner
|
||||
if [[ "$found_any" == "true" ]]; then
|
||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
local size_human
|
||||
size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Application Support logs/caches${NC}, ${YELLOW}$size_human dry${NC}"
|
||||
else
|
||||
@@ -767,9 +784,11 @@ check_ios_device_backups() {
|
||||
local backup_dir="$HOME/Library/Application Support/MobileSync/Backup"
|
||||
# Simplified check without find to avoid hanging.
|
||||
if [[ -d "$backup_dir" ]]; then
|
||||
local backup_kb=$(get_path_size_kb "$backup_dir")
|
||||
local backup_kb
|
||||
backup_kb=$(get_path_size_kb "$backup_dir")
|
||||
if [[ -n "${backup_kb:-}" && "$backup_kb" -gt 102400 ]]; then
|
||||
local backup_human=$(command du -shP "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||
local backup_human
|
||||
backup_human=$(command du -shP "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||
if [[ -n "$backup_human" ]]; then
|
||||
note_activity
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} iOS backups: ${GREEN}${backup_human}${NC}${GRAY}, Path: $backup_dir${NC}"
|
||||
|
||||
@@ -107,7 +107,7 @@ EOF
|
||||
[ -f "$HOME/Documents/.DS_Store" ]
|
||||
}
|
||||
|
||||
@test "clean_recent_items removes shared file lists" {
|
||||
@test "_clean_recent_items removes shared file lists" {
|
||||
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||
mkdir -p "$shared_dir"
|
||||
touch "$shared_dir/com.apple.LSSharedFileList.RecentApplications.sfl2"
|
||||
@@ -120,14 +120,14 @@ source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
safe_clean() {
|
||||
echo "safe_clean $1"
|
||||
}
|
||||
clean_recent_items
|
||||
_clean_recent_items
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Recent"* ]]
|
||||
}
|
||||
|
||||
@test "clean_recent_items handles missing shared directory" {
|
||||
@test "_clean_recent_items handles missing shared directory" {
|
||||
rm -rf "$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
|
||||
@@ -137,13 +137,13 @@ source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
safe_clean() {
|
||||
echo "safe_clean $1"
|
||||
}
|
||||
clean_recent_items
|
||||
_clean_recent_items
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "clean_mail_downloads skips cleanup when size below threshold" {
|
||||
@test "_clean_mail_downloads skips cleanup when size below threshold" {
|
||||
mkdir -p "$HOME/Library/Mail Downloads"
|
||||
echo "test" > "$HOME/Library/Mail Downloads/small.txt"
|
||||
|
||||
@@ -151,14 +151,14 @@ EOF
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
clean_mail_downloads
|
||||
_clean_mail_downloads
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$HOME/Library/Mail Downloads/small.txt" ]
|
||||
}
|
||||
|
||||
@test "clean_mail_downloads removes old attachments" {
|
||||
@test "_clean_mail_downloads removes old attachments" {
|
||||
mkdir -p "$HOME/Library/Mail Downloads"
|
||||
touch "$HOME/Library/Mail Downloads/old.pdf"
|
||||
touch -t 202301010000 "$HOME/Library/Mail Downloads/old.pdf"
|
||||
@@ -171,7 +171,7 @@ EOF
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
clean_mail_downloads
|
||||
_clean_mail_downloads
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -38,22 +38,27 @@ EOF
|
||||
[[ "$output" != *"Trash"* ]]
|
||||
}
|
||||
|
||||
@test "clean_macos_system_caches calls safe_clean for core paths" {
|
||||
@test "clean_app_caches includes macOS system caches" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
stop_section_spinner() { :; }
|
||||
start_section_spinner() { :; }
|
||||
safe_clean() { echo "$2"; }
|
||||
clean_macos_system_caches
|
||||
bytes_to_human() { echo "0B"; }
|
||||
note_activity() { :; }
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
clean_app_caches
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Saved application states"* ]]
|
||||
[[ "$output" == *"QuickLook"* ]]
|
||||
[[ "$output" == *"Saved application states"* ]] || [[ "$output" == *"App caches"* ]]
|
||||
}
|
||||
|
||||
@test "clean_sandboxed_app_caches skips protected containers" {
|
||||
@test "clean_app_caches skips protected containers" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" DRY_RUN=true /bin/bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
@@ -69,12 +74,12 @@ files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
mkdir -p "$HOME/Library/Containers/com.example.app/Data/Library/Caches"
|
||||
process_container_cache "$HOME/Library/Containers/com.example.app"
|
||||
clean_sandboxed_app_caches
|
||||
touch "$HOME/Library/Containers/com.example.app/Data/Library/Caches/test.cache"
|
||||
clean_app_caches
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"Sandboxed app caches"* ]]
|
||||
[[ "$output" != *"App caches"* ]] || [[ "$output" == *"already clean"* ]]
|
||||
}
|
||||
|
||||
@test "clean_group_container_caches keeps protected caches and cleans non-protected caches" {
|
||||
|
||||
Reference in New Issue
Block a user