mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 14:26:46 +00:00
Clean performance speed optimization
This commit is contained in:
19
bin/clean.sh
19
bin/clean.sh
@@ -238,7 +238,7 @@ safe_clean() {
|
||||
# Show progress indicator for potentially slow operations
|
||||
if [[ ${#existing_paths[@]} -gt 3 ]]; then
|
||||
local total_paths=${#existing_paths[@]}
|
||||
if [[ -t 1 ]]; then MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning $total_paths items..."; fi
|
||||
if [[ -t 1 ]]; then MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning items..."; fi
|
||||
local temp_dir
|
||||
# create_temp_dir uses mktemp -d for secure temporary directory creation
|
||||
temp_dir=$(create_temp_dir)
|
||||
@@ -269,11 +269,6 @@ safe_clean() {
|
||||
wait "${pids[0]}" 2> /dev/null || true
|
||||
pids=("${pids[@]:1}")
|
||||
((completed++))
|
||||
# Update progress less frequently to reduce overhead
|
||||
if [[ -t 1 ]] && ((completed % 20 == 0)); then
|
||||
stop_inline_spinner
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning items ($completed/$total_paths)..."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -290,11 +285,6 @@ safe_clean() {
|
||||
read -r size count < "$result_file" 2> /dev/null || true
|
||||
if [[ "$count" -gt 0 && "$size" -gt 0 ]]; then
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# Update spinner to show cleaning progress
|
||||
if [[ -t 1 ]] && ((idx % 5 == 0)); then
|
||||
stop_inline_spinner
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Cleaning items ($idx/$total_paths)..."
|
||||
fi
|
||||
# Handle symbolic links separately (only remove the link, not the target)
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2> /dev/null || true
|
||||
@@ -314,7 +304,7 @@ safe_clean() {
|
||||
else
|
||||
# Show progress for small batches too (simpler jobs)
|
||||
local total_paths=${#existing_paths[@]}
|
||||
if [[ -t 1 ]]; then MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning $total_paths items..."; fi
|
||||
if [[ -t 1 ]]; then MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning items..."; fi
|
||||
|
||||
local idx=0
|
||||
for path in "${existing_paths[@]}"; do
|
||||
@@ -324,11 +314,6 @@ safe_clean() {
|
||||
# Optimization: Skip expensive file counting
|
||||
if [[ "$size_bytes" -gt 0 ]]; then
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# Update spinner to show cleaning progress for slow operations
|
||||
if [[ -t 1 ]]; then
|
||||
stop_inline_spinner
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Cleaning $description..."
|
||||
fi
|
||||
# Handle symbolic links separately (only remove the link, not the target)
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2> /dev/null || true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Clean Homebrew caches and remove orphaned dependencies
|
||||
# Skips if run within 2 days, runs cleanup/autoremove in parallel with 120s timeout
|
||||
# Skips if run within 7 days, runs cleanup/autoremove in parallel with 120s timeout
|
||||
# Env: MO_BREW_TIMEOUT, DRY_RUN
|
||||
clean_homebrew() {
|
||||
command -v brew > /dev/null 2>&1 || return 0
|
||||
@@ -12,9 +12,10 @@ clean_homebrew() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Smart caching: check if brew cleanup was run recently (within 2 days)
|
||||
# Smart caching: check if brew cleanup was run recently (within 7 days)
|
||||
# Extended from 2 days to 7 days to reduce cleanup frequency
|
||||
local brew_cache_file="${HOME}/.cache/mole/brew_last_cleanup"
|
||||
local cache_valid_days=2
|
||||
local cache_valid_days=7
|
||||
local should_skip=false
|
||||
|
||||
if [[ -f "$brew_cache_file" ]]; then
|
||||
@@ -33,35 +34,62 @@ clean_homebrew() {
|
||||
|
||||
[[ "$should_skip" == "true" ]] && return 0
|
||||
|
||||
# Quick pre-check: determine if cleanup is needed based on cache size (<50MB)
|
||||
# Use timeout to prevent slow du on very large caches
|
||||
# If timeout occurs, assume cache is large and run cleanup
|
||||
local skip_cleanup=false
|
||||
local brew_cache_size=0
|
||||
if [[ -d ~/Library/Caches/Homebrew ]]; then
|
||||
brew_cache_size=$(run_with_timeout 3 du -sk ~/Library/Caches/Homebrew 2>/dev/null | awk '{print $1}')
|
||||
local du_exit=$?
|
||||
|
||||
# Skip cleanup (but still run autoremove) if cache is small
|
||||
if [[ $du_exit -eq 0 && -n "$brew_cache_size" && "$brew_cache_size" -lt 51200 ]]; then
|
||||
skip_cleanup=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display appropriate spinner message
|
||||
if [[ -t 1 ]]; then
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Homebrew cleanup and autoremove..."
|
||||
if [[ "$skip_cleanup" == "true" ]]; then
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Homebrew autoremove (cleanup skipped)..."
|
||||
else
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Homebrew cleanup and autoremove..."
|
||||
fi
|
||||
fi
|
||||
|
||||
local timeout_seconds=${MO_BREW_TIMEOUT:-120}
|
||||
|
||||
# Run brew cleanup and autoremove in parallel for performance
|
||||
# Run brew cleanup and/or autoremove based on cache size
|
||||
local brew_tmp_file autoremove_tmp_file
|
||||
brew_tmp_file=$(create_temp_file)
|
||||
local brew_pid autoremove_pid
|
||||
|
||||
if [[ "$skip_cleanup" == "false" ]]; then
|
||||
brew_tmp_file=$(create_temp_file)
|
||||
(brew cleanup > "$brew_tmp_file" 2>&1) &
|
||||
brew_pid=$!
|
||||
fi
|
||||
|
||||
autoremove_tmp_file=$(create_temp_file)
|
||||
|
||||
(brew cleanup > "$brew_tmp_file" 2>&1) &
|
||||
local brew_pid=$!
|
||||
|
||||
(brew autoremove > "$autoremove_tmp_file" 2>&1) &
|
||||
local autoremove_pid=$!
|
||||
autoremove_pid=$!
|
||||
|
||||
local elapsed=0
|
||||
local brew_done=false
|
||||
local autoremove_done=false
|
||||
|
||||
# Mark cleanup as done if it was skipped
|
||||
[[ "$skip_cleanup" == "true" ]] && brew_done=true
|
||||
|
||||
# Wait for both to complete or timeout
|
||||
while [[ "$brew_done" == "false" ]] || [[ "$autoremove_done" == "false" ]]; do
|
||||
if [[ $elapsed -ge $timeout_seconds ]]; then
|
||||
kill -TERM $brew_pid $autoremove_pid 2> /dev/null || true
|
||||
[[ -n "$brew_pid" ]] && kill -TERM $brew_pid 2> /dev/null || true
|
||||
kill -TERM $autoremove_pid 2> /dev/null || true
|
||||
break
|
||||
fi
|
||||
|
||||
kill -0 $brew_pid 2> /dev/null || brew_done=true
|
||||
[[ -n "$brew_pid" ]] && { kill -0 $brew_pid 2> /dev/null || brew_done=true; }
|
||||
kill -0 $autoremove_pid 2> /dev/null || autoremove_done=true
|
||||
|
||||
sleep 1
|
||||
@@ -70,8 +98,10 @@ clean_homebrew() {
|
||||
|
||||
# Wait for processes to finish
|
||||
local brew_success=false
|
||||
if wait $brew_pid 2> /dev/null; then
|
||||
brew_success=true
|
||||
if [[ "$skip_cleanup" == "false" && -n "$brew_pid" ]]; then
|
||||
if wait $brew_pid 2> /dev/null; then
|
||||
brew_success=true
|
||||
fi
|
||||
fi
|
||||
|
||||
local autoremove_success=false
|
||||
@@ -82,7 +112,11 @@ clean_homebrew() {
|
||||
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||
|
||||
# Process cleanup output and extract metrics
|
||||
if [[ "$brew_success" == "true" && -f "$brew_tmp_file" ]]; then
|
||||
if [[ "$skip_cleanup" == "true" ]]; then
|
||||
# Cleanup was skipped due to small cache size
|
||||
local size_mb=$((brew_cache_size / 1024))
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Homebrew cleanup (cache ${size_mb}MB, skipped)"
|
||||
elif [[ "$brew_success" == "true" && -f "$brew_tmp_file" ]]; then
|
||||
local brew_output
|
||||
brew_output=$(cat "$brew_tmp_file" 2> /dev/null || echo "")
|
||||
local removed_count freed_space
|
||||
@@ -114,8 +148,9 @@ clean_homebrew() {
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Autoremove timed out (run ${GRAY}brew autoremove${NC} manually)"
|
||||
fi
|
||||
|
||||
# Update cache timestamp on successful completion
|
||||
if [[ "$brew_success" == "true" || "$autoremove_success" == "true" ]]; then
|
||||
# Update cache timestamp on successful completion or when cleanup was intelligently skipped
|
||||
# This prevents repeated cache size checks within the 7-day window
|
||||
if [[ "$skip_cleanup" == "true" ]] || [[ "$brew_success" == "true" ]] || [[ "$autoremove_success" == "true" ]]; then
|
||||
ensure_user_file "$brew_cache_file"
|
||||
date +%s > "$brew_cache_file"
|
||||
fi
|
||||
|
||||
@@ -125,6 +125,62 @@ clean_service_worker_cache() {
|
||||
# Clean Next.js (.next/cache) and Python (__pycache__) build caches
|
||||
# Uses maxdepth 3, excludes Library/.Trash/node_modules, 10s timeout per scan
|
||||
clean_project_caches() {
|
||||
# Quick check: skip if user likely doesn't have development projects
|
||||
local has_dev_projects=false
|
||||
local -a common_dev_dirs=(
|
||||
"$HOME/Code"
|
||||
"$HOME/Projects"
|
||||
"$HOME/workspace"
|
||||
"$HOME/github"
|
||||
"$HOME/dev"
|
||||
"$HOME/work"
|
||||
"$HOME/src"
|
||||
"$HOME/repos"
|
||||
"$HOME/Development"
|
||||
"$HOME/www"
|
||||
"$HOME/golang"
|
||||
"$HOME/go"
|
||||
"$HOME/rust"
|
||||
"$HOME/python"
|
||||
"$HOME/ruby"
|
||||
"$HOME/java"
|
||||
"$HOME/dotnet"
|
||||
"$HOME/node"
|
||||
)
|
||||
|
||||
for dir in "${common_dev_dirs[@]}"; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
has_dev_projects=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If no common dev directories found, perform feature-based detection
|
||||
# Check for project markers in $HOME (node_modules, .git, target, etc.)
|
||||
if [[ "$has_dev_projects" == "false" ]]; then
|
||||
local -a project_markers=(
|
||||
"node_modules"
|
||||
".git"
|
||||
"target"
|
||||
"go.mod"
|
||||
"Cargo.toml"
|
||||
"package.json"
|
||||
"pom.xml"
|
||||
"build.gradle"
|
||||
)
|
||||
|
||||
for marker in "${project_markers[@]}"; do
|
||||
# Quick check with maxdepth 2 and 3s timeout to avoid slow scans
|
||||
if run_with_timeout 3 sh -c "find '$HOME' -maxdepth 2 -name '$marker' -not -path '*/Library/*' -not -path '*/.Trash/*' 2>/dev/null | head -1" | grep -q .; then
|
||||
has_dev_projects=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If still no dev projects found, skip scanning
|
||||
[[ "$has_dev_projects" == "false" ]] && return 0
|
||||
fi
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
MOLE_SPINNER_PREFIX=" "
|
||||
start_inline_spinner "Searching project caches..."
|
||||
|
||||
@@ -14,8 +14,9 @@ clean_user_essentials() {
|
||||
scan_external_volumes() {
|
||||
[[ -d "/Volumes" ]] || return 0
|
||||
|
||||
# Fast pre-check: count non-system external volumes without expensive operations
|
||||
# Fast pre-check: collect non-system external volumes and detect network volumes
|
||||
local -a candidate_volumes=()
|
||||
local -a network_volumes=()
|
||||
for volume in /Volumes/*; do
|
||||
# Basic checks (directory, writable, not a symlink)
|
||||
[[ -d "$volume" && -w "$volume" && ! -L "$volume" ]] || continue
|
||||
@@ -23,26 +24,50 @@ scan_external_volumes() {
|
||||
# Skip system root if it appears in /Volumes
|
||||
[[ "$volume" == "/" || "$volume" == "/Volumes/Macintosh HD" ]] && continue
|
||||
|
||||
# Use diskutil to intelligently detect network volumes (SMB/NFS/AFP)
|
||||
# Timeout protection: 1s per volume to avoid slow network responses
|
||||
local protocol=""
|
||||
protocol=$(run_with_timeout 1 command diskutil info "$volume" 2>/dev/null | grep -i "Protocol:" | awk '{print $2}' || echo "")
|
||||
|
||||
case "$protocol" in
|
||||
SMB | NFS | AFP | CIFS | WebDAV)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
# Fallback: Check filesystem type via df if diskutil didn't identify protocol
|
||||
local fs_type=""
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2>/dev/null | tail -1 | awk '{print $2}' || echo "")
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
candidate_volumes+=("$volume")
|
||||
done
|
||||
|
||||
# If no external volumes found, return immediately (zero overhead)
|
||||
local volume_count=${#candidate_volumes[@]}
|
||||
[[ $volume_count -eq 0 ]] && return 0
|
||||
local network_count=${#network_volumes[@]}
|
||||
|
||||
# We have external volumes, now perform full scan
|
||||
if [[ $volume_count -eq 0 ]]; then
|
||||
# Show info if network volumes were skipped
|
||||
if [[ $network_count -gt 0 ]]; then
|
||||
echo -e " ${GRAY}→${NC} External volumes (${network_count} network volume(s) skipped)"
|
||||
note_activity
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# We have local external volumes, now perform full scan
|
||||
if [[ -t 1 ]]; then
|
||||
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning $volume_count external volume(s)..."
|
||||
fi
|
||||
|
||||
for volume in "${candidate_volumes[@]}"; do
|
||||
# Skip network volumes with short timeout (reduced from 2s to 1s)
|
||||
local fs_type=""
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}' || echo "unknown")
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav | unknown) continue ;;
|
||||
esac
|
||||
|
||||
# Verify volume is actually mounted (reduced timeout from 2s to 1s)
|
||||
run_with_timeout 1 mount | grep -q "on $volume " || continue
|
||||
|
||||
|
||||
@@ -128,10 +128,15 @@ source "$PROJECT_ROOT/lib/clean/brew.sh"
|
||||
mkdir -p "$HOME/.cache/mole"
|
||||
rm -f "$HOME/.cache/mole/brew_last_cleanup"
|
||||
|
||||
# Create a large enough Homebrew cache to pass pre-check (>50MB)
|
||||
mkdir -p "$HOME/Library/Caches/Homebrew"
|
||||
dd if=/dev/zero of="$HOME/Library/Caches/Homebrew/test.tar.gz" bs=1024 count=51200 2>/dev/null
|
||||
|
||||
MO_BREW_TIMEOUT=2
|
||||
|
||||
start_inline_spinner(){ :; }
|
||||
stop_inline_spinner(){ :; }
|
||||
note_activity(){ :; }
|
||||
|
||||
brew() {
|
||||
case "$1" in
|
||||
@@ -150,6 +155,9 @@ brew() {
|
||||
}
|
||||
|
||||
clean_homebrew
|
||||
|
||||
# Cleanup test files
|
||||
rm -rf "$HOME/Library/Caches/Homebrew"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
Reference in New Issue
Block a user