mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 15:04:42 +00:00
style: standardize punctuation across codebase
- Replace parentheses with commas for supplementary info - Use commas instead of em-dashes for separators - Update bullet points from - to * in some contexts - Improve version extraction regex with fallback logic
This commit is contained in:
@@ -39,7 +39,7 @@ brew install mole
|
|||||||
curl -fsSL https://raw.githubusercontent.com/tw93/mole/main/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/tw93/mole/main/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows:** Mole is designed for macOS, but we offer an experimental Windows version based on user demand. See the [windows branch](https://github.com/tw93/Mole/tree/windows) — for early adopters only.
|
**Windows:** Mole is designed for macOS, but we offer an experimental Windows version based on user demand. See the [windows branch](https://github.com/tw93/Mole/tree/windows), for early adopters only.
|
||||||
|
|
||||||
**Run:**
|
**Run:**
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ Select Categories to Clean - 18.5GB (8 selected)
|
|||||||
● backend-service 2.5GB | node_modules
|
● backend-service 2.5GB | node_modules
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Use with caution:** This will permanently delete selected artifacts. Review carefully before confirming. Recent projects — less than 7 days old — are marked and unselected by default.
|
> **Use with caution:** This will permanently delete selected artifacts. Review carefully before confirming. Recent projects, less than 7 days old, are marked and unselected by default.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>Custom Scan Paths</strong></summary>
|
<summary><strong>Custom Scan Paths</strong></summary>
|
||||||
@@ -282,4 +282,4 @@ Join thousands of users worldwide who trust Mole to keep their Macs clean and op
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License — feel free to enjoy and participate in open source.
|
MIT License, feel free to enjoy and participate in open source.
|
||||||
|
|||||||
22
bin/clean.sh
22
bin/clean.sh
@@ -381,7 +381,7 @@ safe_clean() {
|
|||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_log "Cleaning: $description (${#existing_paths[@]} items)"
|
debug_log "Cleaning: $description, ${#existing_paths[@]} items"
|
||||||
|
|
||||||
# Enhanced debug output with risk level and details
|
# Enhanced debug output with risk level and details
|
||||||
if [[ "${MO_DEBUG:-}" == "1" && ${#existing_paths[@]} -gt 0 ]]; then
|
if [[ "${MO_DEBUG:-}" == "1" && ${#existing_paths[@]} -gt 0 ]]; then
|
||||||
@@ -612,7 +612,7 @@ safe_clean() {
|
|||||||
debug_log "Permission denied while cleaning: $description"
|
debug_log "Permission denied while cleaning: $description"
|
||||||
fi
|
fi
|
||||||
if [[ $removal_failed_count -gt 0 && "$DRY_RUN" != "true" ]]; then
|
if [[ $removal_failed_count -gt 0 && "$DRY_RUN" != "true" ]]; then
|
||||||
debug_log "Skipped $removal_failed_count items (permission denied or in use) for: $description"
|
debug_log "Skipped $removal_failed_count items, permission denied or in use, for: $description"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $removed_any -eq 1 ]]; then
|
if [[ $removed_any -eq 1 ]]; then
|
||||||
@@ -627,7 +627,7 @@ safe_clean() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $label ${YELLOW}($size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $label${NC}, ${YELLOW}$size_human dry${NC}"
|
||||||
|
|
||||||
local paths_temp=$(create_temp_file)
|
local paths_temp=$(create_temp_file)
|
||||||
|
|
||||||
@@ -678,7 +678,7 @@ safe_clean() {
|
|||||||
' | while IFS='|' read -r display_path total_size child_count; do
|
' | while IFS='|' read -r display_path total_size child_count; do
|
||||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ $child_count -gt 1 ]]; then
|
if [[ $child_count -gt 1 ]]; then
|
||||||
echo "$display_path # $size_human ($child_count items)" >> "$EXPORT_LIST_FILE"
|
echo "$display_path # $size_human, $child_count items" >> "$EXPORT_LIST_FILE"
|
||||||
else
|
else
|
||||||
echo "$display_path # $size_human" >> "$EXPORT_LIST_FILE"
|
echo "$display_path # $size_human" >> "$EXPORT_LIST_FILE"
|
||||||
fi
|
fi
|
||||||
@@ -687,7 +687,7 @@ safe_clean() {
|
|||||||
rm -f "$paths_temp"
|
rm -f "$paths_temp"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label${NC}, ${GREEN}$size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += total_count))
|
((files_cleaned += total_count))
|
||||||
((total_size_cleaned += total_size_kb))
|
((total_size_cleaned += total_size_kb))
|
||||||
@@ -711,7 +711,7 @@ start_cleanup() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e "${YELLOW}Dry Run Mode${NC} - Preview only, no deletions"
|
echo -e "${YELLOW}Dry Run Mode${NC}, Preview only, no deletions"
|
||||||
echo ""
|
echo ""
|
||||||
SYSTEM_CLEAN=false
|
SYSTEM_CLEAN=false
|
||||||
|
|
||||||
@@ -737,7 +737,7 @@ EOF
|
|||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Admin access already available"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Admin access already available"
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} System caches need sudo — ${GREEN}Enter${NC} continue, ${GRAY}Space${NC} skip: "
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} System caches need sudo. ${GREEN}Enter${NC} continue, ${GRAY}Space${NC} skip: "
|
||||||
|
|
||||||
local choice
|
local choice
|
||||||
choice=$(read_key)
|
choice=$(read_key)
|
||||||
@@ -774,10 +774,10 @@ EOF
|
|||||||
echo "Running in non-interactive mode"
|
echo "Running in non-interactive mode"
|
||||||
if sudo -n true 2> /dev/null; then
|
if sudo -n true 2> /dev/null; then
|
||||||
SYSTEM_CLEAN=true
|
SYSTEM_CLEAN=true
|
||||||
echo " ${ICON_LIST} System-level cleanup enabled (sudo session active)"
|
echo " ${ICON_LIST} System-level cleanup enabled, sudo session active"
|
||||||
else
|
else
|
||||||
SYSTEM_CLEAN=false
|
SYSTEM_CLEAN=false
|
||||||
echo " ${ICON_LIST} System-level cleanup skipped (requires sudo)"
|
echo " ${ICON_LIST} System-level cleanup skipped, requires sudo"
|
||||||
fi
|
fi
|
||||||
echo " ${ICON_LIST} User-level cleanup will proceed automatically"
|
echo " ${ICON_LIST} User-level cleanup will proceed automatically"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -790,7 +790,7 @@ perform_cleanup() {
|
|||||||
if [[ "${MOLE_TEST_MODE:-0}" == "1" ]]; then
|
if [[ "${MOLE_TEST_MODE:-0}" == "1" ]]; then
|
||||||
test_mode_enabled=true
|
test_mode_enabled=true
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e "${YELLOW}Dry Run Mode${NC} - Preview only, no deletions"
|
echo -e "${YELLOW}Dry Run Mode${NC}, Preview only, no deletions"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
echo -e "${GREEN}${ICON_LIST}${NC} User app cache"
|
echo -e "${GREEN}${ICON_LIST}${NC} User app cache"
|
||||||
@@ -1054,7 +1054,7 @@ perform_cleanup() {
|
|||||||
else
|
else
|
||||||
summary_status="info"
|
summary_status="info"
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
summary_details+=("No significant reclaimable space detected (system already clean).")
|
summary_details+=("No significant reclaimable space detected, system already clean.")
|
||||||
else
|
else
|
||||||
summary_details+=("System was already clean; no additional space freed.")
|
summary_details+=("System was already clean; no additional space freed.")
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ if [[ $# -eq 0 ]]; then
|
|||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Removed stale completion entries from $config_file"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Removed stale completion entries from $config_file"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
log_error "mole not found in PATH - install Mole before enabling completion"
|
log_error "mole not found in PATH, install Mole before enabling completion"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ select_installers() {
|
|||||||
scroll_indicator=" ${GRAY}[${current_pos}/${total_items}]${NC}"
|
scroll_indicator=" ${GRAY}[${current_pos}/${total_items}]${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "${PURPLE_BOLD}Select Installers to Remove${NC}%s ${GRAY}- ${selected_human} ($selected_count selected)${NC}\n" "$scroll_indicator"
|
printf "${PURPLE_BOLD}Select Installers to Remove${NC}%s ${GRAY}, ${selected_human}, ${selected_count} selected${NC}\n" "$scroll_indicator"
|
||||||
printf "%s\n" "$clear_line"
|
printf "%s\n" "$clear_line"
|
||||||
|
|
||||||
# Calculate visible range
|
# Calculate visible range
|
||||||
@@ -546,13 +546,13 @@ delete_selected_installers() {
|
|||||||
local file_size="${INSTALLER_SIZES[$idx]}"
|
local file_size="${INSTALLER_SIZES[$idx]}"
|
||||||
local size_human
|
local size_human
|
||||||
size_human=$(bytes_to_human "$file_size")
|
size_human=$(bytes_to_human "$file_size")
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $(basename "$file_path") ${GRAY}(${size_human})${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $(basename "$file_path") ${GRAY}, ${size_human}${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Confirm deletion
|
# Confirm deletion
|
||||||
echo ""
|
echo ""
|
||||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} Delete ${#selected_indices[@]} installer(s) (${confirm_human}) ${GREEN}Enter${NC} confirm, ${GRAY}ESC${NC} cancel: "
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} Delete ${#selected_indices[@]} installers, ${confirm_human} ${GREEN}Enter${NC} confirm, ${GRAY}ESC${NC} cancel: "
|
||||||
|
|
||||||
IFS= read -r -s -n1 confirm || confirm=""
|
IFS= read -r -s -n1 confirm || confirm=""
|
||||||
case "$confirm" in
|
case "$confirm" in
|
||||||
@@ -655,7 +655,7 @@ show_summary() {
|
|||||||
local freed_mb
|
local freed_mb
|
||||||
freed_mb=$(echo "$total_size_freed_kb" | awk '{printf "%.2f", $1/1024}')
|
freed_mb=$(echo "$total_size_freed_kb" | awk '{printf "%.2f", $1/1024}')
|
||||||
|
|
||||||
summary_details+=("Removed ${GREEN}$total_deleted${NC} installer(s), freed ${GREEN}${freed_mb}MB${NC}")
|
summary_details+=("Removed ${GREEN}$total_deleted${NC} installers, freed ${GREEN}${freed_mb}MB${NC}")
|
||||||
summary_details+=("Your Mac is cleaner now!")
|
summary_details+=("Your Mac is cleaner now!")
|
||||||
else
|
else
|
||||||
summary_details+=("No installers were removed")
|
summary_details+=("No installers were removed")
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ show_optimization_summary() {
|
|||||||
local total_applied=$((safe_count + confirm_count))
|
local total_applied=$((safe_count + confirm_count))
|
||||||
|
|
||||||
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
summary_title="Dry Run Complete - No Changes Made"
|
summary_title="Dry Run Complete, No Changes Made"
|
||||||
summary_details+=("Would apply ${YELLOW}${total_applied:-0}${NC} optimizations")
|
summary_details+=("Would apply ${YELLOW}${total_applied:-0}${NC} optimizations")
|
||||||
summary_details+=("Run without ${YELLOW}--dry-run${NC} to apply these changes")
|
summary_details+=("Run without ${YELLOW}--dry-run${NC} to apply these changes")
|
||||||
else
|
else
|
||||||
@@ -115,9 +115,9 @@ show_optimization_summary() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$key_stat" ]]; then
|
if [[ -n "$key_stat" ]]; then
|
||||||
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations — ${key_stat}")
|
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations, ${key_stat}")
|
||||||
else
|
else
|
||||||
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations — all services tuned")
|
summary_details+=("Applied ${GREEN}${total_applied:-0}${NC} optimizations, all services tuned")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local summary_line3=""
|
local summary_line3=""
|
||||||
@@ -126,11 +126,11 @@ show_optimization_summary() {
|
|||||||
if [[ -n "${AUTO_FIX_DETAILS:-}" ]]; then
|
if [[ -n "${AUTO_FIX_DETAILS:-}" ]]; then
|
||||||
local detail_join
|
local detail_join
|
||||||
detail_join=$(echo "${AUTO_FIX_DETAILS}" | paste -sd ", " -)
|
detail_join=$(echo "${AUTO_FIX_DETAILS}" | paste -sd ", " -)
|
||||||
[[ -n "$detail_join" ]] && summary_line3+=" — ${detail_join}"
|
[[ -n "$detail_join" ]] && summary_line3+=": ${detail_join}"
|
||||||
fi
|
fi
|
||||||
summary_details+=("$summary_line3")
|
summary_details+=("$summary_line3")
|
||||||
fi
|
fi
|
||||||
summary_details+=("System fully optimized — faster, more secure and responsive")
|
summary_details+=("System fully optimized")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_summary_block "$summary_title" "${summary_details[@]}"
|
print_summary_block "$summary_title" "${summary_details[@]}"
|
||||||
@@ -226,12 +226,12 @@ cleanup_path() {
|
|||||||
|
|
||||||
if [[ "$removed" == "true" ]]; then
|
if [[ "$removed" == "true" ]]; then
|
||||||
if [[ -n "$size_display" ]]; then
|
if [[ -n "$size_display" ]]; then
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label ${GREEN}(${size_display})${NC}"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label${NC}, ${GREEN}${size_display}${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $label"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${GRAY}${ICON_WARNING}${NC} Skipped $label ${GRAY}(grant Full Disk Access to your terminal and retry)${NC}"
|
echo -e "${GRAY}${ICON_WARNING}${NC} Skipped $label${GRAY}, grant Full Disk Access to your terminal and retry${NC}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ collect_security_fix_actions() {
|
|||||||
fi
|
fi
|
||||||
if [[ "${GATEKEEPER_DISABLED:-}" == "true" ]]; then
|
if [[ "${GATEKEEPER_DISABLED:-}" == "true" ]]; then
|
||||||
if ! is_whitelisted "gatekeeper"; then
|
if ! is_whitelisted "gatekeeper"; then
|
||||||
SECURITY_FIXES+=("gatekeeper|Enable Gatekeeper (App download protection)")
|
SECURITY_FIXES+=("gatekeeper|Enable Gatekeeper, app download protection")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if touchid_supported && ! touchid_configured; then
|
if touchid_supported && ! touchid_configured; then
|
||||||
@@ -304,7 +304,7 @@ apply_firewall_fix() {
|
|||||||
FIREWALL_DISABLED=false
|
FIREWALL_DISABLED=false
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
echo -e " ${GRAY}${ICON_WARNING}${NC} Failed to enable firewall (check permissions)"
|
echo -e " ${GRAY}${ICON_WARNING}${NC} Failed to enable firewall, check permissions"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ apply_touchid_fix() {
|
|||||||
|
|
||||||
perform_security_fixes() {
|
perform_security_fixes() {
|
||||||
if ! ensure_sudo_session "Security changes require admin access"; then
|
if ! ensure_sudo_session "Security changes require admin access"; then
|
||||||
echo -e "${GRAY}${ICON_WARNING}${NC} Skipped security fixes (sudo denied)"
|
echo -e "${GRAY}${ICON_WARNING}${NC} Skipped security fixes, sudo denied"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -391,7 +391,7 @@ main() {
|
|||||||
|
|
||||||
# Dry-run indicator.
|
# Dry-run indicator.
|
||||||
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
if [[ "${MOLE_DRY_RUN:-0}" == "1" ]]; then
|
||||||
echo -e "${YELLOW}${ICON_DRY_RUN} DRY RUN MODE${NC} - No files will be modified\n"
|
echo -e "${YELLOW}${ICON_DRY_RUN} DRY RUN MODE${NC}, No files will be modified\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v jq > /dev/null 2>&1; then
|
if ! command -v jq > /dev/null 2>&1; then
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ perform_purge() {
|
|||||||
|
|
||||||
# Show help message
|
# Show help message
|
||||||
show_help() {
|
show_help() {
|
||||||
echo -e "${PURPLE_BOLD}Mole Purge${NC} - Clean old project build artifacts"
|
echo -e "${PURPLE_BOLD}Mole Purge${NC}, Clean old project build artifacts"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}Usage:${NC} mo purge [options]"
|
echo -e "${YELLOW}Usage:${NC} mo purge [options]"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -231,7 +231,7 @@ show_help() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}Default Paths:${NC}"
|
echo -e "${YELLOW}Default Paths:${NC}"
|
||||||
for path in "${DEFAULT_PURGE_SEARCH_PATHS[@]}"; do
|
for path in "${DEFAULT_PURGE_SEARCH_PATHS[@]}"; do
|
||||||
echo " - $path"
|
echo " * $path"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ enable_touchid() {
|
|||||||
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
||||||
log_success "Touch ID migrated to sudo_local"
|
log_success "Touch ID migrated to sudo_local"
|
||||||
else
|
else
|
||||||
log_success "Touch ID enabled (via sudo_local) - try: sudo ls"
|
log_success "Touch ID enabled, via sudo_local, try: sudo ls"
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@@ -188,7 +188,7 @@ enable_touchid() {
|
|||||||
|
|
||||||
# Apply the changes
|
# Apply the changes
|
||||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2> /dev/null; then
|
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2> /dev/null; then
|
||||||
log_success "Touch ID enabled - try: sudo ls"
|
log_success "Touch ID enabled, try: sudo ls"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Failed to enable Touch ID"
|
log_error "Failed to enable Touch ID"
|
||||||
@@ -219,7 +219,7 @@ disable_touchid() {
|
|||||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
||||||
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
||||||
fi
|
fi
|
||||||
echo -e "${GREEN}${ICON_SUCCESS} Touch ID disabled (removed from sudo_local)${NC}"
|
echo -e "${GREEN}${ICON_SUCCESS} Touch ID disabled, removed from sudo_local${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ main() {
|
|||||||
rm -f "$apps_file"
|
rm -f "$apps_file"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
echo -e "${BLUE}${ICON_CONFIRM}${NC} Selected ${selection_count} app(s):"
|
echo -e "${BLUE}${ICON_CONFIRM}${NC} Selected ${selection_count} apps:"
|
||||||
local -a summary_rows=()
|
local -a summary_rows=()
|
||||||
local max_name_display_width=0
|
local max_name_display_width=0
|
||||||
local max_size_width=0
|
local max_size_width=0
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ main() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
# Show selected apps with clean alignment
|
# Show selected apps with clean alignment
|
||||||
echo -e "${BLUE}${ICON_CONFIRM}${NC} Selected ${selection_count} app(s):"
|
echo -e "${BLUE}${ICON_CONFIRM}${NC} Selected ${selection_count} apps:"
|
||||||
local -a summary_rows=()
|
local -a summary_rows=()
|
||||||
local max_name_width=0
|
local max_name_width=0
|
||||||
local max_size_width=0
|
local max_size_width=0
|
||||||
|
|||||||
@@ -332,9 +332,9 @@ func (m *model) scheduleOverviewScans() tea.Cmd {
|
|||||||
if len(pendingIndices) > 0 {
|
if len(pendingIndices) > 0 {
|
||||||
firstEntry := m.entries[pendingIndices[0]]
|
firstEntry := m.entries[pendingIndices[0]]
|
||||||
if len(pendingIndices) == 1 {
|
if len(pendingIndices) == 1 {
|
||||||
m.status = fmt.Sprintf("Scanning %s... (%d left)", firstEntry.Name, remaining)
|
m.status = fmt.Sprintf("Scanning %s..., %d left", firstEntry.Name, remaining)
|
||||||
} else {
|
} else {
|
||||||
m.status = fmt.Sprintf("Scanning %d directories... (%d left)", len(pendingIndices), remaining)
|
m.status = fmt.Sprintf("Scanning %d directories..., %d left", len(pendingIndices), remaining)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,7 +736,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
if len(m.largeMultiSelected) > 0 {
|
if len(m.largeMultiSelected) > 0 {
|
||||||
count := len(m.largeMultiSelected)
|
count := len(m.largeMultiSelected)
|
||||||
if count > maxBatchOpen {
|
if count > maxBatchOpen {
|
||||||
m.status = fmt.Sprintf("Too many items to open (max %d, selected %d)", maxBatchOpen, count)
|
m.status = fmt.Sprintf("Too many items to open, max %d, selected %d", maxBatchOpen, count)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
for path := range m.largeMultiSelected {
|
for path := range m.largeMultiSelected {
|
||||||
@@ -761,7 +761,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
if len(m.multiSelected) > 0 {
|
if len(m.multiSelected) > 0 {
|
||||||
count := len(m.multiSelected)
|
count := len(m.multiSelected)
|
||||||
if count > maxBatchOpen {
|
if count > maxBatchOpen {
|
||||||
m.status = fmt.Sprintf("Too many items to open (max %d, selected %d)", maxBatchOpen, count)
|
m.status = fmt.Sprintf("Too many items to open, max %d, selected %d", maxBatchOpen, count)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
for path := range m.multiSelected {
|
for path := range m.multiSelected {
|
||||||
@@ -790,7 +790,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
if len(m.largeMultiSelected) > 0 {
|
if len(m.largeMultiSelected) > 0 {
|
||||||
count := len(m.largeMultiSelected)
|
count := len(m.largeMultiSelected)
|
||||||
if count > maxBatchReveal {
|
if count > maxBatchReveal {
|
||||||
m.status = fmt.Sprintf("Too many items to reveal (max %d, selected %d)", maxBatchReveal, count)
|
m.status = fmt.Sprintf("Too many items to reveal, max %d, selected %d", maxBatchReveal, count)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
for path := range m.largeMultiSelected {
|
for path := range m.largeMultiSelected {
|
||||||
@@ -815,7 +815,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
if len(m.multiSelected) > 0 {
|
if len(m.multiSelected) > 0 {
|
||||||
count := len(m.multiSelected)
|
count := len(m.multiSelected)
|
||||||
if count > maxBatchReveal {
|
if count > maxBatchReveal {
|
||||||
m.status = fmt.Sprintf("Too many items to reveal (max %d, selected %d)", maxBatchReveal, count)
|
m.status = fmt.Sprintf("Too many items to reveal, max %d, selected %d", maxBatchReveal, count)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
for path := range m.multiSelected {
|
for path := range m.multiSelected {
|
||||||
@@ -860,7 +860,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.status = fmt.Sprintf("%d selected (%s)", count, humanizeBytes(totalSize))
|
m.status = fmt.Sprintf("%d selected, %s", count, humanizeBytes(totalSize))
|
||||||
} else {
|
} else {
|
||||||
m.status = fmt.Sprintf("Scanned %s", humanizeBytes(m.totalSize))
|
m.status = fmt.Sprintf("Scanned %s", humanizeBytes(m.totalSize))
|
||||||
}
|
}
|
||||||
@@ -886,7 +886,7 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.status = fmt.Sprintf("%d selected (%s)", count, humanizeBytes(totalSize))
|
m.status = fmt.Sprintf("%d selected, %s", count, humanizeBytes(totalSize))
|
||||||
} else {
|
} else {
|
||||||
m.status = fmt.Sprintf("Scanned %s", humanizeBytes(m.totalSize))
|
m.status = fmt.Sprintf("Scanned %s", humanizeBytes(m.totalSize))
|
||||||
}
|
}
|
||||||
@@ -1011,7 +1011,7 @@ func (m model) enterSelectedDir() (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
return m, tea.Batch(m.scanCmd(m.path), tickCmd())
|
return m, tea.Batch(m.scanCmd(m.path), tickCmd())
|
||||||
}
|
}
|
||||||
m.status = fmt.Sprintf("File: %s (%s)", selected.Name, humanizeBytes(selected.Size))
|
m.status = fmt.Sprintf("File: %s, %s", selected.Name, humanizeBytes(selected.Size))
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -594,7 +594,7 @@ func getDirectorySizeFromDuWithExclude(path string, excludePath string) (int64,
|
|||||||
return 0, fmt.Errorf("du timeout after %v", duTimeout)
|
return 0, fmt.Errorf("du timeout after %v", duTimeout)
|
||||||
}
|
}
|
||||||
if stderr.Len() > 0 {
|
if stderr.Len() > 0 {
|
||||||
return 0, fmt.Errorf("du failed: %v (%s)", err, stderr.String())
|
return 0, fmt.Errorf("du failed: %v, %s", err, stderr.String())
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("du failed: %v", err)
|
return 0, fmt.Errorf("du failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (m model) View() string {
|
|||||||
if m.scanning && percent >= 100 {
|
if m.scanning && percent >= 100 {
|
||||||
percent = 99
|
percent = 99
|
||||||
}
|
}
|
||||||
progressPrefix = fmt.Sprintf(" %s(%.0f%%)%s", colorCyan, percent, colorReset)
|
progressPrefix = fmt.Sprintf(" %s%.0f%%%s", colorCyan, percent, colorReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(&b, "%s%s%s%s Scanning%s: %s%s files%s, %s%s dirs%s, %s%s%s\n",
|
fmt.Fprintf(&b, "%s%s%s%s Scanning%s: %s%s files%s, %s%s dirs%s, %s%s%s\n",
|
||||||
@@ -342,7 +342,7 @@ func (m model) View() string {
|
|||||||
} else if m.showLargeFiles {
|
} else if m.showLargeFiles {
|
||||||
selectCount := len(m.largeMultiSelected)
|
selectCount := len(m.largeMultiSelected)
|
||||||
if selectCount > 0 {
|
if selectCount > 0 {
|
||||||
fmt.Fprintf(&b, "%s↑↓← | Space Select | R Refresh | O Open | F File | ⌫ Del(%d) | ← Back | Q Quit%s\n", colorGray, selectCount, colorReset)
|
fmt.Fprintf(&b, "%s↑↓← | Space Select | R Refresh | O Open | F File | ⌫ Del %d | ← Back | Q Quit%s\n", colorGray, selectCount, colorReset)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "%s↑↓← | Space Select | R Refresh | O Open | F File | ⌫ Del | ← Back | Q Quit%s\n", colorGray, colorReset)
|
fmt.Fprintf(&b, "%s↑↓← | Space Select | R Refresh | O Open | F File | ⌫ Del | ← Back | Q Quit%s\n", colorGray, colorReset)
|
||||||
}
|
}
|
||||||
@@ -351,13 +351,13 @@ func (m model) View() string {
|
|||||||
selectCount := len(m.multiSelected)
|
selectCount := len(m.multiSelected)
|
||||||
if selectCount > 0 {
|
if selectCount > 0 {
|
||||||
if largeFileCount > 0 {
|
if largeFileCount > 0 {
|
||||||
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del(%d) | T Top(%d) | Q Quit%s\n", colorGray, selectCount, largeFileCount, colorReset)
|
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del %d | T Top %d | Q Quit%s\n", colorGray, selectCount, largeFileCount, colorReset)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del(%d) | Q Quit%s\n", colorGray, selectCount, colorReset)
|
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del %d | Q Quit%s\n", colorGray, selectCount, colorReset)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if largeFileCount > 0 {
|
if largeFileCount > 0 {
|
||||||
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del | T Top(%d) | Q Quit%s\n", colorGray, largeFileCount, colorReset)
|
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del | T Top %d | Q Quit%s\n", colorGray, largeFileCount, colorReset)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del | Q Quit%s\n", colorGray, colorReset)
|
fmt.Fprintf(&b, "%s↑↓←→ | Space Select | Enter | R Refresh | O Open | F File | ⌫ Del | Q Quit%s\n", colorGray, colorReset)
|
||||||
}
|
}
|
||||||
@@ -390,12 +390,12 @@ func (m model) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if deleteCount > 1 {
|
if deleteCount > 1 {
|
||||||
fmt.Fprintf(&b, "%sDelete:%s %d items (%s) %sPress Enter to confirm | ESC cancel%s\n",
|
fmt.Fprintf(&b, "%sDelete:%s %d items, %s %sPress Enter to confirm | ESC cancel%s\n",
|
||||||
colorRed, colorReset,
|
colorRed, colorReset,
|
||||||
deleteCount, humanizeBytes(totalDeleteSize),
|
deleteCount, humanizeBytes(totalDeleteSize),
|
||||||
colorGray, colorReset)
|
colorGray, colorReset)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "%sDelete:%s %s (%s) %sPress Enter to confirm | ESC cancel%s\n",
|
fmt.Fprintf(&b, "%sDelete:%s %s, %s %sPress Enter to confirm | ESC cancel%s\n",
|
||||||
colorRed, colorReset,
|
colorRed, colorReset,
|
||||||
m.deleteTarget.Name, humanizeBytes(m.deleteTarget.Size),
|
m.deleteTarget.Name, humanizeBytes(m.deleteTarget.Size),
|
||||||
colorGray, colorReset)
|
colorGray, colorReset)
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func renderHeader(m MetricsSnapshot, errMsg string, animFrame int, termWidth int
|
|||||||
cpuInfo := m.Hardware.CPUModel
|
cpuInfo := m.Hardware.CPUModel
|
||||||
// Append GPU core count when available.
|
// Append GPU core count when available.
|
||||||
if len(m.GPU) > 0 && m.GPU[0].CoreCount > 0 {
|
if len(m.GPU) > 0 && m.GPU[0].CoreCount > 0 {
|
||||||
cpuInfo += fmt.Sprintf(" (%dGPU)", m.GPU[0].CoreCount)
|
cpuInfo += fmt.Sprintf(", %dGPU", m.GPU[0].CoreCount)
|
||||||
}
|
}
|
||||||
infoParts = append(infoParts, cpuInfo)
|
infoParts = append(infoParts, cpuInfo)
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ func renderCPUCard(cpu CPUStatus, thermal ThermalStatus) cardData {
|
|||||||
lines = append(lines, fmt.Sprintf("Total %s %s", usageBar, headerText))
|
lines = append(lines, fmt.Sprintf("Total %s %s", usageBar, headerText))
|
||||||
|
|
||||||
if cpu.PerCoreEstimated {
|
if cpu.PerCoreEstimated {
|
||||||
lines = append(lines, subtleStyle.Render("Per-core data unavailable (using averaged load)"))
|
lines = append(lines, subtleStyle.Render("Per-core data unavailable, using averaged load"))
|
||||||
} else if len(cpu.PerCore) > 0 {
|
} else if len(cpu.PerCore) > 0 {
|
||||||
type coreUsage struct {
|
type coreUsage struct {
|
||||||
idx int
|
idx int
|
||||||
@@ -239,10 +239,10 @@ func renderCPUCard(cpu CPUStatus, thermal ThermalStatus) cardData {
|
|||||||
|
|
||||||
// Load line at the end
|
// Load line at the end
|
||||||
if cpu.PCoreCount > 0 && cpu.ECoreCount > 0 {
|
if cpu.PCoreCount > 0 && cpu.ECoreCount > 0 {
|
||||||
lines = append(lines, fmt.Sprintf("Load %.2f / %.2f / %.2f (%dP+%dE)",
|
lines = append(lines, fmt.Sprintf("Load %.2f / %.2f / %.2f, %dP+%dE",
|
||||||
cpu.Load1, cpu.Load5, cpu.Load15, cpu.PCoreCount, cpu.ECoreCount))
|
cpu.Load1, cpu.Load5, cpu.Load15, cpu.PCoreCount, cpu.ECoreCount))
|
||||||
} else {
|
} else {
|
||||||
lines = append(lines, fmt.Sprintf("Load %.2f / %.2f / %.2f (%d cores)",
|
lines = append(lines, fmt.Sprintf("Load %.2f / %.2f / %.2f, %d cores",
|
||||||
cpu.Load1, cpu.Load5, cpu.Load15, cpu.LogicalCPU))
|
cpu.Load1, cpu.Load5, cpu.Load15, cpu.LogicalCPU))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ func renderMemoryCard(mem MemoryStatus) cardData {
|
|||||||
if mem.SwapTotal > 0 {
|
if mem.SwapTotal > 0 {
|
||||||
swapPercent = (float64(mem.SwapUsed) / float64(mem.SwapTotal)) * 100.0
|
swapPercent = (float64(mem.SwapUsed) / float64(mem.SwapTotal)) * 100.0
|
||||||
}
|
}
|
||||||
swapText := fmt.Sprintf("(%s/%s)", humanBytesCompact(mem.SwapUsed), humanBytesCompact(mem.SwapTotal))
|
swapText := fmt.Sprintf("%s/%s", humanBytesCompact(mem.SwapUsed), humanBytesCompact(mem.SwapTotal))
|
||||||
lines = append(lines, fmt.Sprintf("Swap %s %5.1f%% %s", progressBar(swapPercent), swapPercent, swapText))
|
lines = append(lines, fmt.Sprintf("Swap %s %5.1f%% %s", progressBar(swapPercent), swapPercent, swapText))
|
||||||
|
|
||||||
lines = append(lines, fmt.Sprintf("Total %s / %s", humanBytes(mem.Used), humanBytes(mem.Total)))
|
lines = append(lines, fmt.Sprintf("Total %s / %s", humanBytes(mem.Used), humanBytes(mem.Total)))
|
||||||
@@ -361,7 +361,7 @@ func formatDiskLine(label string, d DiskStatus) string {
|
|||||||
bar := progressBar(d.UsedPercent)
|
bar := progressBar(d.UsedPercent)
|
||||||
used := humanBytesShort(d.Used)
|
used := humanBytesShort(d.Used)
|
||||||
total := humanBytesShort(d.Total)
|
total := humanBytesShort(d.Total)
|
||||||
return fmt.Sprintf("%-6s %s %5.1f%% (%s/%s)", label, bar, d.UsedPercent, used, total)
|
return fmt.Sprintf("%-6s %s %5.1f%%, %s/%s", label, bar, d.UsedPercent, used, total)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ioBar(rate float64) string {
|
func ioBar(rate float64) string {
|
||||||
|
|||||||
12
install.sh
12
install.sh
@@ -165,7 +165,7 @@ resolve_source_dir() {
|
|||||||
url="https://github.com/tw93/mole/archive/refs/tags/${branch}.tar.gz"
|
url="https://github.com/tw93/mole/archive/refs/tags/${branch}.tar.gz"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
start_line_spinner "Fetching Mole source (${branch})..."
|
start_line_spinner "Fetching Mole source, ${branch}..."
|
||||||
if command -v curl > /dev/null 2>&1; then
|
if command -v curl > /dev/null 2>&1; then
|
||||||
if curl -fsSL --connect-timeout 10 --max-time 60 -o "$tmp/mole.tar.gz" "$url" 2> /dev/null; then
|
if curl -fsSL --connect-timeout 10 --max-time 60 -o "$tmp/mole.tar.gz" "$url" 2> /dev/null; then
|
||||||
if tar -xzf "$tmp/mole.tar.gz" -C "$tmp" 2> /dev/null; then
|
if tar -xzf "$tmp/mole.tar.gz" -C "$tmp" 2> /dev/null; then
|
||||||
@@ -509,7 +509,7 @@ download_binary() {
|
|||||||
log_success "Downloaded ${binary_name} binary"
|
log_success "Downloaded ${binary_name} binary"
|
||||||
else
|
else
|
||||||
if [[ -t 1 ]]; then stop_line_spinner; fi
|
if [[ -t 1 ]]; then stop_line_spinner; fi
|
||||||
log_warning "Could not download ${binary_name} binary (v${version}), trying local build"
|
log_warning "Could not download ${binary_name} binary, v${version}, trying local build"
|
||||||
if build_binary_from_source "$binary_name" "$target_path"; then
|
if build_binary_from_source "$binary_name" "$target_path"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -659,9 +659,9 @@ print_usage_summary() {
|
|||||||
local message="Mole ${action} successfully"
|
local message="Mole ${action} successfully"
|
||||||
|
|
||||||
if [[ "$action" == "updated" && -n "$previous_version" && -n "$new_version" && "$previous_version" != "$new_version" ]]; then
|
if [[ "$action" == "updated" && -n "$previous_version" && -n "$new_version" && "$previous_version" != "$new_version" ]]; then
|
||||||
message+=" (${previous_version} -> ${new_version})"
|
message+=", ${previous_version} -> ${new_version}"
|
||||||
elif [[ -n "$new_version" ]]; then
|
elif [[ -n "$new_version" ]]; then
|
||||||
message+=" (version ${new_version})"
|
message+=", version ${new_version}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_confirm "$message"
|
log_confirm "$message"
|
||||||
@@ -763,7 +763,7 @@ perform_update() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$installed_version" == "$target_version" ]]; then
|
if [[ "$installed_version" == "$target_version" ]]; then
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version ($installed_version)"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version, $installed_version"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -794,7 +794,7 @@ perform_update() {
|
|||||||
updated_version="$target_version"
|
updated_version="$target_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version ($updated_version)"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, $updated_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ clean_ds_store_tree() {
|
|||||||
local size_human
|
local size_human
|
||||||
size_human=$(bytes_to_human "$total_bytes")
|
size_human=$(bytes_to_human "$total_bytes")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $label ${YELLOW}($file_count files, $size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $label${NC}, ${YELLOW}$file_count files, $size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label ${GREEN}($file_count files, $size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $label${NC}, ${GREEN}$file_count files, $size_human${NC}"
|
||||||
fi
|
fi
|
||||||
local size_kb=$(((total_bytes + 1023) / 1024))
|
local size_kb=$(((total_bytes + 1023) / 1024))
|
||||||
((files_cleaned += file_count))
|
((files_cleaned += file_count))
|
||||||
@@ -70,7 +70,7 @@ scan_installed_apps() {
|
|||||||
current_time=$(get_epoch_seconds)
|
current_time=$(get_epoch_seconds)
|
||||||
local age=$((current_time - cache_mtime))
|
local age=$((current_time - cache_mtime))
|
||||||
if [[ $age -lt $cache_age_seconds ]]; then
|
if [[ $age -lt $cache_age_seconds ]]; then
|
||||||
debug_log "Using cached app list (age: ${age}s)"
|
debug_log "Using cached app list, age: ${age}s"
|
||||||
if [[ -r "$cache_file" ]] && [[ -s "$cache_file" ]]; then
|
if [[ -r "$cache_file" ]] && [[ -s "$cache_file" ]]; then
|
||||||
if cat "$cache_file" > "$installed_bundles" 2> /dev/null; then
|
if cat "$cache_file" > "$installed_bundles" 2> /dev/null; then
|
||||||
return 0
|
return 0
|
||||||
@@ -82,7 +82,7 @@ scan_installed_apps() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
debug_log "Scanning installed applications (cache expired or missing)"
|
debug_log "Scanning installed applications, cache expired or missing"
|
||||||
local -a app_dirs=(
|
local -a app_dirs=(
|
||||||
"/Applications"
|
"/Applications"
|
||||||
"/System/Applications"
|
"/System/Applications"
|
||||||
@@ -310,7 +310,7 @@ clean_orphaned_app_data() {
|
|||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
if [[ $orphaned_count -gt 0 ]]; then
|
if [[ $orphaned_count -gt 0 ]]; then
|
||||||
local orphaned_mb=$(echo "$total_orphaned_kb" | awk '{printf "%.1f", $1/1024}')
|
local orphaned_mb=$(echo "$total_orphaned_kb" | awk '{printf "%.1f", $1/1024}')
|
||||||
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $orphaned_count items (~${orphaned_mb}MB)"
|
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $orphaned_count items, about ${orphaned_mb}MB"
|
||||||
note_activity
|
note_activity
|
||||||
fi
|
fi
|
||||||
rm -f "$installed_bundles"
|
rm -f "$installed_bundles"
|
||||||
@@ -512,7 +512,7 @@ clean_orphaned_system_services() {
|
|||||||
else
|
else
|
||||||
orphaned_kb_display="${total_orphaned_kb}KB"
|
orphaned_kb_display="${total_orphaned_kb}KB"
|
||||||
fi
|
fi
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $orphaned_count orphaned services (~$orphaned_kb_display)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $orphaned_count orphaned services, about $orphaned_kb_display"
|
||||||
note_activity
|
note_activity
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ clean_homebrew() {
|
|||||||
freed_space=$(printf '%s\n' "$brew_output" | grep -o "[0-9.]*[KMGT]B freed" 2> /dev/null | tail -1 || true)
|
freed_space=$(printf '%s\n' "$brew_output" | grep -o "[0-9.]*[KMGT]B freed" 2> /dev/null | tail -1 || true)
|
||||||
if [[ $removed_count -gt 0 ]] || [[ -n "$freed_space" ]]; then
|
if [[ $removed_count -gt 0 ]] || [[ -n "$freed_space" ]]; then
|
||||||
if [[ -n "$freed_space" ]]; then
|
if [[ -n "$freed_space" ]]; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup ${GREEN}($freed_space)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup${NC}, ${GREEN}$freed_space${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup (${removed_count} items)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup, ${removed_count} items"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
elif [[ $brew_exit -eq 124 ]]; then
|
elif [[ $brew_exit -eq 124 ]]; then
|
||||||
@@ -102,7 +102,7 @@ clean_homebrew() {
|
|||||||
local removed_packages
|
local removed_packages
|
||||||
removed_packages=$(printf '%s\n' "$autoremove_output" | grep -c "^Uninstalling" 2> /dev/null || true)
|
removed_packages=$(printf '%s\n' "$autoremove_output" | grep -c "^Uninstalling" 2> /dev/null || true)
|
||||||
if [[ $removed_packages -gt 0 ]]; then
|
if [[ $removed_packages -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed orphaned dependencies (${removed_packages} packages)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed orphaned dependencies, ${removed_packages} packages"
|
||||||
fi
|
fi
|
||||||
elif [[ $autoremove_exit -eq 124 ]]; then
|
elif [[ $autoremove_exit -eq 124 ]]; then
|
||||||
echo -e " ${GRAY}${ICON_WARNING}${NC} Autoremove timed out · run ${GRAY}brew autoremove${NC} manually"
|
echo -e " ${GRAY}${ICON_WARNING}${NC} Autoremove timed out · run ${GRAY}brew autoremove${NC} manually"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ check_tcc_permissions() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "${BLUE}First-time setup${NC}"
|
echo -e "${BLUE}First-time setup${NC}"
|
||||||
echo -e "${GRAY}macOS will request permissions to access Library folders.${NC}"
|
echo -e "${GRAY}macOS will request permissions to access Library folders.${NC}"
|
||||||
echo -e "${GRAY}You may see ${GREEN}${#tcc_dirs[@]} permission dialogs${NC}${GRAY} - please approve them all.${NC}"
|
echo -e "${GRAY}You may see ${GREEN}${#tcc_dirs[@]} permission dialogs${NC}${GRAY}, please approve them all.${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to continue: "
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to continue: "
|
||||||
read -r
|
read -r
|
||||||
@@ -75,12 +75,12 @@ clean_service_worker_cache() {
|
|||||||
local cleaned_mb=$((cleaned_size / 1024))
|
local cleaned_mb=$((cleaned_size / 1024))
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
if [[ "$DRY_RUN" != "true" ]]; then
|
||||||
if [[ $protected_count -gt 0 ]]; then
|
if [[ $protected_count -gt 0 ]]; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker (${cleaned_mb}MB, ${protected_count} protected)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker, ${cleaned_mb}MB, ${protected_count} protected"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker (${cleaned_mb}MB)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $browser_name Service Worker, ${cleaned_mb}MB"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $browser_name Service Worker (would clean ${cleaned_mb}MB, ${protected_count} protected)"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $browser_name Service Worker, would clean ${cleaned_mb}MB, ${protected_count} protected"
|
||||||
fi
|
fi
|
||||||
note_activity
|
note_activity
|
||||||
if [[ "$spinner_was_running" == "true" ]]; then
|
if [[ "$spinner_was_running" == "true" ]]; then
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ select_purge_categories() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
printf "%s\n" "$clear_line"
|
printf "%s\n" "$clear_line"
|
||||||
printf "%s${PURPLE_BOLD}Select Categories to Clean${NC}%s ${GRAY}- ${selected_gb}GB ($selected_count selected)${NC}\n" "$clear_line" "$scroll_indicator"
|
printf "%s${PURPLE_BOLD}Select Categories to Clean${NC}%s ${GRAY}, ${selected_gb}GB, ${selected_count} selected${NC}\n" "$clear_line" "$scroll_indicator"
|
||||||
printf "%s\n" "$clear_line"
|
printf "%s\n" "$clear_line"
|
||||||
|
|
||||||
IFS=',' read -r -a recent_flags <<< "${PURGE_RECENT_CATEGORIES:-}"
|
IFS=',' read -r -a recent_flags <<< "${PURGE_RECENT_CATEGORIES:-}"
|
||||||
@@ -1135,7 +1135,7 @@ clean_project_artifacts() {
|
|||||||
fi
|
fi
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
stop_inline_spinner
|
stop_inline_spinner
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} $project_path - $artifact_type ${GREEN}($size_human)${NC}"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} $project_path, $artifact_type${NC}, ${GREEN}$size_human${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
# Update count
|
# Update count
|
||||||
|
|||||||
@@ -39,18 +39,18 @@ clean_deep_system() {
|
|||||||
if [[ -d "/macOS Install Data" ]]; then
|
if [[ -d "/macOS Install Data" ]]; then
|
||||||
local mtime=$(get_file_mtime "/macOS Install Data")
|
local mtime=$(get_file_mtime "/macOS Install Data")
|
||||||
local age_days=$((($(get_epoch_seconds) - mtime) / 86400))
|
local age_days=$((($(get_epoch_seconds) - mtime) / 86400))
|
||||||
debug_log "Found macOS Install Data (age: ${age_days} days)"
|
debug_log "Found macOS Install Data, age ${age_days} days"
|
||||||
if [[ $age_days -ge 30 ]]; then
|
if [[ $age_days -ge 30 ]]; then
|
||||||
local size_kb=$(get_path_size_kb "/macOS Install Data")
|
local size_kb=$(get_path_size_kb "/macOS Install Data")
|
||||||
if [[ -n "$size_kb" && "$size_kb" -gt 0 ]]; then
|
if [[ -n "$size_kb" && "$size_kb" -gt 0 ]]; then
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
debug_log "Cleaning macOS Install Data: $size_human (${age_days} days old)"
|
debug_log "Cleaning macOS Install Data: $size_human, ${age_days} days old"
|
||||||
if safe_sudo_remove "/macOS Install Data"; then
|
if safe_sudo_remove "/macOS Install Data"; then
|
||||||
log_success "macOS Install Data ($size_human)"
|
log_success "macOS Install Data, $size_human"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
debug_log "Keeping macOS Install Data (only ${age_days} days old, needs 30+)"
|
debug_log "Keeping macOS Install Data, only ${age_days} days old, needs 30+"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
start_section_spinner "Scanning system caches..."
|
start_section_spinner "Scanning system caches..."
|
||||||
@@ -70,13 +70,13 @@ clean_deep_system() {
|
|||||||
local current_time
|
local current_time
|
||||||
current_time=$(get_epoch_seconds)
|
current_time=$(get_epoch_seconds)
|
||||||
if [[ $((current_time - last_update_time)) -ge $update_interval ]]; then
|
if [[ $((current_time - last_update_time)) -ge $update_interval ]]; then
|
||||||
start_section_spinner "Scanning system caches... ($found_count found)"
|
start_section_spinner "Scanning system caches... $found_count found"
|
||||||
last_update_time=$current_time
|
last_update_time=$current_time
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(run_with_timeout 5 command find /private/var/folders -type d -name "*.code_sign_clone" -path "*/X/*" -print0 2> /dev/null || true)
|
done < <(run_with_timeout 5 command find /private/var/folders -type d -name "*.code_sign_clone" -path "*/X/*" -print0 2> /dev/null || true)
|
||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
[[ $code_sign_cleaned -gt 0 ]] && log_success "Browser code signature caches ($code_sign_cleaned items)"
|
[[ $code_sign_cleaned -gt 0 ]] && log_success "Browser code signature caches, $code_sign_cleaned items"
|
||||||
|
|
||||||
start_section_spinner "Cleaning system diagnostic logs..."
|
start_section_spinner "Cleaning system diagnostic logs..."
|
||||||
local diag_cleaned=0
|
local diag_cleaned=0
|
||||||
@@ -178,7 +178,7 @@ clean_time_machine_failed_backups() {
|
|||||||
local backup_name=$(basename "$inprogress_file")
|
local backup_name=$(basename "$inprogress_file")
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Incomplete backup: $backup_name ${YELLOW}($size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Incomplete backup: $backup_name${NC}, ${YELLOW}$size_human dry${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
note_activity
|
note_activity
|
||||||
continue
|
continue
|
||||||
@@ -188,7 +188,7 @@ clean_time_machine_failed_backups() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete backup: $backup_name ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete backup: $backup_name${NC}, ${GREEN}$size_human${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
((files_cleaned++))
|
((files_cleaned++))
|
||||||
((total_size_cleaned += size_kb))
|
((total_size_cleaned += size_kb))
|
||||||
@@ -224,7 +224,7 @@ clean_time_machine_failed_backups() {
|
|||||||
local backup_name=$(basename "$inprogress_file")
|
local backup_name=$(basename "$inprogress_file")
|
||||||
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
local size_human=$(bytes_to_human "$((size_kb * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Incomplete APFS backup in $bundle_name: $backup_name ${YELLOW}($size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Incomplete APFS backup in $bundle_name: $backup_name${NC}, ${YELLOW}$size_human dry${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
note_activity
|
note_activity
|
||||||
continue
|
continue
|
||||||
@@ -233,7 +233,7 @@ clean_time_machine_failed_backups() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete APFS backup in $bundle_name: $backup_name ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete APFS backup in $bundle_name: $backup_name${NC}, ${GREEN}$size_human${NC}"
|
||||||
((tm_cleaned++))
|
((tm_cleaned++))
|
||||||
((files_cleaned++))
|
((files_cleaned++))
|
||||||
((total_size_cleaned += size_kb))
|
((total_size_cleaned += size_kb))
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ clean_user_essentials() {
|
|||||||
[[ "$trash_count" =~ ^[0-9]+$ ]] || trash_count="0"
|
[[ "$trash_count" =~ ^[0-9]+$ ]] || trash_count="0"
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
[[ $trash_count -gt 0 ]] && echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Trash · would empty ($trash_count items)" || echo -e " ${GRAY}${ICON_EMPTY}${NC} Trash · already empty"
|
[[ $trash_count -gt 0 ]] && echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Trash · would empty, $trash_count items" || echo -e " ${GRAY}${ICON_EMPTY}${NC} Trash · already empty"
|
||||||
elif [[ $trash_count -gt 0 ]]; then
|
elif [[ $trash_count -gt 0 ]]; then
|
||||||
if osascript -e 'tell application "Finder" to empty trash' > /dev/null 2>&1; then
|
if osascript -e 'tell application "Finder" to empty trash' > /dev/null 2>&1; then
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · emptied ($trash_count items)"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Trash · emptied, $trash_count items"
|
||||||
note_activity
|
note_activity
|
||||||
else
|
else
|
||||||
safe_clean ~/.Trash/* "Trash"
|
safe_clean ~/.Trash/* "Trash"
|
||||||
@@ -97,9 +97,9 @@ clean_chrome_old_versions() {
|
|||||||
local size_human
|
local size_human
|
||||||
size_human=$(bytes_to_human "$((total_size * 1024))")
|
size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Chrome old versions ${YELLOW}(${cleaned_count} dirs, $size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Chrome old versions${NC}, ${YELLOW}${cleaned_count} dirs, $size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Chrome old versions ${GREEN}(${cleaned_count} dirs, $size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Chrome old versions${NC}, ${GREEN}${cleaned_count} dirs, $size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += cleaned_count))
|
((files_cleaned += cleaned_count))
|
||||||
((total_size_cleaned += total_size))
|
((total_size_cleaned += total_size))
|
||||||
@@ -183,9 +183,9 @@ clean_edge_old_versions() {
|
|||||||
local size_human
|
local size_human
|
||||||
size_human=$(bytes_to_human "$((total_size * 1024))")
|
size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Edge old versions ${YELLOW}(${cleaned_count} dirs, $size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Edge old versions${NC}, ${YELLOW}${cleaned_count} dirs, $size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Edge old versions ${GREEN}(${cleaned_count} dirs, $size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Edge old versions${NC}, ${GREEN}${cleaned_count} dirs, $size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += cleaned_count))
|
((files_cleaned += cleaned_count))
|
||||||
((total_size_cleaned += total_size))
|
((total_size_cleaned += total_size))
|
||||||
@@ -245,9 +245,9 @@ clean_edge_updater_old_versions() {
|
|||||||
local size_human
|
local size_human
|
||||||
size_human=$(bytes_to_human "$((total_size * 1024))")
|
size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Edge updater old versions ${YELLOW}(${cleaned_count} dirs, $size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Edge updater old versions${NC}, ${YELLOW}${cleaned_count} dirs, $size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Edge updater old versions ${GREEN}(${cleaned_count} dirs, $size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Edge updater old versions${NC}, ${GREEN}${cleaned_count} dirs, $size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += cleaned_count))
|
((files_cleaned += cleaned_count))
|
||||||
((total_size_cleaned += total_size))
|
((total_size_cleaned += total_size))
|
||||||
@@ -285,12 +285,12 @@ scan_external_volumes() {
|
|||||||
local network_count=${#network_volumes[@]}
|
local network_count=${#network_volumes[@]}
|
||||||
if [[ $volume_count -eq 0 ]]; then
|
if [[ $volume_count -eq 0 ]]; then
|
||||||
if [[ $network_count -gt 0 ]]; then
|
if [[ $network_count -gt 0 ]]; then
|
||||||
echo -e " ${GRAY}${ICON_LIST}${NC} External volumes (${network_count} network volume(s) skipped)"
|
echo -e " ${GRAY}${ICON_LIST}${NC} External volumes, ${network_count} network volumes skipped"
|
||||||
note_activity
|
note_activity
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
start_section_spinner "Scanning $volume_count external volume(s)..."
|
start_section_spinner "Scanning $volume_count external volumes..."
|
||||||
for volume in "${candidate_volumes[@]}"; do
|
for volume in "${candidate_volumes[@]}"; do
|
||||||
[[ -d "$volume" && -r "$volume" ]] || continue
|
[[ -d "$volume" && -r "$volume" ]] || continue
|
||||||
local volume_trash="$volume/.Trashes"
|
local volume_trash="$volume/.Trashes"
|
||||||
@@ -300,7 +300,7 @@ scan_external_volumes() {
|
|||||||
done < <(command find "$volume_trash" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
done < <(command find "$volume_trash" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||||
fi
|
fi
|
||||||
if [[ "$PROTECT_FINDER_METADATA" != "true" ]]; then
|
if [[ "$PROTECT_FINDER_METADATA" != "true" ]]; then
|
||||||
clean_ds_store_tree "$volume" "$(basename "$volume") volume (.DS_Store)"
|
clean_ds_store_tree "$volume" "$(basename "$volume") volume, .DS_Store"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
@@ -310,7 +310,7 @@ clean_finder_metadata() {
|
|||||||
if [[ "$PROTECT_FINDER_METADATA" == "true" ]]; then
|
if [[ "$PROTECT_FINDER_METADATA" == "true" ]]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
clean_ds_store_tree "$HOME" "Home directory (.DS_Store)"
|
clean_ds_store_tree "$HOME" "Home directory, .DS_Store"
|
||||||
}
|
}
|
||||||
# macOS system caches and user-level leftovers.
|
# macOS system caches and user-level leftovers.
|
||||||
clean_macos_system_caches() {
|
clean_macos_system_caches() {
|
||||||
@@ -389,7 +389,7 @@ clean_mail_downloads() {
|
|||||||
done
|
done
|
||||||
if [[ $count -gt 0 ]]; then
|
if [[ $count -gt 0 ]]; then
|
||||||
local cleaned_mb=$(echo "$cleaned_kb" | awk '{printf "%.1f", $1/1024}' || echo "0.0")
|
local cleaned_mb=$(echo "$cleaned_kb" | awk '{printf "%.1f", $1/1024}' || echo "0.0")
|
||||||
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $count mail attachments (~${cleaned_mb}MB)"
|
echo " ${GREEN}${ICON_SUCCESS}${NC} Cleaned $count mail attachments, about ${cleaned_mb}MB"
|
||||||
note_activity
|
note_activity
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -418,9 +418,9 @@ clean_sandboxed_app_caches() {
|
|||||||
if [[ "$found_any" == "true" ]]; then
|
if [[ "$found_any" == "true" ]]; then
|
||||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Sandboxed app caches ${YELLOW}($size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Sandboxed app caches${NC}, ${YELLOW}$size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Sandboxed app caches ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Sandboxed app caches${NC}, ${GREEN}$size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += cleaned_count))
|
((files_cleaned += cleaned_count))
|
||||||
((total_size_cleaned += total_size))
|
((total_size_cleaned += total_size))
|
||||||
@@ -603,9 +603,9 @@ clean_application_support_logs() {
|
|||||||
if [[ "$found_any" == "true" ]]; then
|
if [[ "$found_any" == "true" ]]; then
|
||||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Application Support logs/caches ${YELLOW}($size_human dry)${NC}"
|
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Application Support logs/caches${NC}, ${YELLOW}$size_human dry${NC}"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Application Support logs/caches ${GREEN}($size_human)${NC}"
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Application Support logs/caches${NC}, ${GREEN}$size_human${NC}"
|
||||||
fi
|
fi
|
||||||
((files_cleaned += cleaned_count))
|
((files_cleaned += cleaned_count))
|
||||||
((total_size_cleaned += total_size))
|
((total_size_cleaned += total_size))
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ update_progress_if_needed() {
|
|||||||
if [[ $((current_time - last_time)) -ge $interval ]]; then
|
if [[ $((current_time - last_time)) -ge $interval ]]; then
|
||||||
# Update the spinner with progress
|
# Update the spinner with progress
|
||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
start_section_spinner "Scanning items... ($completed/$total)"
|
start_section_spinner "Scanning items... $completed/$total"
|
||||||
|
|
||||||
# Update the last_update_time variable
|
# Update the last_update_time variable
|
||||||
eval "$last_update_var=$current_time"
|
eval "$last_update_var=$current_time"
|
||||||
@@ -717,7 +717,7 @@ push_spinner_state() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
MOLE_SPINNER_STACK+=("$current_state")
|
MOLE_SPINNER_STACK+=("$current_state")
|
||||||
debug_log "Pushed spinner state: $current_state (stack depth: ${#MOLE_SPINNER_STACK[@]})"
|
debug_log "Pushed spinner state: $current_state, stack depth: ${#MOLE_SPINNER_STACK[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pop and restore spinner state from stack
|
# Pop and restore spinner state from stack
|
||||||
@@ -730,7 +730,7 @@ pop_spinner_state() {
|
|||||||
|
|
||||||
# Stack depth safety check
|
# Stack depth safety check
|
||||||
if [[ ${#MOLE_SPINNER_STACK[@]} -gt 10 ]]; then
|
if [[ ${#MOLE_SPINNER_STACK[@]} -gt 10 ]]; then
|
||||||
debug_log "Warning: Spinner stack depth excessive (${#MOLE_SPINNER_STACK[@]}), possible leak"
|
debug_log "Warning: Spinner stack depth excessive, ${#MOLE_SPINNER_STACK[@]}, possible leak"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local last_idx=$((${#MOLE_SPINNER_STACK[@]} - 1))
|
local last_idx=$((${#MOLE_SPINNER_STACK[@]} - 1))
|
||||||
@@ -745,7 +745,7 @@ pop_spinner_state() {
|
|||||||
done
|
done
|
||||||
MOLE_SPINNER_STACK=("${new_stack[@]}")
|
MOLE_SPINNER_STACK=("${new_stack[@]}")
|
||||||
|
|
||||||
debug_log "Popped spinner state: $state (remaining depth: ${#MOLE_SPINNER_STACK[@]})"
|
debug_log "Popped spinner state: $state, remaining depth: ${#MOLE_SPINNER_STACK[@]}"
|
||||||
|
|
||||||
# Restore state if needed
|
# Restore state if needed
|
||||||
if [[ "$state" == running:* ]]; then
|
if [[ "$state" == running:* ]]; then
|
||||||
@@ -822,7 +822,7 @@ get_terminal_info() {
|
|||||||
local info="Terminal: ${TERM:-unknown}"
|
local info="Terminal: ${TERM:-unknown}"
|
||||||
|
|
||||||
if is_ansi_supported; then
|
if is_ansi_supported; then
|
||||||
info+=" (ANSI supported)"
|
info+=", ANSI supported"
|
||||||
|
|
||||||
if command -v tput > /dev/null 2>&1; then
|
if command -v tput > /dev/null 2>&1; then
|
||||||
local cols=$(tput cols 2> /dev/null || echo "?")
|
local cols=$(tput cols 2> /dev/null || echo "?")
|
||||||
@@ -831,7 +831,7 @@ get_terminal_info() {
|
|||||||
info+=" ${cols}x${lines}, ${colors} colors"
|
info+=" ${cols}x${lines}, ${colors} colors"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
info+=" (ANSI not supported)"
|
info+=", ANSI not supported"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$info"
|
echo "$info"
|
||||||
@@ -852,11 +852,11 @@ validate_terminal_environment() {
|
|||||||
# Check if running in a known problematic terminal
|
# Check if running in a known problematic terminal
|
||||||
case "${TERM:-}" in
|
case "${TERM:-}" in
|
||||||
dumb)
|
dumb)
|
||||||
log_warning "Running in 'dumb' terminal - limited functionality"
|
log_warning "Running in 'dumb' terminal, limited functionality"
|
||||||
((warnings++))
|
((warnings++))
|
||||||
;;
|
;;
|
||||||
unknown)
|
unknown)
|
||||||
log_warning "Terminal type unknown - may have display issues"
|
log_warning "Terminal type unknown, may have display issues"
|
||||||
((warnings++))
|
((warnings++))
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -865,7 +865,7 @@ validate_terminal_environment() {
|
|||||||
if command -v tput > /dev/null 2>&1; then
|
if command -v tput > /dev/null 2>&1; then
|
||||||
local cols=$(tput cols 2> /dev/null || echo "80")
|
local cols=$(tput cols 2> /dev/null || echo "80")
|
||||||
if [[ "$cols" -lt 60 ]]; then
|
if [[ "$cols" -lt 60 ]]; then
|
||||||
log_warning "Terminal width ($cols cols) is narrow - output may wrap"
|
log_warning "Terminal width, $cols cols, is narrow, output may wrap"
|
||||||
((warnings++))
|
((warnings++))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ update_via_homebrew() {
|
|||||||
installed_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}')
|
installed_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}')
|
||||||
[[ -z "$installed_version" ]] && installed_version=$(mo --version 2> /dev/null | awk '/Mole version/ {print $3; exit}')
|
[[ -z "$installed_version" ]] && installed_version=$(mo --version 2> /dev/null | awk '/Mole version/ {print $3; exit}')
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version (${installed_version:-$current_version})"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version, ${installed_version:-$current_version}"
|
||||||
echo ""
|
echo ""
|
||||||
elif echo "$upgrade_output" | grep -q "Error:"; then
|
elif echo "$upgrade_output" | grep -q "Error:"; then
|
||||||
log_error "Homebrew upgrade failed"
|
log_error "Homebrew upgrade failed"
|
||||||
@@ -93,7 +93,7 @@ update_via_homebrew() {
|
|||||||
new_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}')
|
new_version=$(brew list --versions mole 2> /dev/null | awk '{print $2}')
|
||||||
[[ -z "$new_version" ]] && new_version=$(mo --version 2> /dev/null | awk '/Mole version/ {print $3; exit}')
|
[[ -z "$new_version" ]] && new_version=$(mo --version 2> /dev/null | awk '/Mole version/ {print $3; exit}')
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-$current_version})"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-$current_version}"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ safe_remove() {
|
|||||||
MOLE_PERMISSION_DENIED_COUNT=${MOLE_PERMISSION_DENIED_COUNT:-0}
|
MOLE_PERMISSION_DENIED_COUNT=${MOLE_PERMISSION_DENIED_COUNT:-0}
|
||||||
MOLE_PERMISSION_DENIED_COUNT=$((MOLE_PERMISSION_DENIED_COUNT + 1))
|
MOLE_PERMISSION_DENIED_COUNT=$((MOLE_PERMISSION_DENIED_COUNT + 1))
|
||||||
export MOLE_PERMISSION_DENIED_COUNT
|
export MOLE_PERMISSION_DENIED_COUNT
|
||||||
debug_log "Permission denied: $path (may need Full Disk Access)"
|
debug_log "Permission denied: $path, may need Full Disk Access"
|
||||||
else
|
else
|
||||||
[[ "$silent" != "true" ]] && log_error "Failed to remove: $path"
|
[[ "$silent" != "true" ]] && log_error "Failed to remove: $path"
|
||||||
fi
|
fi
|
||||||
@@ -267,20 +267,20 @@ safe_sudo_remove() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_file_action "[DRY RUN] Would remove (sudo)" "$path" "$file_size" "$file_age"
|
debug_file_action "[DRY RUN] Would remove, sudo" "$path" "$file_size" "$file_age"
|
||||||
else
|
else
|
||||||
debug_log "[DRY RUN] Would remove (sudo): $path"
|
debug_log "[DRY RUN] Would remove, sudo: $path"
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_log "Removing (sudo): $path"
|
debug_log "Removing, sudo: $path"
|
||||||
|
|
||||||
# Perform the deletion
|
# Perform the deletion
|
||||||
if sudo rm -rf "$path" 2> /dev/null; then # SAFE: safe_sudo_remove implementation
|
if sudo rm -rf "$path" 2> /dev/null; then # SAFE: safe_sudo_remove implementation
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Failed to remove (sudo): $path"
|
log_error "Failed to remove, sudo: $path"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -309,11 +309,11 @@ safe_find_delete() {
|
|||||||
|
|
||||||
# Validate type filter
|
# Validate type filter
|
||||||
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
||||||
log_error "Invalid type filter: $type_filter (must be 'f' or 'd')"
|
log_error "Invalid type filter: $type_filter, must be 'f' or 'd'"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_log "Finding in $base_dir: $pattern (age: ${age_days}d, type: $type_filter)"
|
debug_log "Finding in $base_dir: $pattern, age: ${age_days}d, type: $type_filter"
|
||||||
|
|
||||||
local find_args=("-maxdepth" "5" "-name" "$pattern" "-type" "$type_filter")
|
local find_args=("-maxdepth" "5" "-name" "$pattern" "-type" "$type_filter")
|
||||||
if [[ "$age_days" -gt 0 ]]; then
|
if [[ "$age_days" -gt 0 ]]; then
|
||||||
@@ -340,7 +340,7 @@ safe_sudo_find_delete() {
|
|||||||
|
|
||||||
# Validate base directory (use sudo for permission-restricted dirs)
|
# Validate base directory (use sudo for permission-restricted dirs)
|
||||||
if ! sudo test -d "$base_dir" 2> /dev/null; then
|
if ! sudo test -d "$base_dir" 2> /dev/null; then
|
||||||
debug_log "Directory does not exist (skipping): $base_dir"
|
debug_log "Directory does not exist, skipping: $base_dir"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -351,11 +351,11 @@ safe_sudo_find_delete() {
|
|||||||
|
|
||||||
# Validate type filter
|
# Validate type filter
|
||||||
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
if [[ "$type_filter" != "f" && "$type_filter" != "d" ]]; then
|
||||||
log_error "Invalid type filter: $type_filter (must be 'f' or 'd')"
|
log_error "Invalid type filter: $type_filter, must be 'f' or 'd'"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug_log "Finding (sudo) in $base_dir: $pattern (age: ${age_days}d, type: $type_filter)"
|
debug_log "Finding, sudo, in $base_dir: $pattern, age: ${age_days}d, type: $type_filter"
|
||||||
|
|
||||||
local find_args=("-maxdepth" "5" "-name" "$pattern" "-type" "$type_filter")
|
local find_args=("-maxdepth" "5" "-name" "$pattern" "-type" "$type_filter")
|
||||||
if [[ "$age_days" -gt 0 ]]; then
|
if [[ "$age_days" -gt 0 ]]; then
|
||||||
|
|||||||
@@ -138,10 +138,9 @@ debug_file_action() {
|
|||||||
local file_age="${4:-}"
|
local file_age="${4:-}"
|
||||||
|
|
||||||
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
||||||
local msg=" - $file_path"
|
local msg=" * $file_path"
|
||||||
[[ -n "$file_size" ]] && msg+=" ($file_size"
|
[[ -n "$file_size" ]] && msg+=", $file_size"
|
||||||
[[ -n "$file_age" ]] && msg+=", ${file_age} days old"
|
[[ -n "$file_age" ]] && msg+=", ${file_age} days old"
|
||||||
[[ -n "$file_size" ]] && msg+=")"
|
|
||||||
|
|
||||||
# Output to stderr
|
# Output to stderr
|
||||||
echo -e "${GRAY}[DEBUG] $action: $msg${NC}" >&2
|
echo -e "${GRAY}[DEBUG] $action: $msg${NC}" >&2
|
||||||
@@ -165,10 +164,10 @@ debug_risk_level() {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
# Output to stderr with color
|
# Output to stderr with color
|
||||||
echo -e "${GRAY}[DEBUG] Risk Level: ${color}${risk_level}${GRAY} ($reason)${NC}" >&2
|
echo -e "${GRAY}[DEBUG] Risk Level: ${color}${risk_level}${GRAY}, $reason${NC}" >&2
|
||||||
|
|
||||||
# Also log to file
|
# Also log to file
|
||||||
echo "Risk Level: $risk_level ($reason)" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
|
echo "Risk Level: $risk_level, $reason" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,16 +186,16 @@ log_system_info() {
|
|||||||
# Start block in debug log file
|
# Start block in debug log file
|
||||||
{
|
{
|
||||||
echo "----------------------------------------------------------------------"
|
echo "----------------------------------------------------------------------"
|
||||||
echo "Mole Debug Session - $(date '+%Y-%m-%d %H:%M:%S')"
|
echo "Mole Debug Session, $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
echo "----------------------------------------------------------------------"
|
echo "----------------------------------------------------------------------"
|
||||||
echo "User: $USER"
|
echo "User: $USER"
|
||||||
echo "Hostname: $(hostname)"
|
echo "Hostname: $(hostname)"
|
||||||
echo "Architecture: $(uname -m)"
|
echo "Architecture: $(uname -m)"
|
||||||
echo "Kernel: $(uname -r)"
|
echo "Kernel: $(uname -r)"
|
||||||
if command -v sw_vers > /dev/null; then
|
if command -v sw_vers > /dev/null; then
|
||||||
echo "macOS: $(sw_vers -productVersion) ($(sw_vers -buildVersion))"
|
echo "macOS: $(sw_vers -productVersion), $(sw_vers -buildVersion)"
|
||||||
fi
|
fi
|
||||||
echo "Shell: ${SHELL:-unknown} (${TERM:-unknown})"
|
echo "Shell: ${SHELL:-unknown}, ${TERM:-unknown}"
|
||||||
|
|
||||||
# Check sudo status non-interactively
|
# Check sudo status non-interactively
|
||||||
if sudo -n true 2> /dev/null; then
|
if sudo -n true 2> /dev/null; then
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ _request_password() {
|
|||||||
|
|
||||||
# Show hint on first attempt about Touch ID appearing again
|
# Show hint on first attempt about Touch ID appearing again
|
||||||
if [[ $show_hint == true ]] && check_touchid_support; then
|
if [[ $show_hint == true ]] && check_touchid_support; then
|
||||||
echo -e "${GRAY}Note: Touch ID dialog may appear once more - just cancel it${NC}" > "$tty_path"
|
echo -e "${GRAY}Note: Touch ID dialog may appear once more, just cancel it${NC}" > "$tty_path"
|
||||||
show_hint=false
|
show_hint=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ request_sudo_access() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Touch ID is available and not in clamshell mode
|
# Touch ID is available and not in clamshell mode
|
||||||
echo -e "${PURPLE}${ICON_ARROW}${NC} ${prompt_msg} ${GRAY}(Touch ID or password)${NC}"
|
echo -e "${PURPLE}${ICON_ARROW}${NC} ${prompt_msg} ${GRAY}, Touch ID or password${NC}"
|
||||||
|
|
||||||
# Start sudo in background so we can monitor and control it
|
# Start sudo in background so we can monitor and control it
|
||||||
sudo -v < /dev/null > /dev/null 2>&1 &
|
sudo -v < /dev/null > /dev/null 2>&1 &
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ run_with_timeout() {
|
|||||||
# ========================================================================
|
# ========================================================================
|
||||||
|
|
||||||
if [[ "${MO_DEBUG:-0}" == "1" ]]; then
|
if [[ "${MO_DEBUG:-0}" == "1" ]]; then
|
||||||
echo "[TIMEOUT] Shell fallback (${duration}s): $*" >&2
|
echo "[TIMEOUT] Shell fallback, ${duration}s: $*" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start command in background
|
# Start command in background
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ manage_purge_paths() {
|
|||||||
if [[ -d "$path" ]]; then
|
if [[ -d "$path" ]]; then
|
||||||
echo -e " ${GREEN}✓${NC} $display_path"
|
echo -e " ${GREEN}✓${NC} $display_path"
|
||||||
else
|
else
|
||||||
echo -e " ${GRAY}○${NC} $display_path ${GRAY}(not found)${NC}"
|
echo -e " ${GRAY}○${NC} $display_path${GRAY}, not found${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
@@ -76,7 +76,7 @@ manage_purge_paths() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
if [[ $custom_count -gt 0 ]]; then
|
if [[ $custom_count -gt 0 ]]; then
|
||||||
echo -e "${GRAY}Using custom config with $custom_count path(s)${NC}"
|
echo -e "${GRAY}Using custom config with $custom_count paths${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${GRAY}Using ${#DEFAULT_PURGE_SEARCH_PATHS[@]} default paths${NC}"
|
echo -e "${GRAY}Using ${#DEFAULT_PURGE_SEARCH_PATHS[@]} default paths${NC}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ format_brew_update_label() {
|
|||||||
((formulas > 0)) && details+=("${formulas} formula")
|
((formulas > 0)) && details+=("${formulas} formula")
|
||||||
((casks > 0)) && details+=("${casks} cask")
|
((casks > 0)) && details+=("${casks} cask")
|
||||||
|
|
||||||
local detail_str="(${total} updates)"
|
local detail_str=", ${total} updates"
|
||||||
if ((${#details[@]} > 0)); then
|
if ((${#details[@]} > 0)); then
|
||||||
detail_str="($(
|
detail_str=", $(
|
||||||
IFS=', '
|
IFS=', '
|
||||||
printf '%s' "${details[*]}"
|
printf '%s' "${details[*]}"
|
||||||
))"
|
)"
|
||||||
fi
|
fi
|
||||||
printf " • Homebrew%s" "$detail_str"
|
printf " • Homebrew%s" "$detail_str"
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ ask_for_updates() {
|
|||||||
|
|
||||||
if [[ -n "${APPSTORE_UPDATE_COUNT:-}" && "${APPSTORE_UPDATE_COUNT:-0}" -gt 0 ]]; then
|
if [[ -n "${APPSTORE_UPDATE_COUNT:-}" && "${APPSTORE_UPDATE_COUNT:-0}" -gt 0 ]]; then
|
||||||
has_updates=true
|
has_updates=true
|
||||||
update_list+=(" • App Store (${APPSTORE_UPDATE_COUNT} apps)")
|
update_list+=(" • App Store, ${APPSTORE_UPDATE_COUNT} apps")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${MACOS_UPDATE_AVAILABLE:-}" && "${MACOS_UPDATE_AVAILABLE}" == "true" ]]; then
|
if [[ -n "${MACOS_UPDATE_AVAILABLE:-}" && "${MACOS_UPDATE_AVAILABLE}" == "true" ]]; then
|
||||||
@@ -132,10 +132,10 @@ perform_updates() {
|
|||||||
echo -e "${GRAY}No updates to perform${NC}"
|
echo -e "${GRAY}No updates to perform${NC}"
|
||||||
return 0
|
return 0
|
||||||
elif [[ $updated_count -eq $total_count ]]; then
|
elif [[ $updated_count -eq $total_count ]]; then
|
||||||
echo -e "${GREEN}All updates completed (${updated_count}/${total_count})${NC}"
|
echo -e "${GREEN}All updates completed, ${updated_count}/${total_count}${NC}"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo -e "${RED}Update failed (${updated_count}/${total_count})${NC}"
|
echo -e "${RED}Update failed, ${updated_count}/${total_count}${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ CloudKit cache|$HOME/Library/Caches/CloudKit/*|system_cache
|
|||||||
Trash|$HOME/.Trash|system_cache
|
Trash|$HOME/.Trash|system_cache
|
||||||
EOF
|
EOF
|
||||||
# Add FINDER_METADATA with constant reference
|
# Add FINDER_METADATA with constant reference
|
||||||
echo "Finder metadata (.DS_Store)|$FINDER_METADATA_SENTINEL|system_cache"
|
echo "Finder metadata, .DS_Store|$FINDER_METADATA_SENTINEL|system_cache"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get all optimize items with their patterns
|
# Get all optimize items with their patterns
|
||||||
@@ -284,13 +284,13 @@ manage_whitelist_categories() {
|
|||||||
items_source=$(get_optimize_whitelist_items)
|
items_source=$(get_optimize_whitelist_items)
|
||||||
active_config_file="$WHITELIST_CONFIG_OPTIMIZE"
|
active_config_file="$WHITELIST_CONFIG_OPTIMIZE"
|
||||||
local display_config="${active_config_file/#$HOME/~}"
|
local display_config="${active_config_file/#$HOME/~}"
|
||||||
menu_title="Whitelist Manager – Select system checks to ignore
|
menu_title="Whitelist Manager, Select system checks to ignore
|
||||||
${GRAY}Edit: ${display_config}${NC}"
|
${GRAY}Edit: ${display_config}${NC}"
|
||||||
else
|
else
|
||||||
items_source=$(get_all_cache_items)
|
items_source=$(get_all_cache_items)
|
||||||
active_config_file="$WHITELIST_CONFIG_CLEAN"
|
active_config_file="$WHITELIST_CONFIG_CLEAN"
|
||||||
local display_config="${active_config_file/#$HOME/~}"
|
local display_config="${active_config_file/#$HOME/~}"
|
||||||
menu_title="Whitelist Manager – Select caches to protect
|
menu_title="Whitelist Manager, Select caches to protect
|
||||||
${GRAY}Edit: ${display_config}${NC}"
|
${GRAY}Edit: ${display_config}${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ ${GRAY}Edit: ${display_config}${NC}"
|
|||||||
if [[ ${#custom_patterns[@]} -gt 0 ]]; then
|
if [[ ${#custom_patterns[@]} -gt 0 ]]; then
|
||||||
summary_lines+=("Protected ${#selected_patterns[@]} predefined + ${#custom_patterns[@]} custom patterns")
|
summary_lines+=("Protected ${#selected_patterns[@]} predefined + ${#custom_patterns[@]} custom patterns")
|
||||||
else
|
else
|
||||||
summary_lines+=("Protected ${total_protected} cache(s)")
|
summary_lines+=("Protected ${total_protected} caches")
|
||||||
fi
|
fi
|
||||||
local display_config="${active_config_file/#$HOME/~}"
|
local display_config="${active_config_file/#$HOME/~}"
|
||||||
summary_lines+=("Config: ${GRAY}${display_config}${NC}")
|
summary_lines+=("Config: ${GRAY}${display_config}${NC}")
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ opt_sqlite_vacuum() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v sqlite3 > /dev/null 2>&1; then
|
if ! command -v sqlite3 > /dev/null 2>&1; then
|
||||||
echo -e " ${GRAY}-${NC} Database optimization already optimal (sqlite3 unavailable)"
|
echo -e " ${GRAY}-${NC} Database optimization already optimal, sqlite3 unavailable"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -584,7 +584,7 @@ opt_disk_permissions_repair() {
|
|||||||
opt_msg "User directory permissions repaired"
|
opt_msg "User directory permissions repaired"
|
||||||
opt_msg "File access issues resolved"
|
opt_msg "File access issues resolved"
|
||||||
else
|
else
|
||||||
echo -e " ${YELLOW}!${NC} Failed to repair permissions (may not be needed)"
|
echo -e " ${YELLOW}!${NC} Failed to repair permissions, may not be needed"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
opt_msg "User directory permissions repaired"
|
opt_msg "User directory permissions repaired"
|
||||||
@@ -705,7 +705,7 @@ opt_spotlight_index_optimize() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||||
echo -e " ${BLUE}ℹ${NC} Spotlight search is slow, rebuilding index (may take 1-2 hours)"
|
echo -e " ${BLUE}ℹ${NC} Spotlight search is slow, rebuilding index, may take 1-2 hours"
|
||||||
if sudo mdutil -E / > /dev/null 2>&1; then
|
if sudo mdutil -E / > /dev/null 2>&1; then
|
||||||
opt_msg "Spotlight index rebuild started"
|
opt_msg "Spotlight index rebuild started"
|
||||||
echo -e " ${GRAY}Indexing will continue in background${NC}"
|
echo -e " ${GRAY}Indexing will continue in background${NC}"
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ batch_uninstall_applications() {
|
|||||||
|
|
||||||
local brew_tag=""
|
local brew_tag=""
|
||||||
[[ "$is_brew_cask" == "true" ]] && brew_tag=" ${CYAN}[Brew]${NC}"
|
[[ "$is_brew_cask" == "true" ]] && brew_tag=" ${CYAN}[Brew]${NC}"
|
||||||
echo -e "${BLUE}${ICON_CONFIRM}${NC} ${app_name}${brew_tag} ${GRAY}(${app_size_display})${NC}"
|
echo -e "${BLUE}${ICON_CONFIRM}${NC} ${app_name}${brew_tag} ${GRAY}, ${app_size_display}${NC}"
|
||||||
|
|
||||||
# Show detailed file list for ALL apps (brew casks leave user data behind)
|
# Show detailed file list for ALL apps (brew casks leave user data behind)
|
||||||
local related_files=$(decode_file_list "$encoded_files" "$app_name")
|
local related_files=$(decode_file_list "$encoded_files" "$app_name")
|
||||||
@@ -352,7 +352,7 @@ batch_uninstall_applications() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
local removal_note="Remove ${app_total} ${app_text}"
|
local removal_note="Remove ${app_total} ${app_text}"
|
||||||
[[ -n "$size_display" ]] && removal_note+=" (${size_display})"
|
[[ -n "$size_display" ]] && removal_note+=", ${size_display}"
|
||||||
if [[ ${#running_apps[@]} -gt 0 ]]; then
|
if [[ ${#running_apps[@]} -gt 0 ]]; then
|
||||||
removal_note+=" ${YELLOW}[Running]${NC}"
|
removal_note+=" ${YELLOW}[Running]${NC}"
|
||||||
fi
|
fi
|
||||||
@@ -516,7 +516,7 @@ batch_uninstall_applications() {
|
|||||||
# Show failure
|
# Show failure
|
||||||
if [[ -t 1 ]]; then
|
if [[ -t 1 ]]; then
|
||||||
if [[ ${#app_details[@]} -gt 1 ]]; then
|
if [[ ${#app_details[@]} -gt 1 ]]; then
|
||||||
echo -e "${ICON_ERROR} [$current_index/${#app_details[@]}] ${app_name} ${GRAY}($reason)${NC}"
|
echo -e "${ICON_ERROR} [$current_index/${#app_details[@]}] ${app_name} ${GRAY}, $reason${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${ICON_ERROR} ${app_name} failed: $reason"
|
echo -e "${ICON_ERROR} ${app_name} failed: $reason"
|
||||||
fi
|
fi
|
||||||
@@ -592,7 +592,7 @@ batch_uninstall_applications() {
|
|||||||
still*running*) reason_summary="is still running" ;;
|
still*running*) reason_summary="is still running" ;;
|
||||||
remove*failed*) reason_summary="could not be removed" ;;
|
remove*failed*) reason_summary="could not be removed" ;;
|
||||||
permission*denied*) reason_summary="permission denied" ;;
|
permission*denied*) reason_summary="permission denied" ;;
|
||||||
owned*by*) reason_summary="$first_reason (try with sudo)" ;;
|
owned*by*) reason_summary="$first_reason, try with sudo" ;;
|
||||||
*) reason_summary="$first_reason" ;;
|
*) reason_summary="$first_reason" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|||||||
27
mole
27
mole
@@ -247,14 +247,14 @@ update_mole() {
|
|||||||
|
|
||||||
if [[ -z "$latest" ]]; then
|
if [[ -z "$latest" ]]; then
|
||||||
log_error "Unable to check for updates. Check network connection."
|
log_error "Unable to check for updates. Check network connection."
|
||||||
echo -e "${YELLOW}Tip:${NC} Check if you can access GitHub (https://github.com)"
|
echo -e "${YELLOW}Tip:${NC} Check if you can access GitHub, https://github.com"
|
||||||
echo -e "${YELLOW}Tip:${NC} Try again with: ${GRAY}mo update${NC}"
|
echo -e "${YELLOW}Tip:${NC} Try again with: ${GRAY}mo update${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$VERSION" == "$latest" && "$force_update" != "true" ]]; then
|
if [[ "$VERSION" == "$latest" && "$force_update" != "true" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version (${VERSION})"
|
echo -e "${GREEN}${ICON_SUCCESS}${NC} Already on latest version, ${VERSION}"
|
||||||
echo ""
|
echo ""
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@@ -278,7 +278,7 @@ update_mole() {
|
|||||||
local curl_exit=$?
|
local curl_exit=$?
|
||||||
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||||
rm -f "$tmp_installer"
|
rm -f "$tmp_installer"
|
||||||
log_error "Update failed (curl error: $curl_exit)"
|
log_error "Update failed, curl error: $curl_exit"
|
||||||
|
|
||||||
case $curl_exit in
|
case $curl_exit in
|
||||||
6) echo -e "${YELLOW}Tip:${NC} Could not resolve host. Check DNS or network connection." ;;
|
6) echo -e "${YELLOW}Tip:${NC} Could not resolve host. Check DNS or network connection." ;;
|
||||||
@@ -294,7 +294,7 @@ update_mole() {
|
|||||||
download_error=$(wget --timeout=10 --tries=3 -qO "$tmp_installer" "$installer_url" 2>&1) || {
|
download_error=$(wget --timeout=10 --tries=3 -qO "$tmp_installer" "$installer_url" 2>&1) || {
|
||||||
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||||
rm -f "$tmp_installer"
|
rm -f "$tmp_installer"
|
||||||
log_error "Update failed (wget error)"
|
log_error "Update failed, wget error"
|
||||||
echo -e "${YELLOW}Tip:${NC} Check network connection and try again."
|
echo -e "${YELLOW}Tip:${NC} Check network connection and try again."
|
||||||
echo -e "${YELLOW}Tip:${NC} URL: $installer_url"
|
echo -e "${YELLOW}Tip:${NC} URL: $installer_url"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -324,7 +324,7 @@ update_mole() {
|
|||||||
|
|
||||||
if [[ "$requires_sudo" == "true" ]]; then
|
if [[ "$requires_sudo" == "true" ]]; then
|
||||||
if ! request_sudo_access "Mole update requires admin access"; then
|
if ! request_sudo_access "Mole update requires admin access"; then
|
||||||
log_error "Update aborted (admin access denied)"
|
log_error "Update aborted, admin access denied"
|
||||||
rm -f "$tmp_installer"
|
rm -f "$tmp_installer"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -349,14 +349,17 @@ update_mole() {
|
|||||||
|
|
||||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||||
local new_version
|
local new_version
|
||||||
new_version=$(printf '%s\n' "$output" | sed -n 's/.*(version \([^)]*\)).*/\1/p' | head -1)
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*-> \([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
if [[ -z "$new_version" ]]; then
|
||||||
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*version[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version=$("$mole_path" --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
new_version=$("$mole_path" --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
||||||
fi
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version="$fallback_version"
|
new_version="$fallback_version"
|
||||||
fi
|
fi
|
||||||
printf '\n%s\n\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
printf '\n%s\n\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-unknown}"
|
||||||
else
|
else
|
||||||
printf '\n'
|
printf '\n'
|
||||||
fi
|
fi
|
||||||
@@ -484,15 +487,15 @@ remove_mole() {
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${YELLOW}Remove Mole${NC} - will delete the following:"
|
echo -e "${YELLOW}Remove Mole${NC}, will delete the following:"
|
||||||
if [[ "$is_homebrew" == "true" ]]; then
|
if [[ "$is_homebrew" == "true" ]]; then
|
||||||
echo " - Mole via Homebrew"
|
echo " * Mole via Homebrew"
|
||||||
fi
|
fi
|
||||||
for install in ${manual_installs[@]+"${manual_installs[@]}"} ${alias_installs[@]+"${alias_installs[@]}"}; do
|
for install in ${manual_installs[@]+"${manual_installs[@]}"} ${alias_installs[@]+"${alias_installs[@]}"}; do
|
||||||
echo " - $install"
|
echo " * $install"
|
||||||
done
|
done
|
||||||
echo " - ~/.config/mole"
|
echo " * ~/.config/mole"
|
||||||
echo " - ~/.cache/mole"
|
echo " * ~/.cache/mole"
|
||||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: "
|
echo -ne "${PURPLE}${ICON_ARROW}${NC} Press ${GREEN}Enter${NC} to confirm, ${GRAY}ESC${NC} to cancel: "
|
||||||
|
|
||||||
IFS= read -r -s -n1 key || key=""
|
IFS= read -r -s -n1 key || key=""
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ usage() {
|
|||||||
Usage: ./scripts/check.sh [--format|--no-format]
|
Usage: ./scripts/check.sh [--format|--no-format]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--format Apply formatting fixes only (shfmt, gofmt)
|
--format Apply formatting fixes only, shfmt, gofmt
|
||||||
--no-format Skip formatting and run checks only
|
--no-format Skip formatting and run checks only
|
||||||
--help Show this help
|
--help Show this help
|
||||||
EOF
|
EOF
|
||||||
@@ -55,7 +55,7 @@ readonly ICON_ERROR="☻"
|
|||||||
readonly ICON_WARNING="●"
|
readonly ICON_WARNING="●"
|
||||||
readonly ICON_LIST="•"
|
readonly ICON_LIST="•"
|
||||||
|
|
||||||
echo -e "${BLUE}=== Mole Check (${MODE}) ===${NC}\n"
|
echo -e "${BLUE}=== Mole Check, ${MODE} ===${NC}\n"
|
||||||
|
|
||||||
SHELL_FILES=$(find . -type f \( -name "*.sh" -o -name "mole" \) \
|
SHELL_FILES=$(find . -type f \( -name "*.sh" -o -name "mole" \) \
|
||||||
-not -path "./.git/*" \
|
-not -path "./.git/*" \
|
||||||
@@ -75,11 +75,11 @@ if [[ "$MODE" == "format" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v goimports > /dev/null 2>&1; then
|
if command -v goimports > /dev/null 2>&1; then
|
||||||
echo -e "${YELLOW}Formatting Go code (goimports)...${NC}"
|
echo -e "${YELLOW}Formatting Go code, goimports...${NC}"
|
||||||
goimports -w -local github.com/tw93/Mole ./cmd
|
goimports -w -local github.com/tw93/Mole ./cmd
|
||||||
echo -e "${GREEN}${ICON_SUCCESS} Go formatting complete${NC}\n"
|
echo -e "${GREEN}${ICON_SUCCESS} Go formatting complete${NC}\n"
|
||||||
elif command -v go > /dev/null 2>&1; then
|
elif command -v go > /dev/null 2>&1; then
|
||||||
echo -e "${YELLOW}Formatting Go code (gofmt)...${NC}"
|
echo -e "${YELLOW}Formatting Go code, gofmt...${NC}"
|
||||||
gofmt -w ./cmd
|
gofmt -w ./cmd
|
||||||
echo -e "${GREEN}${ICON_SUCCESS} Go formatting complete${NC}\n"
|
echo -e "${GREEN}${ICON_SUCCESS} Go formatting complete${NC}\n"
|
||||||
else
|
else
|
||||||
@@ -100,11 +100,11 @@ if [[ "$MODE" != "check" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v goimports > /dev/null 2>&1; then
|
if command -v goimports > /dev/null 2>&1; then
|
||||||
echo -e "${YELLOW}2. Formatting Go code (goimports)...${NC}"
|
echo -e "${YELLOW}2. Formatting Go code, goimports...${NC}"
|
||||||
goimports -w -local github.com/tw93/Mole ./cmd
|
goimports -w -local github.com/tw93/Mole ./cmd
|
||||||
echo -e "${GREEN}${ICON_SUCCESS} Go formatting applied${NC}\n"
|
echo -e "${GREEN}${ICON_SUCCESS} Go formatting applied${NC}\n"
|
||||||
elif command -v go > /dev/null 2>&1; then
|
elif command -v go > /dev/null 2>&1; then
|
||||||
echo -e "${YELLOW}2. Formatting Go code (gofmt)...${NC}"
|
echo -e "${YELLOW}2. Formatting Go code, gofmt...${NC}"
|
||||||
gofmt -w ./cmd
|
gofmt -w ./cmd
|
||||||
echo -e "${GREEN}${ICON_SUCCESS} Go formatting applied${NC}\n"
|
echo -e "${GREEN}${ICON_SUCCESS} Go formatting applied${NC}\n"
|
||||||
fi
|
fi
|
||||||
@@ -148,18 +148,18 @@ fi
|
|||||||
|
|
||||||
echo -e "${YELLOW}5. Running syntax check...${NC}"
|
echo -e "${YELLOW}5. Running syntax check...${NC}"
|
||||||
if ! bash -n mole; then
|
if ! bash -n mole; then
|
||||||
echo -e "${RED}${ICON_ERROR} Syntax check failed (mole)${NC}\n"
|
echo -e "${RED}${ICON_ERROR} Syntax check failed, mole${NC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
for script in bin/*.sh; do
|
for script in bin/*.sh; do
|
||||||
if ! bash -n "$script"; then
|
if ! bash -n "$script"; then
|
||||||
echo -e "${RED}${ICON_ERROR} Syntax check failed ($script)${NC}\n"
|
echo -e "${RED}${ICON_ERROR} Syntax check failed, $script${NC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
find lib -name "*.sh" | while read -r script; do
|
find lib -name "*.sh" | while read -r script; do
|
||||||
if ! bash -n "$script"; then
|
if ! bash -n "$script"; then
|
||||||
echo -e "${RED}${ICON_ERROR} Syntax check failed ($script)${NC}\n"
|
echo -e "${RED}${ICON_ERROR} Syntax check failed, $script${NC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ ${command}
|
|||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
EOF
|
EOF
|
||||||
log_success "Workflow ready: ${name} (keyword: ${keyword})"
|
log_success "Workflow ready: ${name}, keyword: ${keyword}"
|
||||||
done
|
done
|
||||||
|
|
||||||
log_step "Open Alfred preferences → Workflows if you need to adjust keywords."
|
log_step "Open Alfred preferences → Workflows if you need to adjust keywords."
|
||||||
@@ -413,11 +413,11 @@ main() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
log_success "Done! Raycast and Alfred are ready with 5 commands:"
|
log_success "Done! Raycast and Alfred are ready with 5 commands:"
|
||||||
echo " • clean - Deep system cleanup"
|
echo " • clean, Deep system cleanup"
|
||||||
echo " • uninstall - Remove applications"
|
echo " • uninstall, Remove applications"
|
||||||
echo " • optimize - System health & tuning"
|
echo " • optimize, System health & tuning"
|
||||||
echo " • analyze - Disk space explorer"
|
echo " • analyze, Disk space explorer"
|
||||||
echo " • status - Live system monitor"
|
echo " • status, Live system monitor"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ echo ""
|
|||||||
echo "6. Testing installation..."
|
echo "6. Testing installation..."
|
||||||
# Skip if Homebrew mole is installed (install.sh will refuse to overwrite)
|
# Skip if Homebrew mole is installed (install.sh will refuse to overwrite)
|
||||||
if brew list mole &> /dev/null; then
|
if brew list mole &> /dev/null; then
|
||||||
printf "${GREEN}${ICON_SUCCESS} Installation test skipped (Homebrew)${NC}\n"
|
printf "${GREEN}${ICON_SUCCESS} Installation test skipped, Homebrew${NC}\n"
|
||||||
elif ./install.sh --prefix /tmp/mole-test > /dev/null 2>&1; then
|
elif ./install.sh --prefix /tmp/mole-test > /dev/null 2>&1; then
|
||||||
if [ -f /tmp/mole-test/mole ]; then
|
if [ -f /tmp/mole-test/mole ]; then
|
||||||
printf "${GREEN}${ICON_SUCCESS} Installation test passed${NC}\n"
|
printf "${GREEN}${ICON_SUCCESS} Installation test passed${NC}\n"
|
||||||
@@ -203,5 +203,5 @@ if [[ $FAILED -eq 0 ]]; then
|
|||||||
printf "${GREEN}${ICON_SUCCESS} All tests passed!${NC}\n"
|
printf "${GREEN}${ICON_SUCCESS} All tests passed!${NC}\n"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
printf "${RED}${ICON_ERROR} $FAILED test(s) failed!${NC}\n"
|
printf "${RED}${ICON_ERROR} $FAILED tests failed!${NC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ ask_for_updates
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
[ "$status" -eq 1 ] # ESC cancels
|
[ "$status" -eq 1 ] # ESC cancels
|
||||||
[[ "$output" == *"Homebrew (5 updates)"* ]]
|
[[ "$output" == *"Homebrew, 3 formula, 2 cask"* ]]
|
||||||
[[ "$output" == *"App Store (1 apps)"* ]]
|
[[ "$output" == *"App Store, 1 apps"* ]]
|
||||||
[[ "$output" == *"macOS system"* ]]
|
[[ "$output" == *"macOS system"* ]]
|
||||||
[[ "$output" == *"Mole"* ]]
|
[[ "$output" == *"Mole"* ]]
|
||||||
}
|
}
|
||||||
@@ -253,24 +253,27 @@ process_install_output() {
|
|||||||
|
|
||||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||||
local new_version
|
local new_version
|
||||||
new_version=$(printf '%s\n' "$output" | sed -n 's/.*(version \([^)]*\)).*/\1/p' | head -1)
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*-> \([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
if [[ -z "$new_version" ]]; then
|
||||||
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*version[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
||||||
fi
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version="$fallback_version"
|
new_version="$fallback_version"
|
||||||
fi
|
fi
|
||||||
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-unknown}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
output="Installing Mole...
|
output="Installing Mole...
|
||||||
◎ Mole installed successfully (version 1.23.1)"
|
◎ Mole installed successfully, version 1.23.1"
|
||||||
process_install_output "$output" "1.23.0"
|
process_install_output "$output" "1.23.0"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == *"Updated to latest version (1.23.1)"* ]]
|
[[ "$output" == *"Updated to latest version, 1.23.1"* ]]
|
||||||
[[ "$output" != *"1.23.0"* ]]
|
[[ "$output" != *"1.23.0"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,14 +296,17 @@ process_install_output() {
|
|||||||
|
|
||||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||||
local new_version
|
local new_version
|
||||||
new_version=$(printf '%s\n' "$output" | sed -n 's/.*(version \([^)]*\)).*/\1/p' | head -1)
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*-> \([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
if [[ -z "$new_version" ]]; then
|
||||||
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*version[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
||||||
fi
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version="$fallback_version"
|
new_version="$fallback_version"
|
||||||
fi
|
fi
|
||||||
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-unknown}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +317,7 @@ EOF
|
|||||||
|
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == *"Installation completed"* ]]
|
[[ "$output" == *"Installation completed"* ]]
|
||||||
[[ "$output" == *"Updated to latest version (1.23.1)"* ]]
|
[[ "$output" == *"Updated to latest version, 1.23.1"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "process_install_output handles empty output with fallback version" {
|
@test "process_install_output handles empty output with fallback version" {
|
||||||
@@ -333,14 +339,17 @@ process_install_output() {
|
|||||||
|
|
||||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||||
local new_version
|
local new_version
|
||||||
new_version=$(printf '%s\n' "$output" | sed -n 's/.*(version \([^)]*\)).*/\1/p' | head -1)
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*-> \([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
if [[ -z "$new_version" ]]; then
|
||||||
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*version[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
||||||
fi
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version="$fallback_version"
|
new_version="$fallback_version"
|
||||||
fi
|
fi
|
||||||
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-unknown}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +358,7 @@ process_install_output "$output" "1.23.1"
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == *"Updated to latest version (1.23.1)"* ]]
|
[[ "$output" == *"Updated to latest version, 1.23.1"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "process_install_output does not extract wrong parentheses content" {
|
@test "process_install_output does not extract wrong parentheses content" {
|
||||||
@@ -371,14 +380,17 @@ process_install_output() {
|
|||||||
|
|
||||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||||
local new_version
|
local new_version
|
||||||
new_version=$(printf '%s\n' "$output" | sed -n 's/.*(version \([^)]*\)).*/\1/p' | head -1)
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*-> \([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
if [[ -z "$new_version" ]]; then
|
||||||
|
new_version=$(printf '%s\n' "$output" | sed -n 's/.*version[[:space:]]\{1,\}\([^[:space:]]\{1,\}\).*/\1/p' | head -1)
|
||||||
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
new_version=$(command -v mo > /dev/null 2>&1 && mo --version 2> /dev/null | awk 'NR==1 && NF {print $NF}' || echo "")
|
||||||
fi
|
fi
|
||||||
if [[ -z "$new_version" ]]; then
|
if [[ -z "$new_version" ]]; then
|
||||||
new_version="$fallback_version"
|
new_version="$fallback_version"
|
||||||
fi
|
fi
|
||||||
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
printf '\n%s\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version, ${new_version:-unknown}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +401,7 @@ EOF
|
|||||||
|
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == *"Downloading (progress: 100%)"* ]]
|
[[ "$output" == *"Downloading (progress: 100%)"* ]]
|
||||||
[[ "$output" == *"Updated to latest version (1.23.1)"* ]]
|
[[ "$output" == *"Updated to latest version, 1.23.1"* ]]
|
||||||
[[ "$output" != *"progress: 100%"* ]] || [[ "$output" == *"Downloading (progress: 100%)"* ]]
|
[[ "$output" != *"progress: 100%"* ]] || [[ "$output" == *"Downloading (progress: 100%)"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +430,7 @@ curl() {
|
|||||||
if [[ -n "$out" ]]; then
|
if [[ -n "$out" ]]; then
|
||||||
cat > "$out" << 'INSTALLER'
|
cat > "$out" << 'INSTALLER'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
echo "Mole installed successfully (version $CURRENT_VERSION)"
|
echo "Mole installed successfully, version $CURRENT_VERSION"
|
||||||
INSTALLER
|
INSTALLER
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user