mirror of
https://github.com/tw93/Mole.git
synced 2026-02-12 13:26:18 +00:00
feat: add --dry-run support for mo optimize
- Skip destructive operations (killall, launchctl unload, sudo) in dry-run mode - Add opt_msg() helper for consistent output formatting - Refactor opt_system_services_refresh() with service array - Show appropriate summary for dry-run vs actual execution - Skip sudo session prompt in dry-run mode
This commit is contained in:
@@ -26,6 +26,11 @@ print_header() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_system_checks() {
|
run_system_checks() {
|
||||||
|
# Skip system checks in dry-run mode (only show what optimizations would run)
|
||||||
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
unset AUTO_FIX_SUMMARY AUTO_FIX_DETAILS
|
unset AUTO_FIX_SUMMARY AUTO_FIX_DETAILS
|
||||||
unset MOLE_SECURITY_FIXES_SHOWN
|
unset MOLE_SECURITY_FIXES_SHOWN
|
||||||
unset MOLE_SECURITY_FIXES_SKIPPED
|
unset MOLE_SECURITY_FIXES_SKIPPED
|
||||||
@@ -65,25 +70,33 @@ show_optimization_summary() {
|
|||||||
if ((safe_count == 0 && confirm_count == 0)) && [[ -z "${AUTO_FIX_SUMMARY:-}" ]]; then
|
if ((safe_count == 0 && confirm_count == 0)) && [[ -z "${AUTO_FIX_SUMMARY:-}" ]]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
local summary_title="Optimization and Check Complete"
|
|
||||||
|
local summary_title
|
||||||
local -a summary_details=()
|
local -a summary_details=()
|
||||||
|
|
||||||
local total_applied=$((safe_count + confirm_count))
|
local total_applied=$((safe_count + confirm_count))
|
||||||
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations; all system services tuned")
|
|
||||||
summary_details+=("Updates, security and system health fully reviewed")
|
|
||||||
|
|
||||||
local summary_line4=""
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
if [[ -n "${AUTO_FIX_SUMMARY:-}" ]]; then
|
summary_title="Dry Run Complete - No Changes Made"
|
||||||
summary_line4="${AUTO_FIX_SUMMARY}"
|
summary_details+=("Would apply ${YELLOW}${total_applied:-0}${NC} optimizations")
|
||||||
if [[ -n "${AUTO_FIX_DETAILS:-}" ]]; then
|
summary_details+=("Run without ${YELLOW}--dry-run${NC} to apply these changes")
|
||||||
local detail_join
|
|
||||||
detail_join=$(echo "${AUTO_FIX_DETAILS}" | paste -sd ", " -)
|
|
||||||
[[ -n "$detail_join" ]] && summary_line4+=" — ${detail_join}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
summary_line4="Your Mac is now faster and more responsive"
|
summary_title="Optimization and Check Complete"
|
||||||
|
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations; all system services tuned")
|
||||||
|
summary_details+=("Updates, security and system health fully reviewed")
|
||||||
|
|
||||||
|
local summary_line4=""
|
||||||
|
if [[ -n "${AUTO_FIX_SUMMARY:-}" ]]; then
|
||||||
|
summary_line4="${AUTO_FIX_SUMMARY}"
|
||||||
|
if [[ -n "${AUTO_FIX_DETAILS:-}" ]]; then
|
||||||
|
local detail_join
|
||||||
|
detail_join=$(echo "${AUTO_FIX_DETAILS}" | paste -sd ", " -)
|
||||||
|
[[ -n "$detail_join" ]] && summary_line4+=" — ${detail_join}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
summary_line4="Your Mac is now faster and more responsive"
|
||||||
|
fi
|
||||||
|
summary_details+=("$summary_line4")
|
||||||
fi
|
fi
|
||||||
summary_details+=("$summary_line4")
|
|
||||||
|
|
||||||
print_summary_block "$summary_title" "${summary_details[@]}"
|
print_summary_block "$summary_title" "${summary_details[@]}"
|
||||||
}
|
}
|
||||||
@@ -317,6 +330,9 @@ main() {
|
|||||||
"--debug")
|
"--debug")
|
||||||
export MO_DEBUG=1
|
export MO_DEBUG=1
|
||||||
;;
|
;;
|
||||||
|
"--dry-run")
|
||||||
|
export MOLE_DRY_RUN=1
|
||||||
|
;;
|
||||||
"--whitelist")
|
"--whitelist")
|
||||||
manage_whitelist "optimize"
|
manage_whitelist "optimize"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -330,6 +346,12 @@ main() {
|
|||||||
clear
|
clear
|
||||||
fi
|
fi
|
||||||
print_header
|
print_header
|
||||||
|
|
||||||
|
# Show dry-run mode indicator
|
||||||
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
|
echo -e "${YELLOW}${ICON_DRY_RUN} DRY RUN MODE${NC} - No files will be modified\n"
|
||||||
|
fi
|
||||||
|
|
||||||
if ! command -v jq > /dev/null 2>&1; then
|
if ! command -v jq > /dev/null 2>&1; then
|
||||||
echo -e "${YELLOW}${ICON_ERROR}${NC} Missing dependency: jq"
|
echo -e "${YELLOW}${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}"
|
||||||
@@ -408,7 +430,9 @@ main() {
|
|||||||
done < "$opts_file"
|
done < "$opts_file"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
ensure_sudo_session "System optimization requires admin access" || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
ensure_sudo_session "System optimization requires admin access" || true
|
||||||
|
fi
|
||||||
|
|
||||||
export FIRST_ACTION=true
|
export FIRST_ACTION=true
|
||||||
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
||||||
|
|||||||
@@ -11,7 +11,23 @@ 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 to get appropriate icon and color for dry-run mode
|
||||||
|
opt_msg() {
|
||||||
|
local message="$1"
|
||||||
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $message"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} $message"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
flush_dns_cache() {
|
flush_dns_cache() {
|
||||||
|
# Skip actual flush in dry-run mode
|
||||||
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
|
MOLE_DNS_FLUSHED=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if sudo dscacheutil -flushcache 2> /dev/null && sudo killall -HUP mDNSResponder 2> /dev/null; then
|
if sudo dscacheutil -flushcache 2> /dev/null && sudo killall -HUP mDNSResponder 2> /dev/null; then
|
||||||
MOLE_DNS_FLUSHED=1
|
MOLE_DNS_FLUSHED=1
|
||||||
return 0
|
return 0
|
||||||
@@ -21,34 +37,28 @@ flush_dns_cache() {
|
|||||||
|
|
||||||
# Rebuild databases and flush caches
|
# Rebuild databases and flush caches
|
||||||
opt_system_maintenance() {
|
opt_system_maintenance() {
|
||||||
local -a results=()
|
|
||||||
local darwin_major
|
|
||||||
darwin_major=$(get_darwin_major)
|
|
||||||
|
|
||||||
if flush_dns_cache; then
|
if flush_dns_cache; then
|
||||||
results+=("${GREEN}✓${NC} DNS cache flushed")
|
opt_msg "DNS cache flushed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local spotlight_status
|
local spotlight_status
|
||||||
spotlight_status=$(mdutil -s / 2> /dev/null || echo "")
|
spotlight_status=$(mdutil -s / 2> /dev/null || echo "")
|
||||||
if echo "$spotlight_status" | grep -qi "Indexing disabled"; then
|
if echo "$spotlight_status" | grep -qi "Indexing disabled"; then
|
||||||
results+=("${GRAY}${ICON_EMPTY}${NC} Spotlight indexing disabled")
|
echo -e " ${GRAY}${ICON_EMPTY}${NC} Spotlight indexing disabled"
|
||||||
else
|
else
|
||||||
results+=("${GREEN}✓${NC} Spotlight index verified")
|
opt_msg "Spotlight index verified"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for result in "${results[@]}"; do
|
|
||||||
echo -e " $result"
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Refresh Finder caches (QuickLook and icon services)
|
# Refresh Finder caches (QuickLook and icon services)
|
||||||
# Note: Safari caches are cleaned separately in clean/user.sh, so excluded here
|
# Note: Safari caches are cleaned separately in clean/user.sh, so excluded here
|
||||||
opt_cache_refresh() {
|
opt_cache_refresh() {
|
||||||
qlmanage -r cache > /dev/null 2>&1 || true
|
# Skip qlmanage commands in dry-run mode
|
||||||
qlmanage -r > /dev/null 2>&1 || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
qlmanage -r cache > /dev/null 2>&1 || true
|
||||||
|
qlmanage -r > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
local refreshed=0
|
|
||||||
local -a cache_targets=(
|
local -a cache_targets=(
|
||||||
"$HOME/Library/Caches/com.apple.QuickLook.thumbnailcache"
|
"$HOME/Library/Caches/com.apple.QuickLook.thumbnailcache"
|
||||||
"$HOME/Library/Caches/com.apple.iconservices.store"
|
"$HOME/Library/Caches/com.apple.iconservices.store"
|
||||||
@@ -58,15 +68,13 @@ opt_cache_refresh() {
|
|||||||
for target_path in "${cache_targets[@]}"; do
|
for target_path in "${cache_targets[@]}"; do
|
||||||
if [[ -e "$target_path" ]]; then
|
if [[ -e "$target_path" ]]; then
|
||||||
if ! should_protect_path "$target_path"; then
|
if ! should_protect_path "$target_path"; then
|
||||||
if safe_remove "$target_path" true; then
|
safe_remove "$target_path" true > /dev/null 2>&1
|
||||||
((refreshed++))
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -e " ${GREEN}✓${NC} QuickLook thumbnails refreshed"
|
opt_msg "QuickLook thumbnails refreshed"
|
||||||
echo -e " ${GREEN}✓${NC} Icon services cache rebuilt"
|
opt_msg "Icon services cache rebuilt"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Removed: opt_maintenance_scripts - macOS handles log rotation automatically via launchd
|
# Removed: opt_maintenance_scripts - macOS handles log rotation automatically via launchd
|
||||||
@@ -86,7 +94,7 @@ opt_saved_state_cleanup() {
|
|||||||
done < <(command find "$state_dir" -type d -name "*.savedState" -mtime "+$MOLE_SAVED_STATE_AGE_DAYS" -print0 2> /dev/null)
|
done < <(command find "$state_dir" -type d -name "*.savedState" -mtime "+$MOLE_SAVED_STATE_AGE_DAYS" -print0 2> /dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e " ${GREEN}✓${NC} App saved states optimized"
|
opt_msg "App saved states optimized"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Removed: opt_swap_cleanup - Direct virtual memory operations pose system crash risk
|
# Removed: opt_swap_cleanup - Direct virtual memory operations pose system crash risk
|
||||||
@@ -99,23 +107,23 @@ opt_fix_broken_configs() {
|
|||||||
local broken_prefs=$(fix_broken_preferences)
|
local broken_prefs=$(fix_broken_preferences)
|
||||||
|
|
||||||
if [[ $broken_prefs -gt 0 ]]; then
|
if [[ $broken_prefs -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Repaired $broken_prefs corrupted preference files"
|
opt_msg "Repaired $broken_prefs corrupted preference files"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}✓${NC} All preference files valid"
|
opt_msg "All preference files valid"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Network cache optimization
|
# Network cache optimization
|
||||||
opt_network_optimization() {
|
opt_network_optimization() {
|
||||||
if [[ "${MOLE_DNS_FLUSHED:-0}" == "1" ]]; then
|
if [[ "${MOLE_DNS_FLUSHED:-0}" == "1" ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} DNS cache already refreshed"
|
opt_msg "DNS cache already refreshed"
|
||||||
echo -e " ${GREEN}✓${NC} mDNSResponder already restarted"
|
opt_msg "mDNSResponder already restarted"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if flush_dns_cache; then
|
if flush_dns_cache; then
|
||||||
echo -e " ${GREEN}✓${NC} DNS cache refreshed"
|
opt_msg "DNS cache refreshed"
|
||||||
echo -e " ${GREEN}✓${NC} mDNSResponder restarted"
|
opt_msg "mDNSResponder restarted"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to refresh DNS cache"
|
echo -e " ${YELLOW}!${NC} Failed to refresh DNS cache"
|
||||||
fi
|
fi
|
||||||
@@ -167,27 +175,32 @@ opt_sqlite_vacuum() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Try to vacuum
|
# Try to vacuum (skip in dry-run mode)
|
||||||
local exit_code=0
|
local exit_code=0
|
||||||
set +e
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
run_with_timeout 20 sqlite3 "$db_file" "VACUUM;" 2> /dev/null
|
set +e
|
||||||
exit_code=$?
|
run_with_timeout 20 sqlite3 "$db_file" "VACUUM;" 2> /dev/null
|
||||||
set -e
|
exit_code=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
if [[ $exit_code -eq 0 ]]; then
|
if [[ $exit_code -eq 0 ]]; then
|
||||||
((vacuumed++))
|
((vacuumed++))
|
||||||
elif [[ $exit_code -eq 124 ]]; then
|
elif [[ $exit_code -eq 124 ]]; then
|
||||||
((timed_out++))
|
((timed_out++))
|
||||||
|
else
|
||||||
|
((failed++))
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
((failed++))
|
# In dry-run mode, just count the database
|
||||||
|
((vacuumed++))
|
||||||
fi
|
fi
|
||||||
done < <(compgen -G "$pattern" || true)
|
done < <(compgen -G "$pattern" || true)
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ $vacuumed -gt 0 ]]; then
|
if [[ $vacuumed -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Optimized $vacuumed databases for Mail, Safari, Messages"
|
opt_msg "Optimized $vacuumed databases for Mail, Safari, Messages"
|
||||||
elif [[ $timed_out -eq 0 && $failed -eq 0 ]]; then
|
elif [[ $timed_out -eq 0 && $failed -eq 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} All databases already optimized"
|
opt_msg "All databases already optimized"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Database optimization incomplete"
|
echo -e " ${YELLOW}!${NC} Database optimization incomplete"
|
||||||
fi
|
fi
|
||||||
@@ -212,22 +225,28 @@ opt_launch_services_rebuild() {
|
|||||||
|
|
||||||
if [[ -f "$lsregister" ]]; then
|
if [[ -f "$lsregister" ]]; then
|
||||||
local success=0
|
local success=0
|
||||||
set +e
|
|
||||||
"$lsregister" -r -domain local -domain user -domain system > /dev/null 2>&1
|
# Skip actual rebuild in dry-run mode
|
||||||
success=$?
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
if [[ $success -ne 0 ]]; then
|
set +e
|
||||||
"$lsregister" -r -domain local -domain user > /dev/null 2>&1
|
"$lsregister" -r -domain local -domain user -domain system > /dev/null 2>&1
|
||||||
success=$?
|
success=$?
|
||||||
|
if [[ $success -ne 0 ]]; then
|
||||||
|
"$lsregister" -r -domain local -domain user > /dev/null 2>&1
|
||||||
|
success=$?
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
|
else
|
||||||
|
success=0 # Assume success in dry-run mode
|
||||||
fi
|
fi
|
||||||
set -e
|
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
stop_inline_spinner
|
stop_inline_spinner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $success -eq 0 ]]; then
|
if [[ $success -eq 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} LaunchServices repaired"
|
opt_msg "LaunchServices repaired"
|
||||||
echo -e " ${GREEN}✓${NC} File associations refreshed"
|
opt_msg "File associations refreshed"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to rebuild LaunchServices"
|
echo -e " ${YELLOW}!${NC} Failed to rebuild LaunchServices"
|
||||||
fi
|
fi
|
||||||
@@ -244,13 +263,18 @@ opt_launch_services_rebuild() {
|
|||||||
opt_font_cache_rebuild() {
|
opt_font_cache_rebuild() {
|
||||||
local success=false
|
local success=false
|
||||||
|
|
||||||
if sudo atsutil databases -remove > /dev/null 2>&1; then
|
# Skip actual font cache removal in dry-run mode
|
||||||
success=true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
if sudo atsutil databases -remove > /dev/null 2>&1; then
|
||||||
|
success=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
success=true # Assume success in dry-run mode
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$success" == "true" ]]; then
|
if [[ "$success" == "true" ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Font cache cleared"
|
opt_msg "Font cache cleared"
|
||||||
echo -e " ${GREEN}✓${NC} System will rebuild font database automatically"
|
opt_msg "System will rebuild font database automatically"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to clear font cache"
|
echo -e " ${YELLOW}!${NC} Failed to clear font cache"
|
||||||
fi
|
fi
|
||||||
@@ -330,14 +354,18 @@ opt_startup_items_cleanup() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$need_sudo" == "true" ]]; then
|
if [[ "$need_sudo" == "true" ]]; then
|
||||||
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
|
fi
|
||||||
if safe_sudo_remove "$plist_file"; then
|
if safe_sudo_remove "$plist_file"; then
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to remove (sudo) $plist_file"
|
echo -e " ${YELLOW}!${NC} Failed to remove (sudo) $plist_file"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
|
fi
|
||||||
if safe_remove "$plist_file" true; then
|
if safe_remove "$plist_file" true; then
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
else
|
else
|
||||||
@@ -374,23 +402,33 @@ opt_startup_items_cleanup() {
|
|||||||
elif [[ -d "$HOME/Applications/$associated_bundle.app" ]]; then
|
elif [[ -d "$HOME/Applications/$associated_bundle.app" ]]; then
|
||||||
app_path="$HOME/Applications/$associated_bundle.app"
|
app_path="$HOME/Applications/$associated_bundle.app"
|
||||||
else
|
else
|
||||||
# Fallback to mdfind (slower but comprehensive, with 10s timeout)
|
# Try extracting app name from bundle ID (e.g., com.dropbox.Dropbox -> Dropbox)
|
||||||
app_path=$(run_with_timeout 10 mdfind "kMDItemCFBundleIdentifier == '$associated_bundle'" 2> /dev/null | head -1 || echo "")
|
local app_name="${associated_bundle##*.}"
|
||||||
|
if [[ -n "$app_name" && -d "/Applications/$app_name.app" ]]; then
|
||||||
|
app_path="/Applications/$app_name.app"
|
||||||
|
elif [[ -n "$app_name" && -d "$HOME/Applications/$app_name.app" ]]; then
|
||||||
|
app_path="$HOME/Applications/$app_name.app"
|
||||||
|
else
|
||||||
|
# Fallback to mdfind (slower but comprehensive, with 10s timeout)
|
||||||
|
app_path=$(run_with_timeout 10 mdfind "kMDItemCFBundleIdentifier == '$associated_bundle'" 2> /dev/null | head -1 || echo "")
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If associated app is MISSING, this is an orphan
|
# CRITICAL FIX: Only consider it orphaned if BOTH conditions are true:
|
||||||
|
# 1. Associated app is not found
|
||||||
|
# 2. The program/executable itself also doesn't exist
|
||||||
if [[ -z "$app_path" ]]; then
|
if [[ -z "$app_path" ]]; then
|
||||||
if command -v should_protect_path > /dev/null && should_protect_path "$plist_file"; then
|
if command -v should_protect_path > /dev/null && should_protect_path "$plist_file"; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the helper tool path
|
# CRITICAL: Check if the program itself exists (reuse already extracted program path)
|
||||||
local program=""
|
# If the executable exists, this is NOT an orphan - it's a valid helper
|
||||||
program=$(plutil -extract Program raw "$plist_file" 2> /dev/null || echo "")
|
# whose app we just can't find (maybe mdfind indexing issue, non-standard location, etc.)
|
||||||
if [[ -z "$program" ]]; then
|
if [[ -n "$program" && -e "$program" ]]; then
|
||||||
program=$(plutil -extract ProgramArguments.0 raw "$plist_file" 2> /dev/null || echo "")
|
debug_log "Keeping LaunchAgent (program exists): $plist_file -> $program"
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
program="${program/#\~/$HOME}"
|
|
||||||
|
|
||||||
# Double check we are not deleting system files
|
# Double check we are not deleting system files
|
||||||
if [[ "$program" == /System/* ||
|
if [[ "$program" == /System/* ||
|
||||||
@@ -401,25 +439,26 @@ opt_startup_items_cleanup() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Only delete if BOTH app and program are missing
|
||||||
|
debug_log "Removing orphaned helper (app not found, program missing): $plist_file"
|
||||||
|
|
||||||
if [[ "$need_sudo" == "true" ]]; then
|
if [[ "$need_sudo" == "true" ]]; then
|
||||||
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
|
fi
|
||||||
# remove the plist
|
# remove the plist
|
||||||
safe_sudo_remove "$plist_file"
|
safe_sudo_remove "$plist_file"
|
||||||
|
|
||||||
# AND remove the helper binary if it exists and is not protected
|
# The program doesn't exist (verified above), so no need to remove it
|
||||||
if [[ -n "$program" && -f "$program" ]]; then
|
|
||||||
safe_sudo_remove "$program"
|
|
||||||
fi
|
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
echo -e " ${GREEN}✓${NC} Removed orphaned helper: $(basename "$program")"
|
opt_msg "Removed orphaned helper: $(basename "$plist_file" .plist)"
|
||||||
else
|
else
|
||||||
launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
safe_remove "$plist_file" true
|
launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
if [[ -n "$program" && -f "$program" ]]; then
|
|
||||||
safe_remove "$program" true
|
|
||||||
fi
|
fi
|
||||||
|
safe_remove "$plist_file" true
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
echo -e " ${GREEN}✓${NC} Removed orphaned helper: $(basename "$program")"
|
opt_msg "Removed orphaned helper: $(basename "$plist_file" .plist)"
|
||||||
fi
|
fi
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -432,14 +471,18 @@ opt_startup_items_cleanup() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$need_sudo" == "true" ]]; then
|
if [[ "$need_sudo" == "true" ]]; then
|
||||||
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
sudo launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
|
fi
|
||||||
if safe_sudo_remove "$plist_file"; then
|
if safe_sudo_remove "$plist_file"; then
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to remove (sudo) $plist_file"
|
echo -e " ${YELLOW}!${NC} Failed to remove (sudo) $plist_file"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
launchctl unload "$plist_file" 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
launchctl unload "$plist_file" 2> /dev/null || true
|
||||||
|
fi
|
||||||
if safe_remove "$plist_file" true; then
|
if safe_remove "$plist_file" true; then
|
||||||
((broken_count++))
|
((broken_count++))
|
||||||
else
|
else
|
||||||
@@ -451,13 +494,13 @@ opt_startup_items_cleanup() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [[ $broken_count -gt 0 ]]; then
|
if [[ $broken_count -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Removed $broken_count broken startup items"
|
opt_msg "Removed $broken_count broken startup items"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $total_count -gt 0 ]]; then
|
if [[ $total_count -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Verified $total_count startup items"
|
opt_msg "Verified $total_count startup items"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}✓${NC} No startup items found"
|
opt_msg "No startup items found"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +525,7 @@ opt_dyld_cache_update() {
|
|||||||
local one_day_seconds=$((24 * 3600))
|
local one_day_seconds=$((24 * 3600))
|
||||||
|
|
||||||
if [[ $time_diff -lt $one_day_seconds ]]; then
|
if [[ $time_diff -lt $one_day_seconds ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} dyld shared cache already up-to-date"
|
opt_msg "dyld shared cache already up-to-date"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -493,13 +536,20 @@ opt_dyld_cache_update() {
|
|||||||
|
|
||||||
local success=false
|
local success=false
|
||||||
local exit_code=0
|
local exit_code=0
|
||||||
# This can take 1-2 minutes on some systems (180 second timeout)
|
|
||||||
set +e
|
# Skip actual rebuild in dry-run mode
|
||||||
run_with_timeout 180 sudo update_dyld_shared_cache -force > /dev/null 2>&1
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
exit_code=$?
|
# This can take 1-2 minutes on some systems (180 second timeout)
|
||||||
set -e
|
set +e
|
||||||
if [[ $exit_code -eq 0 ]]; then
|
run_with_timeout 180 sudo update_dyld_shared_cache -force > /dev/null 2>&1
|
||||||
success=true
|
exit_code=$?
|
||||||
|
set -e
|
||||||
|
if [[ $exit_code -eq 0 ]]; then
|
||||||
|
success=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
success=true # Assume success in dry-run mode
|
||||||
|
exit_code=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
@@ -507,8 +557,8 @@ opt_dyld_cache_update() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$success" == "true" ]]; then
|
if [[ "$success" == "true" ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} dyld shared cache rebuilt"
|
opt_msg "dyld shared cache rebuilt"
|
||||||
echo -e " ${GREEN}✓${NC} App launch speed improved"
|
opt_msg "App launch speed improved"
|
||||||
elif [[ $exit_code -eq 124 ]]; then
|
elif [[ $exit_code -eq 124 ]]; then
|
||||||
echo -e " ${YELLOW}!${NC} dyld cache update timed out"
|
echo -e " ${YELLOW}!${NC} dyld cache update timed out"
|
||||||
else
|
else
|
||||||
@@ -519,35 +569,45 @@ opt_dyld_cache_update() {
|
|||||||
# System services refresh
|
# System services refresh
|
||||||
# Restarts system services to apply cache and configuration changes
|
# Restarts system services to apply cache and configuration changes
|
||||||
opt_system_services_refresh() {
|
opt_system_services_refresh() {
|
||||||
|
local -a services=(
|
||||||
|
"cfprefsd:Preferences"
|
||||||
|
"lsd:LaunchServices"
|
||||||
|
"iconservicesagent:Icon Services"
|
||||||
|
"fontd:Font Server"
|
||||||
|
)
|
||||||
local -a restarted_services=()
|
local -a restarted_services=()
|
||||||
|
|
||||||
# cfprefsd - Preferences cache daemon (ensures fixed preferences take effect)
|
# Skip actual service restarts in dry-run mode
|
||||||
if killall -HUP cfprefsd 2> /dev/null; then
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
restarted_services+=("Preferences")
|
for service_entry in "${services[@]}"; do
|
||||||
fi
|
IFS=':' read -r process_name display_name <<< "$service_entry"
|
||||||
|
|
||||||
# lsd - LaunchServices daemon (ensures rebuild takes effect)
|
# Special handling for cfprefsd (use -HUP instead of normal kill)
|
||||||
if killall lsd 2> /dev/null; then
|
if [[ "$process_name" == "cfprefsd" ]]; then
|
||||||
restarted_services+=("LaunchServices")
|
if killall -HUP "$process_name" 2> /dev/null; then
|
||||||
fi
|
restarted_services+=("$display_name")
|
||||||
|
fi
|
||||||
# iconservicesagent - Icon services (ensures cache refresh takes effect)
|
else
|
||||||
if killall iconservicesagent 2> /dev/null; then
|
if killall "$process_name" 2> /dev/null; then
|
||||||
restarted_services+=("Icon Services")
|
restarted_services+=("$display_name")
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
# fontd - Font server (ensures font cache refresh takes effect)
|
done
|
||||||
if killall fontd 2> /dev/null; then
|
else
|
||||||
restarted_services+=("Font Server")
|
# In dry-run mode, show all services that would be restarted
|
||||||
|
for service_entry in "${services[@]}"; do
|
||||||
|
IFS=':' read -r _ display_name <<< "$service_entry"
|
||||||
|
restarted_services+=("$display_name")
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${#restarted_services[@]} -gt 0 ]]; then
|
if [[ ${#restarted_services[@]} -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Refreshed ${#restarted_services[@]} system services"
|
opt_msg "Refreshed ${#restarted_services[@]} system services"
|
||||||
for service in "${restarted_services[@]}"; do
|
for service in "${restarted_services[@]}"; do
|
||||||
echo -e " • $service"
|
echo -e " • $service"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}✓${NC} System services already optimal"
|
opt_msg "System services already optimal"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,13 +633,15 @@ opt_dock_refresh() {
|
|||||||
touch "$dock_plist" 2> /dev/null || true
|
touch "$dock_plist" 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restart Dock to apply changes
|
# Restart Dock to apply changes (skip in dry-run mode)
|
||||||
killall Dock 2> /dev/null || true
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
|
killall Dock 2> /dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$refreshed" == "true" ]]; then
|
if [[ "$refreshed" == "true" ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} Dock cache cleared"
|
opt_msg "Dock cache cleared"
|
||||||
fi
|
fi
|
||||||
echo -e " ${GREEN}✓${NC} Dock refreshed"
|
opt_msg "Dock refreshed"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Execute optimization by action name
|
# Execute optimization by action name
|
||||||
|
|||||||
Reference in New Issue
Block a user