mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 22:30:08 +00:00
feat(clean, optimize): enhance recent items cleanup safety
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Date:** December 18, 2025
|
**Date:** December 18, 2025
|
||||||
|
|
||||||
**Audited Version:** Current `main` branch (V1.13.9)
|
**Audited Version:** Current `main` branch (V1.13.10)
|
||||||
|
|
||||||
**Status:** Passed
|
**Status:** Passed
|
||||||
|
|
||||||
|
|||||||
23
bin/clean.sh
23
bin/clean.sh
@@ -4,11 +4,9 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Fix locale issues (avoid Perl warnings on non-English systems)
|
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
export LANG=C
|
export LANG=C
|
||||||
|
|
||||||
# Get script directory and source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/../lib/core/common.sh"
|
source "$SCRIPT_DIR/../lib/core/common.sh"
|
||||||
source "$SCRIPT_DIR/../lib/core/sudo.sh"
|
source "$SCRIPT_DIR/../lib/core/sudo.sh"
|
||||||
@@ -20,72 +18,52 @@ source "$SCRIPT_DIR/../lib/clean/app_caches.sh"
|
|||||||
source "$SCRIPT_DIR/../lib/clean/system.sh"
|
source "$SCRIPT_DIR/../lib/clean/system.sh"
|
||||||
source "$SCRIPT_DIR/../lib/clean/user.sh"
|
source "$SCRIPT_DIR/../lib/clean/user.sh"
|
||||||
|
|
||||||
# Configuration
|
|
||||||
SYSTEM_CLEAN=false
|
SYSTEM_CLEAN=false
|
||||||
DRY_RUN=false
|
DRY_RUN=false
|
||||||
PROTECT_FINDER_METADATA=false
|
PROTECT_FINDER_METADATA=false
|
||||||
IS_M_SERIES=$([[ "$(uname -m)" == "arm64" ]] && echo "true" || echo "false")
|
IS_M_SERIES=$([[ "$(uname -m)" == "arm64" ]] && echo "true" || echo "false")
|
||||||
|
|
||||||
# Export list configuration
|
|
||||||
EXPORT_LIST_FILE="$HOME/.config/mole/clean-list.txt"
|
EXPORT_LIST_FILE="$HOME/.config/mole/clean-list.txt"
|
||||||
CURRENT_SECTION=""
|
CURRENT_SECTION=""
|
||||||
|
|
||||||
# Protected Service Worker domains (web-based editing tools)
|
|
||||||
readonly PROTECTED_SW_DOMAINS=(
|
readonly PROTECTED_SW_DOMAINS=(
|
||||||
"capcut.com"
|
"capcut.com"
|
||||||
"photopea.com"
|
"photopea.com"
|
||||||
"pixlr.com"
|
"pixlr.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Whitelist patterns (loaded from common.sh)
|
|
||||||
# FINDER_METADATA_SENTINEL and DEFAULT_WHITELIST_PATTERNS defined in lib/core/common.sh
|
|
||||||
declare -a WHITELIST_PATTERNS=()
|
declare -a WHITELIST_PATTERNS=()
|
||||||
WHITELIST_WARNINGS=()
|
WHITELIST_WARNINGS=()
|
||||||
|
|
||||||
# Load user-defined whitelist
|
|
||||||
if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
# Trim whitespace
|
|
||||||
# shellcheck disable=SC2295
|
# shellcheck disable=SC2295
|
||||||
line="${line#"${line%%[![:space:]]*}"}"
|
line="${line#"${line%%[![:space:]]*}"}"
|
||||||
# shellcheck disable=SC2295
|
# shellcheck disable=SC2295
|
||||||
line="${line%"${line##*[![:space:]]}"}"
|
line="${line%"${line##*[![:space:]]}"}"
|
||||||
|
|
||||||
# Skip empty lines and comments
|
|
||||||
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||||
|
|
||||||
# Expand tilde to home directory
|
|
||||||
[[ "$line" == ~* ]] && line="${line/#~/$HOME}"
|
[[ "$line" == ~* ]] && line="${line/#~/$HOME}"
|
||||||
|
|
||||||
# Security: reject path traversal attempts
|
|
||||||
if [[ "$line" =~ \.\. ]]; then
|
if [[ "$line" =~ \.\. ]]; then
|
||||||
WHITELIST_WARNINGS+=("Path traversal not allowed: $line")
|
WHITELIST_WARNINGS+=("Path traversal not allowed: $line")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Skip validation for special sentinel values
|
|
||||||
if [[ "$line" != "$FINDER_METADATA_SENTINEL" ]]; then
|
if [[ "$line" != "$FINDER_METADATA_SENTINEL" ]]; then
|
||||||
# Path validation with support for spaces and wildcards
|
|
||||||
# Allow: letters, numbers, /, _, ., -, @, spaces, and * anywhere in path
|
|
||||||
if [[ ! "$line" =~ ^[a-zA-Z0-9/_.@\ *-]+$ ]]; then
|
if [[ ! "$line" =~ ^[a-zA-Z0-9/_.@\ *-]+$ ]]; then
|
||||||
WHITELIST_WARNINGS+=("Invalid path format: $line")
|
WHITELIST_WARNINGS+=("Invalid path format: $line")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Require absolute paths (must start with /)
|
|
||||||
if [[ "$line" != /* ]]; then
|
if [[ "$line" != /* ]]; then
|
||||||
WHITELIST_WARNINGS+=("Must be absolute path: $line")
|
WHITELIST_WARNINGS+=("Must be absolute path: $line")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Reject paths with consecutive slashes (e.g., //)
|
|
||||||
if [[ "$line" =~ // ]]; then
|
if [[ "$line" =~ // ]]; then
|
||||||
WHITELIST_WARNINGS+=("Consecutive slashes: $line")
|
WHITELIST_WARNINGS+=("Consecutive slashes: $line")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prevent critical system directories
|
|
||||||
case "$line" in
|
case "$line" in
|
||||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /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")
|
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||||
@@ -776,7 +754,6 @@ perform_cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
# Parse args
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
"--debug")
|
"--debug")
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ set -euo pipefail
|
|||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
export LANG=C
|
export LANG=C
|
||||||
|
|
||||||
# Load common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/core/common.sh"
|
source "$SCRIPT_DIR/lib/core/common.sh"
|
||||||
source "$SCRIPT_DIR/lib/core/sudo.sh"
|
source "$SCRIPT_DIR/lib/core/sudo.sh"
|
||||||
@@ -15,36 +14,28 @@ source "$SCRIPT_DIR/lib/manage/autofix.sh"
|
|||||||
source "$SCRIPT_DIR/lib/optimize/maintenance.sh"
|
source "$SCRIPT_DIR/lib/optimize/maintenance.sh"
|
||||||
source "$SCRIPT_DIR/lib/optimize/tasks.sh"
|
source "$SCRIPT_DIR/lib/optimize/tasks.sh"
|
||||||
source "$SCRIPT_DIR/lib/check/health_json.sh"
|
source "$SCRIPT_DIR/lib/check/health_json.sh"
|
||||||
|
|
||||||
# Load check modules
|
|
||||||
source "$SCRIPT_DIR/lib/check/all.sh"
|
source "$SCRIPT_DIR/lib/check/all.sh"
|
||||||
source "$SCRIPT_DIR/lib/manage/whitelist.sh"
|
source "$SCRIPT_DIR/lib/manage/whitelist.sh"
|
||||||
|
|
||||||
# Colors and icons from common.sh
|
|
||||||
|
|
||||||
print_header() {
|
print_header() {
|
||||||
printf '\n'
|
printf '\n'
|
||||||
echo -e "${PURPLE_BOLD}Optimize and Check${NC}"
|
echo -e "${PURPLE_BOLD}Optimize and Check${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# System check functions (real-time display)
|
|
||||||
run_system_checks() {
|
run_system_checks() {
|
||||||
unset AUTO_FIX_SUMMARY AUTO_FIX_DETAILS
|
unset AUTO_FIX_SUMMARY AUTO_FIX_DETAILS
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${PURPLE_BOLD}System Check${NC}"
|
echo -e "${PURPLE_BOLD}System Check${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check updates - real-time display
|
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} System updates"
|
echo -e "${BLUE}${ICON_ARROW}${NC} System updates"
|
||||||
check_all_updates
|
check_all_updates
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check health - real-time display
|
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} System health"
|
echo -e "${BLUE}${ICON_ARROW}${NC} System health"
|
||||||
check_system_health
|
check_system_health
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check security - real-time display
|
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} Security posture"
|
echo -e "${BLUE}${ICON_ARROW}${NC} Security posture"
|
||||||
check_all_security
|
check_all_security
|
||||||
if ask_for_security_fixes; then
|
if ask_for_security_fixes; then
|
||||||
@@ -52,21 +43,16 @@ run_system_checks() {
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check configuration - real-time display
|
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} Configuration"
|
echo -e "${BLUE}${ICON_ARROW}${NC} Configuration"
|
||||||
check_all_config
|
check_all_config
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Show suggestions
|
|
||||||
show_suggestions
|
show_suggestions
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Ask about updates first
|
|
||||||
if ask_for_updates; then
|
if ask_for_updates; then
|
||||||
perform_updates
|
perform_updates
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ask about auto-fix
|
|
||||||
if ask_for_auto_fix; then
|
if ask_for_auto_fix; then
|
||||||
perform_auto_fix
|
perform_auto_fix
|
||||||
fi
|
fi
|
||||||
@@ -81,7 +67,6 @@ show_optimization_summary() {
|
|||||||
local summary_title="Optimization and Check Complete"
|
local summary_title="Optimization and Check Complete"
|
||||||
local -a summary_details=()
|
local -a summary_details=()
|
||||||
|
|
||||||
# Optimization results
|
|
||||||
summary_details+=("Applied ${GREEN}${safe_count:-0}${NC} optimizations; all system services tuned")
|
summary_details+=("Applied ${GREEN}${safe_count:-0}${NC} optimizations; all system services tuned")
|
||||||
summary_details+=("Updates, security and system health fully reviewed")
|
summary_details+=("Updates, security and system health fully reviewed")
|
||||||
|
|
||||||
@@ -98,14 +83,12 @@ show_optimization_summary() {
|
|||||||
fi
|
fi
|
||||||
summary_details+=("$summary_line4")
|
summary_details+=("$summary_line4")
|
||||||
|
|
||||||
# Ensure summary is always printed for optimizations
|
|
||||||
print_summary_block "$summary_title" "${summary_details[@]}"
|
print_summary_block "$summary_title" "${summary_details[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
show_system_health() {
|
show_system_health() {
|
||||||
local health_json="$1"
|
local health_json="$1"
|
||||||
|
|
||||||
# Parse system health using jq with fallback to 0
|
|
||||||
local mem_used=$(echo "$health_json" | jq -r '.memory_used_gb // 0' 2> /dev/null || echo "0")
|
local mem_used=$(echo "$health_json" | jq -r '.memory_used_gb // 0' 2> /dev/null || echo "0")
|
||||||
local mem_total=$(echo "$health_json" | jq -r '.memory_total_gb // 0' 2> /dev/null || echo "0")
|
local mem_total=$(echo "$health_json" | jq -r '.memory_total_gb // 0' 2> /dev/null || echo "0")
|
||||||
local disk_used=$(echo "$health_json" | jq -r '.disk_used_gb // 0' 2> /dev/null || echo "0")
|
local disk_used=$(echo "$health_json" | jq -r '.disk_used_gb // 0' 2> /dev/null || echo "0")
|
||||||
@@ -113,7 +96,6 @@ show_system_health() {
|
|||||||
local disk_percent=$(echo "$health_json" | jq -r '.disk_used_percent // 0' 2> /dev/null || echo "0")
|
local disk_percent=$(echo "$health_json" | jq -r '.disk_used_percent // 0' 2> /dev/null || echo "0")
|
||||||
local uptime=$(echo "$health_json" | jq -r '.uptime_days // 0' 2> /dev/null || echo "0")
|
local uptime=$(echo "$health_json" | jq -r '.uptime_days // 0' 2> /dev/null || echo "0")
|
||||||
|
|
||||||
# Ensure all values are numeric (fallback to 0)
|
|
||||||
mem_used=${mem_used:-0}
|
mem_used=${mem_used:-0}
|
||||||
mem_total=${mem_total:-0}
|
mem_total=${mem_total:-0}
|
||||||
disk_used=${disk_used:-0}
|
disk_used=${disk_used:-0}
|
||||||
@@ -121,15 +103,12 @@ show_system_health() {
|
|||||||
disk_percent=${disk_percent:-0}
|
disk_percent=${disk_percent:-0}
|
||||||
uptime=${uptime:-0}
|
uptime=${uptime:-0}
|
||||||
|
|
||||||
# Compact one-line format with icon
|
|
||||||
printf "${ICON_ADMIN} System %.0f/%.0f GB RAM | %.0f/%.0f GB Disk | Uptime %.0fd\n" \
|
printf "${ICON_ADMIN} System %.0f/%.0f GB RAM | %.0f/%.0f GB Disk | Uptime %.0fd\n" \
|
||||||
"$mem_used" "$mem_total" "$disk_used" "$disk_total" "$uptime"
|
"$mem_used" "$mem_total" "$disk_used" "$disk_total" "$uptime"
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_optimizations() {
|
parse_optimizations() {
|
||||||
local health_json="$1"
|
local health_json="$1"
|
||||||
|
|
||||||
# Extract optimizations array
|
|
||||||
echo "$health_json" | jq -c '.optimizations[]' 2> /dev/null
|
echo "$health_json" | jq -c '.optimizations[]' 2> /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,19 +142,14 @@ touchid_configured() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
touchid_supported() {
|
touchid_supported() {
|
||||||
# bioutil is the most reliable way to check for Touch ID hardware/software support
|
|
||||||
if command -v bioutil > /dev/null 2>&1; then
|
if command -v bioutil > /dev/null 2>&1; then
|
||||||
# Check if Touch ID is functional and available for any user
|
|
||||||
if bioutil -r 2> /dev/null | grep -qi "Touch ID"; then
|
if bioutil -r 2> /dev/null | grep -qi "Touch ID"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fallback: check for Apple Silicon which almost always has Touch ID support
|
# Fallback: Apple Silicon Macs usually have Touch ID
|
||||||
# (except for Mac mini/Studio without a Magic Keyboard with Touch ID)
|
|
||||||
if [[ "$(uname -m)" == "arm64" ]]; then
|
if [[ "$(uname -m)" == "arm64" ]]; then
|
||||||
# On Apple Silicon, we can check for the presence of the Touch Bar or Touch ID sensor
|
|
||||||
# but bioutil is generally sufficient. If bioutil failed, we treat arm64 as likely supported.
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
@@ -190,8 +164,6 @@ cleanup_path() {
|
|||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Centralized protection check
|
|
||||||
if should_protect_path "$expanded_path"; then
|
if should_protect_path "$expanded_path"; then
|
||||||
echo -e "${YELLOW}${ICON_WARNING}${NC} Protected $label"
|
echo -e "${YELLOW}${ICON_WARNING}${NC} Protected $label"
|
||||||
return
|
return
|
||||||
@@ -362,8 +334,7 @@ cleanup_all() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local health_json # Declare health_json at the top of main scope
|
local health_json
|
||||||
# Parse args
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
"--debug")
|
"--debug")
|
||||||
@@ -376,15 +347,12 @@ main() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Register unified cleanup handler
|
|
||||||
trap cleanup_all EXIT INT TERM
|
trap cleanup_all EXIT INT TERM
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
clear
|
clear
|
||||||
fi
|
fi
|
||||||
print_header # Outputs "Optimize and Check"
|
print_header
|
||||||
|
|
||||||
# Check dependencies
|
|
||||||
if ! command -v jq > /dev/null 2>&1; then
|
if ! command -v jq > /dev/null 2>&1; then
|
||||||
echo -e "${RED}${ICON_ERROR}${NC} Missing dependency: jq"
|
echo -e "${RED}${ICON_ERROR}${NC} Missing dependency: jq"
|
||||||
echo -e "${GRAY}Install with: ${GREEN}brew install jq${NC}"
|
echo -e "${GRAY}Install with: ${GREEN}brew install jq${NC}"
|
||||||
@@ -397,7 +365,6 @@ main() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect system health data (doesn't require sudo)
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
start_inline_spinner "Collecting system info..."
|
start_inline_spinner "Collecting system info..."
|
||||||
fi
|
fi
|
||||||
@@ -411,7 +378,6 @@ main() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate JSON before proceeding
|
|
||||||
if ! echo "$health_json" | jq empty 2> /dev/null; then
|
if ! echo "$health_json" | jq empty 2> /dev/null; then
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
stop_inline_spinner
|
stop_inline_spinner
|
||||||
@@ -426,13 +392,9 @@ main() {
|
|||||||
stop_inline_spinner
|
stop_inline_spinner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show system health
|
show_system_health "$health_json"
|
||||||
show_system_health "$health_json" # Outputs "⚙ System ..."
|
|
||||||
|
|
||||||
# Load whitelist patterns for checks
|
|
||||||
load_whitelist "optimize"
|
load_whitelist "optimize"
|
||||||
|
|
||||||
# Display active whitelist patterns
|
|
||||||
if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -gt 0 ]]; then
|
if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -gt 0 ]]; then
|
||||||
local count=${#CURRENT_WHITELIST_PATTERNS[@]}
|
local count=${#CURRENT_WHITELIST_PATTERNS[@]}
|
||||||
if [[ $count -le 3 ]]; then
|
if [[ $count -le 3 ]]; then
|
||||||
@@ -445,9 +407,8 @@ main() {
|
|||||||
echo -e "${ICON_ADMIN} Active Whitelist: ${GRAY}${count} items${NC}"
|
echo -e "${ICON_ADMIN} Active Whitelist: ${GRAY}${count} items${NC}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "" # Empty line before sudo prompt
|
echo ""
|
||||||
|
|
||||||
# Simple confirmation
|
|
||||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} Optimization needs sudo — ${GREEN}Enter${NC} continue, ${GRAY}ESC${NC} cancel: "
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} Optimization needs sudo — ${GREEN}Enter${NC} continue, ${GRAY}ESC${NC} cancel: "
|
||||||
|
|
||||||
local key
|
local key
|
||||||
@@ -465,11 +426,8 @@ main() {
|
|||||||
stop_inline_spinner
|
stop_inline_spinner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Parse and display optimizations
|
|
||||||
local -a safe_items=()
|
local -a safe_items=()
|
||||||
local -a confirm_items=()
|
local -a confirm_items=()
|
||||||
|
|
||||||
# Use temp file instead of process substitution to avoid hanging
|
|
||||||
local opts_file
|
local opts_file
|
||||||
opts_file=$(mktemp_file)
|
opts_file=$(mktemp_file)
|
||||||
parse_optimizations "$health_json" > "$opts_file"
|
parse_optimizations "$health_json" > "$opts_file"
|
||||||
@@ -492,12 +450,9 @@ main() {
|
|||||||
fi
|
fi
|
||||||
done < "$opts_file"
|
done < "$opts_file"
|
||||||
|
|
||||||
# Execute all optimizations
|
|
||||||
local first_heading=true
|
local first_heading=true
|
||||||
|
|
||||||
ensure_sudo_session "System optimization requires admin access" || true
|
ensure_sudo_session "System optimization requires admin access" || true
|
||||||
|
|
||||||
# Run safe optimizations
|
|
||||||
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
||||||
for item in "${safe_items[@]}"; do
|
for item in "${safe_items[@]}"; do
|
||||||
IFS='|' read -r name desc action path <<< "$item"
|
IFS='|' read -r name desc action path <<< "$item"
|
||||||
@@ -506,7 +461,6 @@ main() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run confirm items
|
|
||||||
if [[ ${#confirm_items[@]} -gt 0 ]]; then
|
if [[ ${#confirm_items[@]} -gt 0 ]]; then
|
||||||
for item in "${confirm_items[@]}"; do
|
for item in "${confirm_items[@]}"; do
|
||||||
IFS='|' read -r name desc action path <<< "$item"
|
IFS='|' read -r name desc action path <<< "$item"
|
||||||
@@ -515,17 +469,14 @@ main() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prepare optimization summary data (to show at the end)
|
|
||||||
local safe_count=${#safe_items[@]}
|
local safe_count=${#safe_items[@]}
|
||||||
local confirm_count=${#confirm_items[@]}
|
local confirm_count=${#confirm_items[@]}
|
||||||
|
|
||||||
# Run system checks first
|
|
||||||
run_system_checks
|
run_system_checks
|
||||||
|
|
||||||
export OPTIMIZE_SAFE_COUNT=$safe_count
|
export OPTIMIZE_SAFE_COUNT=$safe_count
|
||||||
export OPTIMIZE_CONFIRM_COUNT=$confirm_count
|
export OPTIMIZE_CONFIRM_COUNT=$confirm_count
|
||||||
|
|
||||||
# Show optimization summary at the end
|
|
||||||
show_optimization_summary
|
show_optimization_summary
|
||||||
|
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ scan_applications() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load applications into arrays
|
|
||||||
load_applications() {
|
load_applications() {
|
||||||
local apps_file="$1"
|
local apps_file="$1"
|
||||||
|
|
||||||
@@ -403,9 +402,7 @@ cleanup() {
|
|||||||
# Set trap for cleanup on exit
|
# Set trap for cleanup on exit
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
# Main function
|
|
||||||
main() {
|
main() {
|
||||||
# Parse args
|
|
||||||
local force_rescan=false
|
local force_rescan=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ scan_applications() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load applications into arrays
|
|
||||||
load_applications() {
|
load_applications() {
|
||||||
local apps_file="$1"
|
local apps_file="$1"
|
||||||
|
|
||||||
@@ -403,9 +402,7 @@ cleanup() {
|
|||||||
# Set trap for cleanup on exit
|
# Set trap for cleanup on exit
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
# Main function
|
|
||||||
main() {
|
main() {
|
||||||
# Parse args
|
|
||||||
local force_rescan=false
|
local force_rescan=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ select_purge_categories() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
handle_interrupt() {
|
handle_interrupt() {
|
||||||
restore_terminal
|
restore_terminal
|
||||||
exit 130
|
exit 130
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ clean_finder_metadata() {
|
|||||||
|
|
||||||
# Clean macOS system caches
|
# Clean macOS system caches
|
||||||
clean_macos_system_caches() {
|
clean_macos_system_caches() {
|
||||||
|
# Clean saved application states with protection for System Settings
|
||||||
|
# Note: safe_clean already calls should_protect_path for each file
|
||||||
safe_clean ~/Library/Saved\ Application\ State/* "Saved application states"
|
safe_clean ~/Library/Saved\ Application\ State/* "Saved application states"
|
||||||
|
|
||||||
# REMOVED: Spotlight cache cleanup can cause system UI issues
|
# REMOVED: Spotlight cache cleanup can cause system UI issues
|
||||||
|
|||||||
@@ -519,6 +519,10 @@ should_protect_path() {
|
|||||||
*/Library/Group\ Containers/com.apple.systempreferences* | */Library/Group\ Containers/com.apple.Settings*)
|
*/Library/Group\ Containers/com.apple.systempreferences* | */Library/Group\ Containers/com.apple.Settings*)
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
# Shared file lists for System Settings (macOS Sequoia) - Issue #136
|
||||||
|
*/com.apple.sharedfilelist/*com.apple.Settings* | */com.apple.sharedfilelist/*com.apple.SystemSettings* | */com.apple.sharedfilelist/*systempreferences*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# 3. Extract bundle ID from app container/group container paths
|
# 3. Extract bundle ID from app container/group container paths
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ set -euo pipefail
|
|||||||
readonly MOLE_TM_THIN_TIMEOUT=180
|
readonly MOLE_TM_THIN_TIMEOUT=180
|
||||||
readonly MOLE_TM_THIN_VALUE=9999999999
|
readonly MOLE_TM_THIN_VALUE=9999999999
|
||||||
|
|
||||||
# Helper function: Flush DNS cache
|
|
||||||
flush_dns_cache() {
|
flush_dns_cache() {
|
||||||
sudo dscacheutil -flushcache 2> /dev/null && sudo killall -HUP mDNSResponder 2> /dev/null
|
sudo dscacheutil -flushcache 2> /dev/null && sudo killall -HUP mDNSResponder 2> /dev/null
|
||||||
}
|
}
|
||||||
@@ -98,8 +97,24 @@ opt_recent_items() {
|
|||||||
echo -e "${BLUE}${ICON_ARROW}${NC} Clearing recent items lists..."
|
echo -e "${BLUE}${ICON_ARROW}${NC} Clearing recent items lists..."
|
||||||
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
local shared_dir="$HOME/Library/Application Support/com.apple.sharedfilelist"
|
||||||
if [[ -d "$shared_dir" ]]; then
|
if [[ -d "$shared_dir" ]]; then
|
||||||
safe_find_delete "$shared_dir" "*.sfl2" 0 "f"
|
# Use safe removal with protection checks instead of find -delete
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Shared file lists cleared"
|
# This prevents accidental deletion of System Settings files
|
||||||
|
local deleted=0
|
||||||
|
while IFS= read -r -d '' sfl_file; do
|
||||||
|
# Check if file should be protected (System Settings, etc.)
|
||||||
|
if should_protect_path "$sfl_file"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if safe_remove "$sfl_file" true; then
|
||||||
|
((deleted++))
|
||||||
|
fi
|
||||||
|
done < <(command find "$shared_dir" -maxdepth 5 -name "*.sfl2" -type f -print0 2> /dev/null)
|
||||||
|
|
||||||
|
if [[ $deleted -gt 0 ]]; then
|
||||||
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Shared file lists cleared ($deleted files)"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Shared file lists cleared"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "$HOME/Library/Preferences/com.apple.recentitems.plist" 2> /dev/null || true
|
rm -f "$HOME/Library/Preferences/com.apple.recentitems.plist" 2> /dev/null || true
|
||||||
@@ -116,13 +131,9 @@ opt_radio_refresh() {
|
|||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Bluetooth controller refreshed"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Bluetooth controller refreshed"
|
||||||
|
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} Refreshing Wi-Fi service..."
|
echo -e "${BLUE}${ICON_ARROW}${NC} Refreshing Wi-Fi service..."
|
||||||
# Only restart Wi-Fi service, do NOT delete saved networks
|
|
||||||
|
|
||||||
# Safe alternative: just restart the Wi-Fi interface
|
|
||||||
local wifi_interface
|
local wifi_interface
|
||||||
wifi_interface=$(networksetup -listallhardwareports | awk '/Wi-Fi/{getline; print $2}' | head -1)
|
wifi_interface=$(networksetup -listallhardwareports | awk '/Wi-Fi/{getline; print $2}' | head -1)
|
||||||
if [[ -n "$wifi_interface" ]]; then
|
if [[ -n "$wifi_interface" ]]; then
|
||||||
# Use atomic execution to ensure interface comes back up even if interrupted
|
|
||||||
if sudo bash -c "trap '' INT TERM; ifconfig '$wifi_interface' down; sleep 1; ifconfig '$wifi_interface' up" 2> /dev/null; then
|
if sudo bash -c "trap '' INT TERM; ifconfig '$wifi_interface' down; sleep 1; ifconfig '$wifi_interface' up" 2> /dev/null; then
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Wi-Fi interface restarted"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Wi-Fi interface restarted"
|
||||||
else
|
else
|
||||||
@@ -140,8 +151,6 @@ opt_radio_refresh() {
|
|||||||
|
|
||||||
# Mail downloads: clear OLD Mail attachment cache (30+ days)
|
# Mail downloads: clear OLD Mail attachment cache (30+ days)
|
||||||
opt_mail_downloads() {
|
opt_mail_downloads() {
|
||||||
# Validate configuration parameters
|
|
||||||
# Validate configuration parameters
|
|
||||||
local min_size_kb=${MOLE_MAIL_DOWNLOADS_MIN_KB:-5120}
|
local min_size_kb=${MOLE_MAIL_DOWNLOADS_MIN_KB:-5120}
|
||||||
local mail_age_days=${MOLE_MAIL_AGE_DAYS:-30}
|
local mail_age_days=${MOLE_MAIL_AGE_DAYS:-30}
|
||||||
if ! [[ "$min_size_kb" =~ ^[0-9]+$ ]]; then
|
if ! [[ "$min_size_kb" =~ ^[0-9]+$ ]]; then
|
||||||
@@ -183,8 +192,6 @@ opt_mail_downloads() {
|
|||||||
echo -e "${GRAY}-${NC} Only $(bytes_to_human $((total_size_kb * 1024))) detected, skipping cleanup"
|
echo -e "${GRAY}-${NC} Only $(bytes_to_human $((total_size_kb * 1024))) detected, skipping cleanup"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Only delete old attachments (safety window)
|
|
||||||
local cleaned=false
|
local cleaned=false
|
||||||
for target_path in "${mail_dirs[@]}"; do
|
for target_path in "${mail_dirs[@]}"; do
|
||||||
if [[ -d "$target_path" ]]; then
|
if [[ -d "$target_path" ]]; then
|
||||||
@@ -210,10 +217,8 @@ opt_saved_state_cleanup() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Only delete old saved states (safety window)
|
|
||||||
local deleted=0
|
local deleted=0
|
||||||
while IFS= read -r -d '' state_path; do
|
while IFS= read -r -d '' state_path; do
|
||||||
# Protect system critical components
|
|
||||||
if should_protect_path "$state_path"; then
|
if should_protect_path "$state_path"; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -233,7 +238,6 @@ opt_saved_state_cleanup() {
|
|||||||
opt_swap_cleanup() {
|
opt_swap_cleanup() {
|
||||||
echo -e "${BLUE}${ICON_ARROW}${NC} Removing swapfiles and resetting dynamic pager..."
|
echo -e "${BLUE}${ICON_ARROW}${NC} Removing swapfiles and resetting dynamic pager..."
|
||||||
if sudo launchctl unload /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist > /dev/null 2>&1; then
|
if sudo launchctl unload /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist > /dev/null 2>&1; then
|
||||||
# Safe swap reset: just restart the pager, don't manually rm files
|
|
||||||
sudo launchctl load /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist > /dev/null 2>&1 || true
|
sudo launchctl load /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist > /dev/null 2>&1 || true
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Swap cache rebuilt"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Swap cache rebuilt"
|
||||||
else
|
else
|
||||||
@@ -241,10 +245,8 @@ opt_swap_cleanup() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Startup cache: rebuild kernel caches
|
# Startup cache: rebuild kernel caches (handled automatically by modern macOS)
|
||||||
opt_startup_cache() {
|
opt_startup_cache() {
|
||||||
# kextcache/PrelinkedKernel rebuilds are legacy and heavy.
|
|
||||||
# Modern macOS (Big Sur+) handles this automatically and securely (SSV).
|
|
||||||
echo -e "${GRAY}-${NC} Startup cache rebuild skipped (handled by macOS)"
|
echo -e "${GRAY}-${NC} Startup cache rebuild skipped (handled by macOS)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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.13.9"
|
VERSION="1.13.10"
|
||||||
MOLE_TAGLINE="Deep clean and optimize your Mac."
|
MOLE_TAGLINE="Deep clean and optimize your Mac."
|
||||||
|
|
||||||
# Check if Touch ID is already configured
|
# Check if Touch ID is already configured
|
||||||
|
|||||||
Reference in New Issue
Block a user