1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 17:24:45 +00:00

fix: implement layered error tolerance and accurate cleanup reporting (#175 #176 #180)

- Fix safe_remove set -e trap in command substitution
  - Fix has_full_disk_access false positives and unknown state handling
  - Use set +e in perform_cleanup for graceful degradation
  - Track removal failures and only count actually deleted items (#180)
  - Add "Skipped X items (permission denied or in use)" notification
  - Improve spinner reliability with cooperative stop mechanism (#175)
This commit is contained in:
Tw93
2025-12-29 14:27:47 +08:00
parent 16de9d13a8
commit 694c55f73b
15 changed files with 228 additions and 546 deletions

View File

@@ -1,13 +1,11 @@
#!/bin/bash
# Developer Tools Cleanup Module
set -euo pipefail
# Helper function to clean tool caches using their built-in commands
# Args: $1 - description, $@ - command to execute
# Env: DRY_RUN
# Note: Try to estimate potential savings (many tool caches don't have a direct path,
# so we just report the action if we can't easily find a path)
# Note: Try to estimate potential savings (many tool caches don't have a direct path,
clean_tool_cache() {
local description="$1"
shift
@@ -18,30 +16,27 @@ clean_tool_cache() {
else
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} $description · would clean"
fi
return 0
}
# Clean npm cache (command + directories)
# npm cache clean clears official npm cache, safe_clean handles alternative package managers
# Env: DRY_RUN
# npm cache clean clears official npm cache, safe_clean handles alternative package managers
clean_dev_npm() {
if command -v npm > /dev/null 2>&1; then
# clean_tool_cache now calculates size before cleanup for better statistics
clean_tool_cache "npm cache" npm cache clean --force
note_activity
fi
# Clean pnpm store cache
local pnpm_default_store=~/Library/pnpm/store
if command -v pnpm > /dev/null 2>&1; then
# Use pnpm's built-in prune command
clean_tool_cache "pnpm cache" pnpm store prune
# Get the actual store path to check if default is orphaned
local pnpm_store_path
start_section_spinner "Checking store path..."
pnpm_store_path=$(run_with_timeout 2 pnpm store path 2> /dev/null) || pnpm_store_path=""
stop_section_spinner
# If store path is different from default, clean the orphaned default
if [[ -n "$pnpm_store_path" && "$pnpm_store_path" != "$pnpm_default_store" ]]; then
safe_clean "$pnpm_default_store"/* "Orphaned pnpm store"
@@ -51,24 +46,21 @@ clean_dev_npm() {
safe_clean "$pnpm_default_store"/* "pnpm store"
fi
note_activity
# Clean alternative package manager caches
safe_clean ~/.tnpm/_cacache/* "tnpm cache directory"
safe_clean ~/.tnpm/_logs/* "tnpm logs"
safe_clean ~/.yarn/cache/* "Yarn cache"
safe_clean ~/.bun/install/cache/* "Bun cache"
}
# Clean Python/pip cache (command + directories)
# pip cache purge clears official pip cache, safe_clean handles other Python tools
# Env: DRY_RUN
# pip cache purge clears official pip cache, safe_clean handles other Python tools
clean_dev_python() {
if command -v pip3 > /dev/null 2>&1; then
# clean_tool_cache now calculates size before cleanup for better statistics
clean_tool_cache "pip cache" bash -c 'pip3 cache purge >/dev/null 2>&1 || true'
note_activity
fi
# Clean Python ecosystem caches
safe_clean ~/.pyenv/cache/* "pyenv cache"
safe_clean ~/.cache/poetry/* "Poetry cache"
@@ -84,10 +76,9 @@ clean_dev_python() {
safe_clean ~/anaconda3/pkgs/* "Anaconda packages cache"
safe_clean ~/.cache/wandb/* "Weights & Biases cache"
}
# Clean Go cache (command + directories)
# go clean handles build and module caches comprehensively
# Env: DRY_RUN
# go clean handles build and module caches comprehensively
clean_dev_go() {
if command -v go > /dev/null 2>&1; then
# clean_tool_cache now calculates size before cleanup for better statistics
@@ -95,16 +86,14 @@ clean_dev_go() {
note_activity
fi
}
# Clean Rust/cargo cache directories
clean_dev_rust() {
safe_clean ~/.cargo/registry/cache/* "Rust cargo cache"
safe_clean ~/.cargo/git/* "Cargo git cache"
safe_clean ~/.rustup/downloads/* "Rust downloads cache"
}
# Clean Docker cache (command + directories)
# Env: DRY_RUN
# Clean Docker cache (command + directories)
clean_dev_docker() {
if command -v docker > /dev/null 2>&1; then
if [[ "$DRY_RUN" != "true" ]]; then
@@ -115,7 +104,6 @@ clean_dev_docker() {
docker_running=true
fi
stop_section_spinner
if [[ "$docker_running" == "true" ]]; then
clean_tool_cache "Docker build cache" docker builder prune -af
else
@@ -131,7 +119,6 @@ clean_dev_docker() {
retry_success=true
fi
stop_section_spinner
if [[ "$retry_success" == "true" ]]; then
clean_tool_cache "Docker build cache" docker builder prune -af
else
@@ -150,12 +137,10 @@ clean_dev_docker() {
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Docker build cache · would clean"
fi
fi
safe_clean ~/.docker/buildx/cache/* "Docker BuildX cache"
}
# Clean Nix package manager
# Env: DRY_RUN
# Clean Nix package manager
clean_dev_nix() {
if command -v nix-collect-garbage > /dev/null 2>&1; then
if [[ "$DRY_RUN" != "true" ]]; then
@@ -166,7 +151,6 @@ clean_dev_nix() {
note_activity
fi
}
# Clean cloud CLI tools cache
clean_dev_cloud() {
safe_clean ~/.kube/cache/* "Kubernetes cache"
@@ -175,7 +159,6 @@ clean_dev_cloud() {
safe_clean ~/.config/gcloud/logs/* "Google Cloud logs"
safe_clean ~/.azure/logs/* "Azure CLI logs"
}
# Clean frontend build tool caches
clean_dev_frontend() {
safe_clean ~/.cache/typescript/* "TypeScript cache"
@@ -190,23 +173,20 @@ clean_dev_frontend() {
safe_clean ~/.cache/eslint/* "ESLint cache"
safe_clean ~/.cache/prettier/* "Prettier cache"
}
# Clean mobile development tools
# iOS simulator cleanup can free significant space (70GB+ in some cases)
# DeviceSupport files accumulate for each iOS version connected
# Simulator runtime caches can grow large over time
# DeviceSupport files accumulate for each iOS version connected
clean_dev_mobile() {
# Clean Xcode unavailable simulators
# Removes old and unused local iOS simulator data from old unused runtimes
# Can free up significant space (70GB+ in some cases)
if command -v xcrun > /dev/null 2>&1; then
debug_log "Checking for unavailable Xcode simulators"
if [[ "$DRY_RUN" == "true" ]]; then
clean_tool_cache "Xcode unavailable simulators" xcrun simctl delete unavailable
else
start_section_spinner "Checking unavailable simulators..."
# Run command manually to control UI output order
if xcrun simctl delete unavailable > /dev/null 2>&1; then
stop_section_spinner
@@ -218,7 +198,6 @@ clean_dev_mobile() {
fi
note_activity
fi
# Clean iOS DeviceSupport - more comprehensive cleanup
# DeviceSupport directories store debug symbols for each iOS version
# Safe to clean caches and logs, but preserve device support files themselves
@@ -226,11 +205,9 @@ clean_dev_mobile() {
safe_clean ~/Library/Developer/Xcode/iOS\ DeviceSupport/*.log "iOS device support logs"
safe_clean ~/Library/Developer/Xcode/watchOS\ DeviceSupport/*/Symbols/System/Library/Caches/* "watchOS device symbol cache"
safe_clean ~/Library/Developer/Xcode/tvOS\ DeviceSupport/*/Symbols/System/Library/Caches/* "tvOS device symbol cache"
# Clean simulator runtime caches
# RuntimeRoot caches can accumulate system library caches
safe_clean ~/Library/Developer/CoreSimulator/Profiles/Runtimes/*/Contents/Resources/RuntimeRoot/System/Library/Caches/* "Simulator runtime cache"
safe_clean ~/Library/Caches/Google/AndroidStudio*/* "Android Studio cache"
safe_clean ~/Library/Caches/CocoaPods/* "CocoaPods cache"
safe_clean ~/.cache/flutter/* "Flutter cache"
@@ -239,7 +216,6 @@ clean_dev_mobile() {
safe_clean ~/Library/Developer/Xcode/UserData/IB\ Support/* "Xcode Interface Builder cache"
safe_clean ~/.cache/swift-package-manager/* "Swift package manager cache"
}
# Clean JVM ecosystem tools
clean_dev_jvm() {
safe_clean ~/.gradle/caches/* "Gradle caches"
@@ -247,7 +223,6 @@ clean_dev_jvm() {
safe_clean ~/.sbt/* "SBT cache"
safe_clean ~/.ivy2/cache/* "Ivy cache"
}
# Clean other language tools
clean_dev_other_langs() {
safe_clean ~/.bundle/cache/* "Ruby Bundler cache"
@@ -258,7 +233,6 @@ clean_dev_other_langs() {
safe_clean ~/.cache/zig/* "Zig cache"
safe_clean ~/Library/Caches/deno/* "Deno cache"
}
# Clean CI/CD and DevOps tools
clean_dev_cicd() {
safe_clean ~/.cache/terraform/* "Terraform cache"
@@ -270,7 +244,6 @@ clean_dev_cicd() {
safe_clean ~/.circleci/cache/* "CircleCI cache"
safe_clean ~/.sonar/* "SonarQube cache"
}
# Clean database tools
clean_dev_database() {
safe_clean ~/Library/Caches/com.sequel-ace.sequel-ace/* "Sequel Ace cache"
@@ -280,7 +253,6 @@ clean_dev_database() {
safe_clean ~/Library/Caches/com.dbeaver.* "DBeaver cache"
safe_clean ~/Library/Caches/com.redis.RedisInsight "Redis Insight cache"
}
# Clean API/network debugging tools
clean_dev_api_tools() {
safe_clean ~/Library/Caches/com.postmanlabs.mac/* "Postman cache"
@@ -290,7 +262,6 @@ clean_dev_api_tools() {
safe_clean ~/Library/Caches/com.charlesproxy.charles/* "Charles Proxy cache"
safe_clean ~/Library/Caches/com.proxyman.NSProxy/* "Proxyman cache"
}
# Clean misc dev tools
clean_dev_misc() {
safe_clean ~/Library/Caches/com.unity3d.*/* "Unity cache"
@@ -301,7 +272,6 @@ clean_dev_misc() {
safe_clean ~/Library/Caches/KSCrash/* "KSCrash reports"
safe_clean ~/Library/Caches/com.crashlytics.data/* "Crashlytics data"
}
# Clean shell and version control
clean_dev_shell() {
safe_clean ~/.gitconfig.lock "Git config lock"
@@ -312,7 +282,6 @@ clean_dev_shell() {
safe_clean ~/.zsh_history.bak* "Zsh history backup"
safe_clean ~/.cache/pre-commit/* "pre-commit cache"
}
# Clean network utilities
clean_dev_network() {
safe_clean ~/.cache/curl/* "curl cache"
@@ -320,26 +289,22 @@ clean_dev_network() {
safe_clean ~/Library/Caches/curl/* "macOS curl cache"
safe_clean ~/Library/Caches/wget/* "macOS wget cache"
}
# Clean orphaned SQLite temporary files (-shm and -wal files)
# Strategy: Only clean truly orphaned temp files where base database is missing
# This is fast and safe - skip complex checks for files with existing base DB
# Env: DRY_RUN
# This is fast and safe - skip complex checks for files with existing base DB
clean_sqlite_temp_files() {
# Skip this cleanup due to low ROI (收益比低,经常没东西可清理)
# Find scan is still slow even optimized, and orphaned files are rare
return 0
}
# Main developer tools cleanup function
# Calls all specialized cleanup functions
# Env: DRY_RUN
# Calls all specialized cleanup functions
clean_developer_tools() {
stop_section_spinner
# Clean SQLite temporary files first
clean_sqlite_temp_files
clean_dev_npm
clean_dev_python
clean_dev_go
@@ -349,10 +314,8 @@ clean_developer_tools() {
clean_dev_nix
clean_dev_shell
clean_dev_frontend
# Project build caches (delegated to clean_caches module)
clean_project_caches
clean_dev_mobile
clean_dev_jvm
clean_dev_other_langs
@@ -361,16 +324,13 @@ clean_developer_tools() {
clean_dev_api_tools
clean_dev_network
clean_dev_misc
# Homebrew caches and cleanup (delegated to clean_brew module)
safe_clean ~/Library/Caches/Homebrew/* "Homebrew cache"
# Clean Homebrew locks intelligently (avoid repeated sudo prompts)
local brew_lock_dirs=(
"/opt/homebrew/var/homebrew/locks"
"/usr/local/var/homebrew/locks"
)
for lock_dir in "${brew_lock_dirs[@]}"; do
if [[ -d "$lock_dir" && -w "$lock_dir" ]]; then
# User can write, safe to clean
@@ -384,6 +344,5 @@ clean_developer_tools() {
fi
fi
done
clean_homebrew
}