#!/bin/bash # Mole - Common Functions Library # Main entry point that loads all core modules set -euo pipefail # Prevent multiple sourcing if [[ -n "${MOLE_COMMON_LOADED:-}" ]]; then return 0 fi readonly MOLE_COMMON_LOADED=1 _MOLE_CORE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Load core modules source "$_MOLE_CORE_DIR/base.sh" source "$_MOLE_CORE_DIR/log.sh" source "$_MOLE_CORE_DIR/timeout.sh" source "$_MOLE_CORE_DIR/file_ops.sh" source "$_MOLE_CORE_DIR/ui.sh" source "$_MOLE_CORE_DIR/app_protection.sh" # Load sudo management if available if [[ -f "$_MOLE_CORE_DIR/sudo.sh" ]]; then source "$_MOLE_CORE_DIR/sudo.sh" fi # Update via Homebrew update_via_homebrew() { local current_version="$1" local temp_update temp_upgrade temp_update=$(mktemp_file "brew_update") temp_upgrade=$(mktemp_file "brew_upgrade") # Set up trap for interruption (Ctrl+C) with inline cleanup trap 'stop_inline_spinner 2>/dev/null; safe_remove "$temp_update" true; safe_remove "$temp_upgrade" true; echo ""; exit 130' INT TERM # Update Homebrew if [[ -t 1 ]]; then start_inline_spinner "Updating Homebrew..." else echo "Updating Homebrew..." fi brew update > "$temp_update" 2>&1 & local update_pid=$! wait $update_pid 2> /dev/null || true # Continue even if brew update fails if [[ -t 1 ]]; then stop_inline_spinner fi # Upgrade Mole if [[ -t 1 ]]; then start_inline_spinner "Upgrading Mole..." else echo "Upgrading Mole..." fi brew upgrade mole > "$temp_upgrade" 2>&1 & local upgrade_pid=$! wait $upgrade_pid 2> /dev/null || true # Continue even if brew upgrade fails local upgrade_output upgrade_output=$(cat "$temp_upgrade") if [[ -t 1 ]]; then stop_inline_spinner fi # Clear trap trap - INT TERM # Cleanup temp files safe_remove "$temp_update" true safe_remove "$temp_upgrade" true if echo "$upgrade_output" | grep -q "already installed"; then local installed_version installed_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}') echo "" echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version (${installed_version:-$current_version})" echo "" elif echo "$upgrade_output" | grep -q "Error:"; then log_error "Homebrew upgrade failed" echo "$upgrade_output" | grep "Error:" >&2 return 1 else echo "$upgrade_output" | grep -Ev "^(==>|Updating Homebrew|Warning:)" || true local new_version new_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}') echo "" echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-$current_version})" echo "" fi # Clear update cache (suppress errors if cache doesn't exist or is locked) rm -f "$HOME/.cache/mole/version_check" "$HOME/.cache/mole/update_message" 2> /dev/null || true } # Remove applications from Dock remove_apps_from_dock() { if [[ $# -eq 0 ]]; then return 0 fi local -a targets=() for arg in "$@"; do [[ -n "$arg" ]] && targets+=("$arg") done if [[ ${#targets[@]} -eq 0 ]]; then return 0 fi # Use pure shell (PlistBuddy) to remove items from Dock # This avoids dependencies on Python 3 or osascript (AppleScript) local plist="$HOME/Library/Preferences/com.apple.dock.plist" [[ -f "$plist" ]] || return 0 # PlistBuddy is at /usr/libexec/PlistBuddy on macOS [[ -x /usr/libexec/PlistBuddy ]] || return 0 local changed=false for target in "${targets[@]}"; do local app_path="$target" local full_path="" if [[ "$app_path" =~ [[:cntrl:]] ]]; then debug_log "Skipping dock removal for path with control chars: $app_path" continue fi if [[ -e "$app_path" ]]; then if full_path=$(cd "$(dirname "$app_path")" 2> /dev/null && pwd); then full_path="$full_path/$(basename "$app_path")" else continue fi else case "$app_path" in ~/*) full_path="$HOME/${app_path#~/}" ;; /*) full_path="$app_path" ;; *) continue ;; esac fi [[ -z "$full_path" ]] && continue local encoded_path="${full_path// /%20}" # Find the index of the app in persistent-apps local i=0 while true; do local label label=$(/usr/libexec/PlistBuddy -c "Print :persistent-apps:$i:tile-data:file-label" "$plist" 2> /dev/null || echo "") [[ -z "$label" ]] && break local url url=$(/usr/libexec/PlistBuddy -c "Print :persistent-apps:$i:tile-data:file-data:_CFURLString" "$plist" 2> /dev/null || echo "") [[ -z "$url" ]] && { ((i++)) continue } # Match by URL-encoded path to handle spaces in app names if [[ -n "$encoded_path" && "$url" == *"$encoded_path"* ]]; then if /usr/libexec/PlistBuddy -c "Delete :persistent-apps:$i" "$plist" 2> /dev/null; then changed=true # After deletion, current index i now points to the next item continue fi fi ((i++)) done done if [[ "$changed" == "true" ]]; then # Restart Dock to apply changes from the plist killall Dock 2> /dev/null || true fi }