mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 14:26:46 +00:00
384 lines
10 KiB
Bash
Executable File
384 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Load common functions
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
source "$SCRIPT_DIR/lib/optimize_health.sh"
|
|
source "$SCRIPT_DIR/lib/sudo_manager.sh"
|
|
source "$SCRIPT_DIR/lib/update_manager.sh"
|
|
source "$SCRIPT_DIR/lib/autofix_manager.sh"
|
|
source "$SCRIPT_DIR/lib/optimization_tasks.sh"
|
|
|
|
# Load check modules
|
|
source "$SCRIPT_DIR/lib/check_updates.sh"
|
|
source "$SCRIPT_DIR/lib/check_health.sh"
|
|
source "$SCRIPT_DIR/lib/check_security.sh"
|
|
source "$SCRIPT_DIR/lib/check_config.sh"
|
|
|
|
# Colors and icons from common.sh
|
|
|
|
print_header() {
|
|
printf '\n'
|
|
echo -e "${PURPLE}Optimize and Check${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# System check functions (real-time display)
|
|
run_system_checks() {
|
|
echo ""
|
|
echo -e "${PURPLE}System Check${NC}"
|
|
echo ""
|
|
|
|
# Check updates - real-time display
|
|
echo -e "${BLUE}${ICON_ARROW}${NC} System updates"
|
|
check_all_updates
|
|
echo ""
|
|
|
|
# Check health - real-time display
|
|
echo -e "${BLUE}${ICON_ARROW}${NC} System health"
|
|
check_system_health
|
|
echo ""
|
|
|
|
# Check security - real-time display
|
|
echo -e "${BLUE}${ICON_ARROW}${NC} Security posture"
|
|
check_all_security
|
|
echo ""
|
|
|
|
# Check configuration - real-time display
|
|
echo -e "${BLUE}${ICON_ARROW}${NC} Configuration"
|
|
check_all_config
|
|
echo ""
|
|
|
|
# Show suggestions
|
|
show_suggestions
|
|
echo ""
|
|
|
|
# Ask about updates first
|
|
if ask_for_updates; then
|
|
perform_updates
|
|
fi
|
|
|
|
# Ask about auto-fix
|
|
if ask_for_auto_fix; then
|
|
perform_auto_fix
|
|
fi
|
|
}
|
|
|
|
show_optimization_summary() {
|
|
if [[ -z "${OPTIMIZE_SAFE_COUNT:-}" ]]; then
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
local summary_title="Optimization and Check Complete"
|
|
local -a summary_details=()
|
|
|
|
# Optimization results
|
|
if ((OPTIMIZE_SAFE_COUNT > 0)); then
|
|
summary_details+=("Applied ${GREEN}${OPTIMIZE_SAFE_COUNT}${NC} optimizations")
|
|
else
|
|
summary_details+=("System already optimized")
|
|
fi
|
|
|
|
if ((OPTIMIZE_CONFIRM_COUNT > 0)); then
|
|
summary_details+=("${YELLOW}${OPTIMIZE_CONFIRM_COUNT}${NC} manual checks suggested")
|
|
fi
|
|
|
|
summary_details+=("Caches cleared, databases rebuilt, services refreshed")
|
|
|
|
# System check results
|
|
summary_details+=("System updates, health, security, and config reviewed")
|
|
summary_details+=("System should feel faster and more responsive")
|
|
|
|
if [[ "${OPTIMIZE_SHOW_TOUCHID_TIP:-false}" == "true" ]]; then
|
|
echo -e "${YELLOW}☻${NC} Run ${GRAY}mo touchid${NC} to approve sudo via Touch ID"
|
|
fi
|
|
print_summary_block "success" "$summary_title" "${summary_details[@]}"
|
|
}
|
|
|
|
|
|
show_system_health() {
|
|
local health_json="$1"
|
|
|
|
# Parse system health using jq
|
|
local mem_used=$(echo "$health_json" | jq -r '.memory_used_gb')
|
|
local mem_total=$(echo "$health_json" | jq -r '.memory_total_gb')
|
|
local disk_used=$(echo "$health_json" | jq -r '.disk_used_gb')
|
|
local disk_total=$(echo "$health_json" | jq -r '.disk_total_gb')
|
|
local disk_percent=$(echo "$health_json" | jq -r '.disk_used_percent')
|
|
local uptime=$(echo "$health_json" | jq -r '.uptime_days')
|
|
|
|
# Compact one-line format with icon
|
|
printf "${ICON_ADMIN} System %.0f/%.0f GB RAM | %.0f/%.0f GB Disk (%.0f%%) | Uptime %.0fd\n" \
|
|
"$mem_used" "$mem_total" "$disk_used" "$disk_total" "$disk_percent" "$uptime"
|
|
echo ""
|
|
}
|
|
|
|
parse_optimizations() {
|
|
local health_json="$1"
|
|
|
|
# Extract optimizations array
|
|
echo "$health_json" | jq -c '.optimizations[]' 2> /dev/null
|
|
}
|
|
|
|
announce_action() {
|
|
local name="$1"
|
|
local desc="$2"
|
|
local kind="$3"
|
|
|
|
local badge=""
|
|
if [[ "$kind" == "confirm" ]]; then
|
|
badge="${YELLOW}[Confirm]${NC} "
|
|
fi
|
|
|
|
local line="${BLUE}${ICON_ARROW}${NC} ${badge}${name}"
|
|
if [[ -n "$desc" ]]; then
|
|
line+=" ${GRAY}- ${desc}${NC}"
|
|
fi
|
|
|
|
if ${first_heading:-true}; then
|
|
first_heading=false
|
|
else
|
|
echo ""
|
|
fi
|
|
|
|
echo -e "$line"
|
|
}
|
|
|
|
touchid_configured() {
|
|
local pam_file="/etc/pam.d/sudo"
|
|
[[ -f "$pam_file" ]] && grep -q "pam_tid.so" "$pam_file" 2> /dev/null
|
|
}
|
|
|
|
touchid_supported() {
|
|
if command -v bioutil > /dev/null 2>&1; then
|
|
bioutil -r 2> /dev/null | grep -q "Touch ID" && return 0
|
|
fi
|
|
[[ "$(uname -m)" == "arm64" ]]
|
|
}
|
|
|
|
cleanup_path() {
|
|
local raw_path="$1"
|
|
local label="$2"
|
|
|
|
local expanded_path="${raw_path/#\~/$HOME}"
|
|
if [[ ! -e "$expanded_path" ]]; then
|
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
|
return
|
|
fi
|
|
|
|
local size_kb
|
|
size_kb=$(du -sk "$expanded_path" 2> /dev/null | awk '{print $1}' || echo "0")
|
|
local size_display=""
|
|
if [[ "$size_kb" =~ ^[0-9]+$ && "$size_kb" -gt 0 ]]; then
|
|
size_display=$(bytes_to_human "$((size_kb * 1024))")
|
|
fi
|
|
|
|
local removed=false
|
|
if rm -rf "$expanded_path" 2> /dev/null; then
|
|
removed=true
|
|
elif request_sudo_access "Removing $label requires admin access"; then
|
|
if sudo rm -rf "$expanded_path" 2> /dev/null; then
|
|
removed=true
|
|
fi
|
|
fi
|
|
|
|
if [[ "$removed" == "true" ]]; then
|
|
if [[ -n "$size_display" ]]; then
|
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label ${GREEN}(${size_display})${NC}"
|
|
else
|
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}${ICON_WARNING}${NC} Skipped $label ${GRAY}(grant Full Disk Access to your terminal and retry)${NC}"
|
|
fi
|
|
}
|
|
|
|
ensure_directory() {
|
|
local raw_path="$1"
|
|
local expanded_path="${raw_path/#\~/$HOME}"
|
|
mkdir -p "$expanded_path" > /dev/null 2>&1 || true
|
|
}
|
|
|
|
count_local_snapshots() {
|
|
if ! command -v tmutil > /dev/null 2>&1; then
|
|
echo 0
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$(tmutil listlocalsnapshots / 2> /dev/null || true)
|
|
if [[ -z "$output" ]]; then
|
|
echo 0
|
|
return
|
|
fi
|
|
|
|
echo "$output" | grep -c "com.apple.TimeMachine." | tr -d ' '
|
|
}
|
|
|
|
|
|
cleanup_all() {
|
|
stop_sudo_session
|
|
cleanup_temp_files
|
|
}
|
|
|
|
main() {
|
|
# Register unified cleanup handler
|
|
trap cleanup_all EXIT INT TERM
|
|
|
|
if [[ -t 1 ]]; then
|
|
clear
|
|
fi
|
|
print_header
|
|
|
|
# Check dependencies
|
|
if ! command -v jq > /dev/null 2>&1; then
|
|
echo -e "${RED}${ICON_ERROR}${NC} Missing dependency: jq"
|
|
echo -e "${GRAY}Install with: ${GREEN}brew install jq${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v bc > /dev/null 2>&1; then
|
|
echo -e "${RED}${ICON_ERROR}${NC} Missing dependency: bc"
|
|
echo -e "${GRAY}Install with: ${GREEN}brew install bc${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Simple confirmation
|
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} Optimization needs sudo — ${GREEN}Enter${NC} continue, ${GRAY}ESC${NC} cancel: "
|
|
|
|
local key
|
|
if ! key=$(read_key); then
|
|
echo -e " ${GRAY}Cancelled${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$key" == "ENTER" ]]; then
|
|
printf "\r\033[K"
|
|
else
|
|
echo -e " ${GRAY}Cancelled${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# Collect system health data after confirmation
|
|
if [[ -t 1 ]]; then
|
|
start_inline_spinner "Collecting system info..."
|
|
fi
|
|
|
|
local health_json
|
|
if ! health_json=$(generate_health_json 2> /dev/null); then
|
|
if [[ -t 1 ]]; then
|
|
stop_inline_spinner
|
|
fi
|
|
echo ""
|
|
log_error "Failed to collect system health data"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -t 1 ]]; then
|
|
stop_inline_spinner
|
|
fi
|
|
|
|
# Show system health
|
|
show_system_health "$health_json"
|
|
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
echo "DEBUG: System health displayed"
|
|
fi
|
|
|
|
# Parse and display optimizations
|
|
local -a safe_items=()
|
|
local -a confirm_items=()
|
|
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
echo "DEBUG: Parsing optimizations..."
|
|
fi
|
|
|
|
# Use temp file instead of process substitution to avoid hanging
|
|
local opts_file
|
|
opts_file=$(mktemp_file)
|
|
parse_optimizations "$health_json" > "$opts_file"
|
|
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
local opt_count=$(wc -l < "$opts_file" | tr -d ' ')
|
|
echo "DEBUG: Found $opt_count optimizations"
|
|
fi
|
|
|
|
while IFS= read -r opt_json; do
|
|
[[ -z "$opt_json" ]] && continue
|
|
|
|
local name=$(echo "$opt_json" | jq -r '.name')
|
|
local desc=$(echo "$opt_json" | jq -r '.description')
|
|
local action=$(echo "$opt_json" | jq -r '.action')
|
|
local path=$(echo "$opt_json" | jq -r '.path // ""')
|
|
local safe=$(echo "$opt_json" | jq -r '.safe')
|
|
|
|
local item="${name}|${desc}|${action}|${path}"
|
|
|
|
if [[ "$safe" == "true" ]]; then
|
|
safe_items+=("$item")
|
|
else
|
|
confirm_items+=("$item")
|
|
fi
|
|
done < "$opts_file"
|
|
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
echo "DEBUG: Parsing complete. Safe: ${#safe_items[@]}, Confirm: ${#confirm_items[@]}"
|
|
fi
|
|
|
|
# Execute all optimizations
|
|
local first_heading=true
|
|
|
|
# Debug: show what we're about to do
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
echo "DEBUG: About to request sudo. Safe items: ${#safe_items[@]}, Confirm items: ${#confirm_items[@]}"
|
|
fi
|
|
|
|
ensure_sudo_session "System optimization requires admin access" || true
|
|
|
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
|
echo "DEBUG: Sudo session established or skipped"
|
|
fi
|
|
|
|
# Run safe optimizations
|
|
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
|
for item in "${safe_items[@]}"; do
|
|
IFS='|' read -r name desc action path <<< "$item"
|
|
announce_action "$name" "$desc" "safe"
|
|
execute_optimization "$action" "$path"
|
|
done
|
|
fi
|
|
|
|
# Run confirm items
|
|
if [[ ${#confirm_items[@]} -gt 0 ]]; then
|
|
for item in "${confirm_items[@]}"; do
|
|
IFS='|' read -r name desc action path <<< "$item"
|
|
announce_action "$name" "$desc" "confirm"
|
|
execute_optimization "$action" "$path"
|
|
done
|
|
fi
|
|
|
|
# Prepare optimization summary data (to show at the end)
|
|
local safe_count=${#safe_items[@]}
|
|
local confirm_count=${#confirm_items[@]}
|
|
|
|
export OPTIMIZE_SAFE_COUNT=$safe_count
|
|
export OPTIMIZE_CONFIRM_COUNT=$confirm_count
|
|
export OPTIMIZE_SHOW_TOUCHID_TIP="false"
|
|
if touchid_supported && ! touchid_configured; then
|
|
export OPTIMIZE_SHOW_TOUCHID_TIP="true"
|
|
fi
|
|
|
|
# Run system checks first
|
|
run_system_checks
|
|
|
|
# Show optimization summary at the end
|
|
show_optimization_summary
|
|
|
|
printf '\n'
|
|
}
|
|
|
|
main "$@"
|