mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 17:24:45 +00:00
feat(clean): add large file review and unify warnings
This commit is contained in:
@@ -699,7 +699,7 @@ start_cleanup() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" && -t 0 ]]; then
|
if [[ "$DRY_RUN" != "true" && -t 0 ]]; then
|
||||||
echo -e "${GRAY}${ICON_SOLID} Use --dry-run to preview, --whitelist to manage protected paths${NC}"
|
echo -e "${GRAY}${ICON_WARNING} Use --dry-run to preview, --whitelist to manage protected paths${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
@@ -979,6 +979,11 @@ perform_cleanup() {
|
|||||||
clean_time_machine_failed_backups
|
clean_time_machine_failed_backups
|
||||||
end_section
|
end_section
|
||||||
|
|
||||||
|
# ===== 16. Large files to review (report only) =====
|
||||||
|
start_section "Large files to review"
|
||||||
|
check_large_file_candidates
|
||||||
|
end_section
|
||||||
|
|
||||||
# ===== Final summary =====
|
# ===== Final summary =====
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ tm_is_running() {
|
|||||||
grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=[[:space:]]*1([[:space:]]*;|$)' <<< "$st"
|
grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=[[:space:]]*1([[:space:]]*;|$)' <<< "$st"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Local APFS snapshots (keep the most recent).
|
# Local APFS snapshots (report only).
|
||||||
clean_local_snapshots() {
|
clean_local_snapshots() {
|
||||||
if ! command -v tmutil > /dev/null 2>&1; then
|
if ! command -v tmutil > /dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
@@ -279,93 +279,25 @@ clean_local_snapshots() {
|
|||||||
tm_is_running || rc_running=$?
|
tm_is_running || rc_running=$?
|
||||||
|
|
||||||
if [[ $rc_running -eq 2 ]]; then
|
if [[ $rc_running -eq 2 ]]; then
|
||||||
echo -e " ${YELLOW}!${NC} Could not determine Time Machine status; skipping snapshot cleanup"
|
echo -e " ${YELLOW}!${NC} Could not determine Time Machine status; skipping snapshot check"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $rc_running -eq 0 ]]; then
|
if [[ $rc_running -eq 0 ]]; then
|
||||||
echo -e " ${YELLOW}!${NC} Time Machine is active; skipping snapshot cleanup"
|
echo -e " ${YELLOW}!${NC} Time Machine is active; skipping snapshot check"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
start_section_spinner "Checking local snapshots..."
|
start_section_spinner "Checking local snapshots..."
|
||||||
local snapshot_list
|
local snapshot_list
|
||||||
snapshot_list=$(tmutil listlocalsnapshots / 2> /dev/null)
|
snapshot_list=$(run_with_timeout 3 tmutil listlocalsnapshots / 2> /dev/null || true)
|
||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
[[ -z "$snapshot_list" ]] && return 0
|
[[ -z "$snapshot_list" ]] && return 0
|
||||||
local cleaned_count=0
|
|
||||||
local total_cleaned_size=0 # Estimation not possible without thin
|
|
||||||
local newest_ts=0
|
|
||||||
local newest_name=""
|
|
||||||
local -a snapshots=()
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ "$line" =~ com\.apple\.TimeMachine\.([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{6}) ]]; then
|
|
||||||
local snap_name="${BASH_REMATCH[0]}"
|
|
||||||
snapshots+=("$snap_name")
|
|
||||||
local date_str="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]} ${BASH_REMATCH[4]:0:2}:${BASH_REMATCH[4]:2:2}:${BASH_REMATCH[4]:4:2}"
|
|
||||||
local snap_ts=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_str" "+%s" 2> /dev/null || echo "0")
|
|
||||||
[[ "$snap_ts" == "0" ]] && continue
|
|
||||||
if [[ "$snap_ts" -gt "$newest_ts" ]]; then
|
|
||||||
newest_ts="$snap_ts"
|
|
||||||
newest_name="$snap_name"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done <<< "$snapshot_list"
|
|
||||||
|
|
||||||
[[ ${#snapshots[@]} -eq 0 ]] && return 0
|
local snapshot_count
|
||||||
[[ -z "$newest_name" ]] && return 0
|
snapshot_count=$(echo "$snapshot_list" | grep -Eo 'com\.apple\.TimeMachine\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}' | wc -l | awk '{print $1}')
|
||||||
|
if [[ "$snapshot_count" =~ ^[0-9]+$ && "$snapshot_count" -gt 0 ]]; then
|
||||||
local deletable_count=$((${#snapshots[@]} - 1))
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Time Machine local snapshots: ${GREEN}${snapshot_count}${NC}${GRAY}, Review: tmutil listlocalsnapshots /${NC}"
|
||||||
[[ $deletable_count -le 0 ]] && return 0
|
note_activity
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != "true" ]]; then
|
|
||||||
if [[ ! -t 0 ]]; then
|
|
||||||
echo -e " ${YELLOW}!${NC} ${#snapshots[@]} local snapshot(s) found, skipping non-interactive mode"
|
|
||||||
echo -e " ${GRAY}${ICON_WARNING}${NC} ${GRAY}Tip: Snapshots may cause Disk Utility to show different 'Available' values${NC}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
echo -e " ${YELLOW}!${NC} Time Machine local snapshots found"
|
|
||||||
echo -e " ${GRAY}macOS can recreate them if needed.${NC}"
|
|
||||||
echo -e " ${GRAY}The most recent snapshot will be kept.${NC}"
|
|
||||||
echo -ne " ${PURPLE}${ICON_ARROW}${NC} Remove all local snapshots except the most recent one? ${GREEN}Enter${NC} continue, ${GRAY}Space${NC} skip: "
|
|
||||||
local choice
|
|
||||||
if type read_key > /dev/null 2>&1; then
|
|
||||||
choice=$(read_key)
|
|
||||||
else
|
|
||||||
IFS= read -r -s -n 1 choice || choice=""
|
|
||||||
if [[ -z "$choice" || "$choice" == $'\n' || "$choice" == $'\r' ]]; then
|
|
||||||
choice="ENTER"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [[ "$choice" == "ENTER" ]]; then
|
|
||||||
printf "\r\033[K" # Clear the prompt line
|
|
||||||
else
|
|
||||||
echo -e " ${GRAY}Skipped${NC}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local snap_name
|
|
||||||
for snap_name in "${snapshots[@]}"; do
|
|
||||||
if [[ "$snap_name" =~ com\.apple\.TimeMachine\.([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{6}) ]]; then
|
|
||||||
if [[ "${BASH_REMATCH[0]}" != "$newest_name" ]]; then
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
|
||||||
echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} Local snapshot: $snap_name ${YELLOW}dry-run${NC}"
|
|
||||||
((cleaned_count++))
|
|
||||||
note_activity
|
|
||||||
else
|
|
||||||
if sudo tmutil deletelocalsnapshots "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}" > /dev/null 2>&1; then
|
|
||||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed snapshot: $snap_name"
|
|
||||||
((cleaned_count++))
|
|
||||||
note_activity
|
|
||||||
else
|
|
||||||
echo -e " ${YELLOW}!${NC} Failed to remove: $snap_name"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [[ $cleaned_count -gt 0 && "$DRY_RUN" != "true" ]]; then
|
|
||||||
log_success "Cleaned $cleaned_count local snapshots, kept latest"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,13 +600,107 @@ check_ios_device_backups() {
|
|||||||
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||||
if [[ -n "$backup_human" ]]; then
|
if [[ -n "$backup_human" ]]; then
|
||||||
note_activity
|
note_activity
|
||||||
echo -e " Found ${GREEN}${backup_human}${NC} iOS backups"
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} iOS backups: ${GREEN}${backup_human}${NC}${GRAY}, Path: $backup_dir${NC}"
|
||||||
echo -e " You can delete them manually: ${backup_dir}"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Large file candidates (report only, no deletion).
|
||||||
|
check_large_file_candidates() {
|
||||||
|
local threshold_kb=$((1024 * 1024)) # 1GB
|
||||||
|
local found_any=false
|
||||||
|
|
||||||
|
local mail_dir="$HOME/Library/Mail"
|
||||||
|
if [[ -d "$mail_dir" ]]; then
|
||||||
|
local mail_kb
|
||||||
|
mail_kb=$(get_path_size_kb "$mail_dir")
|
||||||
|
if [[ "$mail_kb" -ge "$threshold_kb" ]]; then
|
||||||
|
local mail_human
|
||||||
|
mail_human=$(bytes_to_human "$((mail_kb * 1024))")
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Mail data: ${GREEN}${mail_human}${NC}${GRAY}, Path: $mail_dir${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local mail_downloads="$HOME/Library/Mail Downloads"
|
||||||
|
if [[ -d "$mail_downloads" ]]; then
|
||||||
|
local downloads_kb
|
||||||
|
downloads_kb=$(get_path_size_kb "$mail_downloads")
|
||||||
|
if [[ "$downloads_kb" -ge "$threshold_kb" ]]; then
|
||||||
|
local downloads_human
|
||||||
|
downloads_human=$(bytes_to_human "$((downloads_kb * 1024))")
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Mail downloads: ${GREEN}${downloads_human}${NC}${GRAY}, Path: $mail_downloads${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local installer_path
|
||||||
|
for installer_path in /Applications/Install\ macOS*.app; do
|
||||||
|
if [[ -e "$installer_path" ]]; then
|
||||||
|
local installer_kb
|
||||||
|
installer_kb=$(get_path_size_kb "$installer_path")
|
||||||
|
if [[ "$installer_kb" -gt 0 ]]; then
|
||||||
|
local installer_human
|
||||||
|
installer_human=$(bytes_to_human "$((installer_kb * 1024))")
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} macOS installer: ${GREEN}${installer_human}${NC}${GRAY}, Path: $installer_path${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local updates_dir="$HOME/Library/Updates"
|
||||||
|
if [[ -d "$updates_dir" ]]; then
|
||||||
|
local updates_kb
|
||||||
|
updates_kb=$(get_path_size_kb "$updates_dir")
|
||||||
|
if [[ "$updates_kb" -ge "$threshold_kb" ]]; then
|
||||||
|
local updates_human
|
||||||
|
updates_human=$(bytes_to_human "$((updates_kb * 1024))")
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} macOS updates cache: ${GREEN}${updates_human}${NC}${GRAY}, Path: $updates_dir${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${SYSTEM_CLEAN:-false}" != "true" ]] && command -v tmutil > /dev/null 2>&1; then
|
||||||
|
local snapshot_list snapshot_count
|
||||||
|
snapshot_list=$(run_with_timeout 3 tmutil listlocalsnapshots / 2> /dev/null || true)
|
||||||
|
if [[ -n "$snapshot_list" ]]; then
|
||||||
|
snapshot_count=$(echo "$snapshot_list" | grep -Eo 'com\.apple\.TimeMachine\.[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}' | wc -l | awk '{print $1}')
|
||||||
|
if [[ "$snapshot_count" =~ ^[0-9]+$ && "$snapshot_count" -gt 0 ]]; then
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Time Machine local snapshots: ${GREEN}${snapshot_count}${NC}${GRAY}, Review: tmutil listlocalsnapshots /${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v docker > /dev/null 2>&1; then
|
||||||
|
local docker_output
|
||||||
|
docker_output=$(run_with_timeout 3 docker system df --format '{{.Type}}\t{{.Size}}\t{{.Reclaimable}}' 2> /dev/null || true)
|
||||||
|
if [[ -n "$docker_output" ]]; then
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Docker storage:"
|
||||||
|
while IFS=$'\t' read -r dtype dsize dreclaim; do
|
||||||
|
[[ -z "$dtype" ]] && continue
|
||||||
|
echo -e " ${GRAY}• $dtype: $dsize, Reclaimable: $dreclaim${NC}"
|
||||||
|
done <<< "$docker_output"
|
||||||
|
found_any=true
|
||||||
|
else
|
||||||
|
docker_output=$(run_with_timeout 3 docker system df 2> /dev/null || true)
|
||||||
|
if [[ -n "$docker_output" ]]; then
|
||||||
|
echo -e " ${YELLOW}${ICON_WARNING}${NC} Docker storage:"
|
||||||
|
echo -e " ${GRAY}• Run: docker system df${NC}"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$found_any" == "false" ]]; then
|
||||||
|
echo -e " ${GREEN}${ICON_SUCCESS}${NC} No large items detected in common locations"
|
||||||
|
fi
|
||||||
|
|
||||||
|
note_activity
|
||||||
|
return 0
|
||||||
|
}
|
||||||
# Apple Silicon specific caches (IS_M_SERIES).
|
# Apple Silicon specific caches (IS_M_SERIES).
|
||||||
clean_apple_silicon_caches() {
|
clean_apple_silicon_caches() {
|
||||||
if [[ "${IS_M_SERIES:-false}" != "true" ]]; then
|
if [[ "${IS_M_SERIES:-false}" != "true" ]]; then
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ readonly ICON_CONFIRM="◎"
|
|||||||
readonly ICON_ADMIN="⚙"
|
readonly ICON_ADMIN="⚙"
|
||||||
readonly ICON_SUCCESS="✓"
|
readonly ICON_SUCCESS="✓"
|
||||||
readonly ICON_ERROR="☻"
|
readonly ICON_ERROR="☻"
|
||||||
readonly ICON_WARNING="●"
|
readonly ICON_WARNING="☉"
|
||||||
readonly ICON_EMPTY="○"
|
readonly ICON_EMPTY="○"
|
||||||
readonly ICON_SOLID="●"
|
readonly ICON_SOLID="●"
|
||||||
readonly ICON_LIST="•"
|
readonly ICON_LIST="•"
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ batch_uninstall_applications() {
|
|||||||
while IFS= read -r file; do
|
while IFS= read -r file; do
|
||||||
if [[ -n "$file" && -e "$file" ]]; then
|
if [[ -n "$file" && -e "$file" ]]; then
|
||||||
if [[ $sys_file_count -lt $max_files ]]; then
|
if [[ $sys_file_count -lt $max_files ]]; then
|
||||||
echo -e " ${BLUE}${ICON_SOLID}${NC} System: $file"
|
echo -e " ${BLUE}${ICON_WARNING}${NC} System: $file"
|
||||||
fi
|
fi
|
||||||
((sys_file_count++))
|
((sys_file_count++))
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user