mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 20:54:50 +00:00
Safer cleaning and enhancement capabilities
This commit is contained in:
31
bin/clean.sh
31
bin/clean.sh
@@ -25,11 +25,6 @@ DRY_RUN=false
|
||||
PROTECT_FINDER_METADATA=false
|
||||
IS_M_SERIES=$([[ "$(uname -m)" == "arm64" ]] && echo "true" || echo "false")
|
||||
|
||||
# Constants
|
||||
readonly MAX_PARALLEL_JOBS=15 # Maximum parallel background jobs
|
||||
readonly TEMP_FILE_AGE_DAYS=7 # Age threshold for temp file cleanup
|
||||
readonly ORPHAN_AGE_DAYS=60 # Age threshold for orphaned data
|
||||
|
||||
# Protected Service Worker domains (web-based editing tools)
|
||||
readonly PROTECTED_SW_DOMAINS=(
|
||||
"capcut.com"
|
||||
@@ -64,6 +59,12 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
# Expand tilde to home directory
|
||||
[[ "$line" == ~* ]] && line="${line/#~/$HOME}"
|
||||
|
||||
# Security: reject path traversal attempts
|
||||
if [[ "$line" =~ \.\. ]]; then
|
||||
WHITELIST_WARNINGS+=("Path traversal not allowed: $line")
|
||||
continue
|
||||
fi
|
||||
|
||||
# Path validation with support for spaces and wildcards
|
||||
# Allow: letters, numbers, /, _, ., -, @, spaces, and * anywhere in path
|
||||
if [[ ! "$line" =~ ^[a-zA-Z0-9/_.@\ *-]+$ ]]; then
|
||||
@@ -71,6 +72,12 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Require absolute paths (must start with /)
|
||||
if [[ "$line" != /* ]]; then
|
||||
WHITELIST_WARNINGS+=("Must be absolute path: $line")
|
||||
continue
|
||||
fi
|
||||
|
||||
# Reject paths with consecutive slashes (e.g., //)
|
||||
if [[ "$line" =~ // ]]; then
|
||||
WHITELIST_WARNINGS+=("Consecutive slashes: $line")
|
||||
@@ -79,7 +86,7 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
|
||||
# Prevent critical system directories
|
||||
case "$line" in
|
||||
/System/* | /bin/* | /sbin/* | /usr/bin/* | /usr/sbin/* | /etc/* | /var/db/*)
|
||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /var/db/*)
|
||||
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||
continue
|
||||
;;
|
||||
@@ -322,7 +329,7 @@ safe_clean() {
|
||||
pids+=($!)
|
||||
((idx++))
|
||||
|
||||
if ((${#pids[@]} >= MAX_PARALLEL_JOBS)); then
|
||||
if ((${#pids[@]} >= MOLE_MAX_PARALLEL_JOBS)); then
|
||||
wait "${pids[0]}" 2> /dev/null || true
|
||||
pids=("${pids[@]:1}")
|
||||
((completed++))
|
||||
@@ -351,7 +358,7 @@ safe_clean() {
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2> /dev/null || true
|
||||
else
|
||||
rm -rf "$path" 2> /dev/null || true
|
||||
safe_remove "$path" true || true
|
||||
fi
|
||||
fi
|
||||
((total_size_bytes += size))
|
||||
@@ -380,7 +387,7 @@ safe_clean() {
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2> /dev/null || true
|
||||
else
|
||||
rm -rf "$path" 2> /dev/null || true
|
||||
safe_remove "$path" true || true
|
||||
fi
|
||||
fi
|
||||
((total_size_bytes += size_bytes))
|
||||
@@ -606,9 +613,9 @@ perform_cleanup() {
|
||||
clean_virtualization_tools
|
||||
end_section
|
||||
|
||||
# ===== 11. Application Support logs cleanup =====
|
||||
start_section "Application Support logs"
|
||||
# Application Support logs cleanup (delegated to clean_user_data module)
|
||||
# ===== 11. Application Support logs and caches cleanup =====
|
||||
start_section "Application Support"
|
||||
# Clean logs, Service Worker caches, Code Cache, Crashpad, stale updates, Group Containers
|
||||
clean_application_support_logs
|
||||
end_section
|
||||
|
||||
|
||||
@@ -190,10 +190,10 @@ cleanup_path() {
|
||||
fi
|
||||
|
||||
local removed=false
|
||||
if rm -rf "$expanded_path" 2> /dev/null; then
|
||||
if safe_remove "$expanded_path" true; then
|
||||
removed=true
|
||||
elif request_sudo_access "Removing $label requires admin access"; then
|
||||
if sudo rm -rf "$expanded_path" 2> /dev/null; then
|
||||
if safe_sudo_remove "$expanded_path"; then
|
||||
removed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -522,7 +522,7 @@ uninstall_applications() {
|
||||
done
|
||||
|
||||
# Remove the application
|
||||
if rm -rf "$app_path" 2> /dev/null; then
|
||||
if safe_remove "$app_path" true; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed application"
|
||||
else
|
||||
echo -e " ${RED}${ICON_ERROR}${NC} Failed to remove $app_path"
|
||||
@@ -538,7 +538,7 @@ uninstall_applications() {
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)"
|
||||
fi
|
||||
else
|
||||
if rm -rf "$file" 2> /dev/null; then
|
||||
if safe_remove "$file" true; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)"
|
||||
fi
|
||||
fi
|
||||
@@ -558,7 +558,7 @@ uninstall_applications() {
|
||||
echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file"
|
||||
fi
|
||||
else
|
||||
if sudo rm -rf "$file" 2> /dev/null; then
|
||||
if safe_sudo_remove "$file"; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(basename "$file")"
|
||||
else
|
||||
echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file"
|
||||
|
||||
@@ -90,7 +90,7 @@ clean_service_worker_cache() {
|
||||
# Clean if not protected
|
||||
if [[ "$is_protected" == "false" ]]; then
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
rm -rf "$cache_dir" 2> /dev/null || true
|
||||
safe_remove "$cache_dir" true || true
|
||||
fi
|
||||
cleaned_size=$((cleaned_size + size))
|
||||
fi
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#!/bin/bash
|
||||
# User Data Cleanup Module
|
||||
# Essential user caches, browsers, cloud storage, office apps
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Clean user essentials (caches, logs, trash, crash reports)
|
||||
# Env: DRY_RUN
|
||||
clean_user_essentials() {
|
||||
safe_clean ~/Library/Caches/* "User app cache"
|
||||
safe_clean ~/Library/Logs/* "User app logs"
|
||||
@@ -22,10 +20,13 @@ clean_user_essentials() {
|
||||
nfs | smbfs | afpfs | cifs | webdav) continue ;;
|
||||
esac
|
||||
|
||||
# Verify volume is mounted
|
||||
if mount | grep -q "on $volume "; then
|
||||
# Verify volume is mounted and not a symlink
|
||||
if mount | grep -q "on $volume " && [[ ! -L "$volume/.Trashes" ]]; then
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -exec rm -rf {} \; 2> /dev/null || true
|
||||
# Safely iterate and remove each item
|
||||
while IFS= read -r -d '' item; do
|
||||
safe_remove "$item" true || true
|
||||
done < <(find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -52,7 +53,6 @@ clean_user_essentials() {
|
||||
}
|
||||
|
||||
# Clean Finder metadata (.DS_Store files)
|
||||
# Env: PROTECT_FINDER_METADATA
|
||||
clean_finder_metadata() {
|
||||
if [[ "$PROTECT_FINDER_METADATA" == "true" ]]; then
|
||||
note_activity
|
||||
@@ -170,7 +170,7 @@ clean_virtualization_tools() {
|
||||
safe_clean ~/.vagrant.d/tmp/* "Vagrant temporary files"
|
||||
}
|
||||
|
||||
# Clean Application Support logs
|
||||
# Clean Application Support logs and caches
|
||||
clean_application_support_logs() {
|
||||
# Check permission
|
||||
if [[ ! -d "$HOME/Library/Application Support" ]] || ! ls "$HOME/Library/Application Support" > /dev/null 2>&1; then
|
||||
@@ -179,7 +179,7 @@ clean_application_support_logs() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Clean log directories with iteration limit to prevent hanging
|
||||
# Clean log directories and cache patterns with iteration limit
|
||||
local iteration_count=0
|
||||
local max_iterations=200
|
||||
|
||||
@@ -201,7 +201,7 @@ clean_application_support_logs() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clean common log directories (only if they exist and are accessible)
|
||||
# Clean log directories
|
||||
if [[ -d "$app_dir/log" ]] && ls "$app_dir/log" > /dev/null 2>&1; then
|
||||
safe_clean "$app_dir/log"/* "App logs: $app_name"
|
||||
fi
|
||||
@@ -211,7 +211,44 @@ clean_application_support_logs() {
|
||||
if [[ -d "$app_dir/activitylog" ]] && ls "$app_dir/activitylog" > /dev/null 2>&1; then
|
||||
safe_clean "$app_dir/activitylog"/* "Activity logs: $app_name"
|
||||
fi
|
||||
|
||||
# Clean common cache patterns (Service Worker, Code Cache, Crashpad)
|
||||
if [[ -d "$app_dir/Cache/Cache_Data" ]] && ls "$app_dir/Cache/Cache_Data" > /dev/null 2>&1; then
|
||||
safe_clean "$app_dir/Cache/Cache_Data" "Cache data: $app_name"
|
||||
fi
|
||||
if [[ -d "$app_dir/Code Cache/js" ]] && ls "$app_dir/Code Cache/js" > /dev/null 2>&1; then
|
||||
safe_clean "$app_dir/Code Cache/js"/* "Code cache: $app_name"
|
||||
fi
|
||||
if [[ -d "$app_dir/Crashpad/completed" ]] && ls "$app_dir/Crashpad/completed" > /dev/null 2>&1; then
|
||||
safe_clean "$app_dir/Crashpad/completed"/* "Crash reports: $app_name"
|
||||
fi
|
||||
|
||||
# Clean Service Worker caches (CacheStorage and ScriptCache)
|
||||
while IFS= read -r -d '' sw_cache; do
|
||||
local profile_path=$(dirname "$(dirname "$sw_cache")")
|
||||
local profile_name=$(basename "$profile_path")
|
||||
[[ "$profile_name" == "User Data" ]] && profile_name=$(basename "$(dirname "$profile_path")")
|
||||
clean_service_worker_cache "$app_name ($profile_name)" "$sw_cache"
|
||||
done < <(find "$app_dir" -maxdepth 4 -type d \( -name "CacheStorage" -o -name "ScriptCache" \) -path "*/Service Worker/*" 2> /dev/null || true)
|
||||
|
||||
# Clean stale update downloads (older than 7 days)
|
||||
if [[ -d "$app_dir/update" ]] && ls "$app_dir/update" > /dev/null 2>&1; then
|
||||
while IFS= read -r update_dir; do
|
||||
local dir_age_days=$(( ($(date +%s) - $(get_file_mtime "$update_dir")) / 86400 ))
|
||||
if [[ $dir_age_days -ge $MOLE_TEMP_FILE_AGE_DAYS ]]; then
|
||||
safe_clean "$update_dir" "Stale update: $app_name"
|
||||
fi
|
||||
done < <(find "$app_dir/update" -mindepth 1 -maxdepth 1 -type d 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean Group Containers logs
|
||||
if [[ -d "$HOME/Library/Group Containers" ]]; then
|
||||
while IFS= read -r logs_dir; do
|
||||
local container_name=$(basename "$(dirname "$logs_dir")")
|
||||
safe_clean "$logs_dir"/* "Group container logs: $container_name"
|
||||
done < <(find "$HOME/Library/Group Containers" -maxdepth 2 -type d -name "Logs" 2> /dev/null || true)
|
||||
fi
|
||||
}
|
||||
|
||||
# Check and show iOS device backup info
|
||||
|
||||
209
lib/common.sh
209
lib/common.sh
@@ -20,30 +20,39 @@ readonly RED="${ESC}[0;31m"
|
||||
readonly GRAY="${ESC}[0;90m"
|
||||
readonly NC="${ESC}[0m"
|
||||
|
||||
# Icon definitions (shared across modules)
|
||||
readonly ICON_CONFIRM="◎" # Confirm operation / spinner text
|
||||
readonly ICON_ADMIN="⚙" # Gear indicator for admin/settings/system info
|
||||
readonly ICON_SUCCESS="✓" # Success mark
|
||||
readonly ICON_ERROR="☻" # Error / warning mark
|
||||
readonly ICON_EMPTY="○" # Hollow circle (empty state / unchecked)
|
||||
readonly ICON_SOLID="●" # Solid circle (selected / system marker)
|
||||
readonly ICON_LIST="•" # Basic list bullet
|
||||
readonly ICON_ARROW="➤" # Pointer / prompt indicator
|
||||
readonly ICON_WARNING="☻" # Warning marker (shares glyph with error)
|
||||
readonly ICON_NAV_UP="↑" # Navigation up
|
||||
readonly ICON_NAV_DOWN="↓" # Navigation down
|
||||
readonly ICON_NAV_LEFT="←" # Navigation left
|
||||
readonly ICON_NAV_RIGHT="→" # Navigation right
|
||||
# Icon definitions
|
||||
readonly ICON_CONFIRM="◎"
|
||||
readonly ICON_ADMIN="⚙"
|
||||
readonly ICON_SUCCESS="✓"
|
||||
readonly ICON_ERROR="☻"
|
||||
readonly ICON_EMPTY="○"
|
||||
readonly ICON_SOLID="●"
|
||||
readonly ICON_LIST="•"
|
||||
readonly ICON_ARROW="➤"
|
||||
readonly ICON_WARNING="☻"
|
||||
readonly ICON_NAV_UP="↑"
|
||||
readonly ICON_NAV_DOWN="↓"
|
||||
readonly ICON_NAV_LEFT="←"
|
||||
readonly ICON_NAV_RIGHT="→"
|
||||
|
||||
# Get spinner characters (ASCII by default, overridable via MO_SPINNER_CHARS env)
|
||||
# Global configuration constants
|
||||
readonly MOLE_TEMP_FILE_AGE_DAYS=7 # Temp file cleanup threshold
|
||||
readonly MOLE_ORPHAN_AGE_DAYS=60 # Orphaned data threshold
|
||||
readonly MOLE_MAX_PARALLEL_JOBS=15 # Parallel job limit
|
||||
readonly MOLE_MAIL_DOWNLOADS_MIN_KB=5120 # Mail attachments size threshold (~5MB)
|
||||
readonly MOLE_LOG_AGE_DAYS=30 # System log retention
|
||||
readonly MOLE_CRASH_REPORT_AGE_DAYS=30 # Crash report retention
|
||||
readonly MOLE_SAVED_STATE_AGE_DAYS=7 # App saved state retention
|
||||
readonly MOLE_TM_BACKUP_SAFE_HOURS=48 # Time Machine failed backup safety window
|
||||
|
||||
# Get spinner characters (overridable via MO_SPINNER_CHARS)
|
||||
mo_spinner_chars() {
|
||||
local chars="${MO_SPINNER_CHARS:-|/-\\}"
|
||||
[[ -z "$chars" ]] && chars='|/-\\'
|
||||
printf "%s" "$chars"
|
||||
}
|
||||
|
||||
# BSD stat compatibility (for users with GNU CoreUtils installed)
|
||||
# Always use system BSD stat instead of potentially overridden GNU version
|
||||
# BSD stat compatibility
|
||||
readonly STAT_BSD="/usr/bin/stat"
|
||||
|
||||
# Get file size in bytes using BSD stat
|
||||
@@ -66,20 +75,7 @@ get_file_owner() {
|
||||
|
||||
# Security and Path Validation Functions
|
||||
|
||||
# Validates a path for safe deletion
|
||||
#
|
||||
# Security checks:
|
||||
# - Rejects empty paths
|
||||
# - Requires absolute paths (must start with /)
|
||||
# - Blocks control characters and newlines
|
||||
# - Protects critical system directories
|
||||
#
|
||||
# Args:
|
||||
# $1 - Path to validate
|
||||
#
|
||||
# Returns:
|
||||
# 0 if path is safe to delete
|
||||
# 1 if path fails any validation check
|
||||
# Validates path for deletion (absolute, no control chars, not system dir)
|
||||
validate_path_for_deletion() {
|
||||
local path="$1"
|
||||
|
||||
@@ -113,21 +109,8 @@ validate_path_for_deletion() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Safe wrapper around rm -rf with validation and logging
|
||||
#
|
||||
# Provides a secure alternative to direct rm -rf calls with:
|
||||
# - Path validation (absolute paths, no control characters)
|
||||
# - System directory protection
|
||||
# - Logging of all operations
|
||||
# - Silent mode for non-critical failures
|
||||
#
|
||||
# Usage:
|
||||
# safe_remove "/path/to/file" # Normal mode with logging
|
||||
# safe_remove "/path/to/file" true # Silent mode
|
||||
#
|
||||
# Returns:
|
||||
# 0 on success or if path doesn't exist
|
||||
# 1 on validation failure or deletion error
|
||||
# Safe wrapper around rm -rf with path validation and logging
|
||||
# Usage: safe_remove "/path" [silent]
|
||||
safe_remove() {
|
||||
local path="$1"
|
||||
local silent="${2:-false}"
|
||||
@@ -139,26 +122,121 @@ safe_remove() {
|
||||
|
||||
# Check if path exists
|
||||
if [[ ! -e "$path" ]]; then
|
||||
[[ "$silent" != "true" ]] && log_warning "Path does not exist, skipping: $path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Log what we're about to delete
|
||||
if [[ -d "$path" ]]; then
|
||||
log_info "Removing directory: $path"
|
||||
else
|
||||
log_info "Removing file: $path"
|
||||
fi
|
||||
|
||||
# Perform the deletion
|
||||
# Perform the deletion (log only on error)
|
||||
if rm -rf "$path" 2> /dev/null; then
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to remove: $path"
|
||||
[[ "$silent" != "true" ]] && log_error "Failed to remove: $path"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Safe sudo remove with validation (rejects symlinks)
|
||||
# Usage: safe_sudo_remove "/path"
|
||||
safe_sudo_remove() {
|
||||
local path="$1"
|
||||
|
||||
# Validate path
|
||||
if ! validate_path_for_deletion "$path"; then
|
||||
log_error "Path validation failed for sudo remove: $path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if path exists
|
||||
if [[ ! -e "$path" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Additional check: reject symlinks for sudo operations
|
||||
if [[ -L "$path" ]]; then
|
||||
log_error "Refusing to sudo remove symlink: $path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Perform the deletion (log only on error)
|
||||
if sudo rm -rf "$path" 2> /dev/null; then
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to remove (sudo): $path"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Safe find delete with depth limit and validation
|
||||
# Usage: safe_find_delete "/dir" "pattern" age_days "f|d"
|
||||
safe_find_delete() {
|
||||
local base_dir="$1"
|
||||
local pattern="$2"
|
||||
local age_days="${3:-7}"
|
||||
local type_filter="${4:-f}"
|
||||
|
||||
# Validate base directory exists and is not a symlink
|
||||
if [[ ! -d "$base_dir" ]]; then
|
||||
log_warning "Base directory does not exist: $base_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -L "$base_dir" ]]; then
|
||||
log_error "Refusing to search symlinked directory: $base_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate type filter
|
||||
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
||||
log_error "Invalid type filter: $type_filter (must be 'f' or 'd')"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Execute find with safety limits
|
||||
find "$base_dir" \
|
||||
-maxdepth 3 \
|
||||
-name "$pattern" \
|
||||
-type "$type_filter" \
|
||||
-mtime "+$age_days" \
|
||||
-delete 2> /dev/null || true
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Safe sudo find delete (same as safe_find_delete with sudo)
|
||||
# Usage: safe_sudo_find_delete "/dir" "pattern" age_days "f|d"
|
||||
safe_sudo_find_delete() {
|
||||
local base_dir="$1"
|
||||
local pattern="$2"
|
||||
local age_days="${3:-7}"
|
||||
local type_filter="${4:-f}"
|
||||
|
||||
# Validate base directory exists and is not a symlink
|
||||
if [[ ! -d "$base_dir" ]]; then
|
||||
log_warning "Base directory does not exist: $base_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -L "$base_dir" ]]; then
|
||||
log_error "Refusing to search symlinked directory: $base_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate type filter
|
||||
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
||||
log_error "Invalid type filter: $type_filter (must be 'f' or 'd')"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Execute find with safety limits
|
||||
sudo find "$base_dir" \
|
||||
-maxdepth 3 \
|
||||
-name "$pattern" \
|
||||
-type "$type_filter" \
|
||||
-mtime "+$age_days" \
|
||||
-delete 2> /dev/null || true
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Logging configuration
|
||||
readonly LOG_FILE="${HOME}/.config/mole/mole.log"
|
||||
readonly LOG_MAX_SIZE_DEFAULT=1048576 # 1MB
|
||||
@@ -200,6 +278,22 @@ log_error() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$LOG_FILE" 2> /dev/null || true
|
||||
}
|
||||
|
||||
# Run command with optional error handling
|
||||
# Usage: run_silent command args... # Ignore errors
|
||||
# run_logged command args... # Log errors but continue
|
||||
run_silent() {
|
||||
"$@" > /dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
run_logged() {
|
||||
local cmd="$1"
|
||||
if ! "$@" 2>&1 | tee -a "$LOG_FILE" > /dev/null; then
|
||||
log_warning "Command failed: $cmd"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Call rotation check once when common.sh is sourced
|
||||
rotate_log_once
|
||||
|
||||
@@ -261,8 +355,7 @@ show_cursor() {
|
||||
}
|
||||
|
||||
# Read single keypress and return normalized key name
|
||||
# Returns: ENTER, SPACE, UP, DOWN, LEFT, RIGHT, QUIT, DELETE, CHAR:<c>, etc.
|
||||
# Env: MOLE_READ_KEY_FORCE_CHAR=1 for filter mode
|
||||
# Returns: ENTER, SPACE, UP, DOWN, LEFT, RIGHT, QUIT, DELETE, CHAR:<c>
|
||||
read_key() {
|
||||
local key rest read_status
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Optimization Tasks
|
||||
# Individual optimization operations extracted from execute_optimization
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly MAIL_DOWNLOADS_MIN_KB=5120 # ~5MB threshold
|
||||
|
||||
_opt_get_dir_size_kb() {
|
||||
local path="$1"
|
||||
[[ -e "$path" ]] || {
|
||||
@@ -145,8 +142,8 @@ opt_log_cleanup() {
|
||||
done
|
||||
|
||||
if [[ -d "/Library/Logs/DiagnosticReports" ]]; then
|
||||
sudo find /Library/Logs/DiagnosticReports -type f -name "*.crash" -delete 2> /dev/null || true
|
||||
sudo find /Library/Logs/DiagnosticReports -type f -name "*.panic" -delete 2> /dev/null || true
|
||||
safe_sudo_find_delete "/Library/Logs/DiagnosticReports" "*.crash" 0 "f"
|
||||
safe_sudo_find_delete "/Library/Logs/DiagnosticReports" "*.panic" 0 "f"
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} System diagnostic logs cleared"
|
||||
else
|
||||
echo -e "${GRAY}-${NC} No system diagnostic logs found"
|
||||
@@ -158,7 +155,7 @@ opt_recent_items() {
|
||||
echo -e "${BLUE}${ICON_ARROW}${NC} Clearing recent items lists..."
|
||||
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||
if [[ -d "$shared_dir" ]]; then
|
||||
find "$shared_dir" -name "*.sfl2" -type f -delete 2> /dev/null || true
|
||||
safe_find_delete "$shared_dir" "*.sfl2" 0 "f"
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Shared file lists cleared"
|
||||
fi
|
||||
|
||||
@@ -211,16 +208,20 @@ opt_mail_downloads() {
|
||||
total_kb=$((total_kb + $(_opt_get_dir_size_kb "$target_path")))
|
||||
done
|
||||
|
||||
if [[ $total_kb -lt $MAIL_DOWNLOADS_MIN_KB ]]; then
|
||||
if [[ $total_kb -lt $MOLE_MAIL_DOWNLOADS_MIN_KB ]]; then
|
||||
echo -e "${GRAY}-${NC} Only $(bytes_to_human $((total_kb * 1024))) detected, skipping cleanup"
|
||||
return
|
||||
fi
|
||||
|
||||
# Only delete files older than 30 days (safer)
|
||||
# Only delete old attachments (safety window)
|
||||
local deleted=0
|
||||
for target_path in "${mail_dirs[@]}"; do
|
||||
if [[ -d "$target_path" ]]; then
|
||||
deleted=$((deleted + $(find "$target_path" -type f -mtime +30 -delete -print 2> /dev/null | wc -l | tr -d ' ')))
|
||||
local file_count=$(find "$target_path" -type f -mtime "+$MOLE_LOG_AGE_DAYS" 2> /dev/null | wc -l | tr -d ' ')
|
||||
if [[ "$file_count" -gt 0 ]]; then
|
||||
safe_find_delete "$target_path" "*" "$MOLE_LOG_AGE_DAYS" "f"
|
||||
deleted=$((deleted + file_count))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -233,7 +234,7 @@ opt_mail_downloads() {
|
||||
|
||||
# Saved state: remove OLD app saved states (7+ days)
|
||||
opt_saved_state_cleanup() {
|
||||
echo -e "${BLUE}${ICON_ARROW}${NC} Removing old saved application states (7+ days)..."
|
||||
echo -e "${BLUE}${ICON_ARROW}${NC} Removing old saved application states (${MOLE_SAVED_STATE_AGE_DAYS}+ days)..."
|
||||
local state_dir="$HOME/Library/Saved Application State"
|
||||
|
||||
if [[ ! -d "$state_dir" ]]; then
|
||||
@@ -241,9 +242,13 @@ opt_saved_state_cleanup() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Only delete states older than 7 days (safer - won't lose recent work)
|
||||
# Only delete old saved states (safety window)
|
||||
local deleted=0
|
||||
deleted=$(find "$state_dir" -type d -name "*.savedState" -mtime +7 -exec rm -rf {} \; -print 2> /dev/null | wc -l | tr -d ' ')
|
||||
while IFS= read -r -d '' state_path; do
|
||||
if safe_remove "$state_path" true; then
|
||||
((deleted++))
|
||||
fi
|
||||
done < <(find "$state_dir" -type d -name "*.savedState" -mtime "+$MOLE_SAVED_STATE_AGE_DAYS" -print0 2> /dev/null)
|
||||
|
||||
if [[ $deleted -gt 0 ]]; then
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Removed $deleted old saved state(s)"
|
||||
|
||||
@@ -199,16 +199,16 @@ batch_uninstall_applications() {
|
||||
fi
|
||||
if [[ -z "$reason" ]]; then
|
||||
if [[ "$needs_sudo" == true ]]; then
|
||||
sudo rm -rf "$app_path" 2> /dev/null || reason="remove failed"
|
||||
safe_sudo_remove "$app_path" || reason="remove failed"
|
||||
else
|
||||
rm -rf "$app_path" 2> /dev/null || reason="remove failed"
|
||||
safe_remove "$app_path" true || reason="remove failed"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$reason" ]]; then
|
||||
local files_removed=0
|
||||
while IFS= read -r file; do
|
||||
[[ -n "$file" && -e "$file" ]] || continue
|
||||
rm -rf "$file" 2> /dev/null && ((files_removed++)) || true
|
||||
safe_remove "$file" true && ((files_removed++)) || true
|
||||
done <<< "$related_files"
|
||||
((total_size_freed += total_kb))
|
||||
((success_count++))
|
||||
|
||||
Reference in New Issue
Block a user