- Add macos-cleaner v1.0.0 - Intelligent macOS disk space recovery - Safety-first philosophy with risk categorization (Safe/Caution/Keep) - Smart analysis: caches, app remnants, large files, dev environments - Interactive cleanup with explicit user confirmation - Bundled scripts: analyze_caches, analyze_dev_env, analyze_large_files, find_app_remnants, safe_delete, cleanup_report - Comprehensive references: cleanup_targets, mole_integration, safety_rules - Update marketplace to v1.21.0 - Update all documentation (README.md, README.zh-CN.md, CHANGELOG.md, CLAUDE.md) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
235 lines
6.8 KiB
Python
Executable File
235 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Generate before/after cleanup reports.
|
|
|
|
Usage:
|
|
# Capture before snapshot
|
|
python3 cleanup_report.py --snapshot before
|
|
|
|
# Capture after snapshot and generate report
|
|
python3 cleanup_report.py --snapshot after --compare
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def format_size(bytes_size):
|
|
"""Convert bytes to human-readable format."""
|
|
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
|
if bytes_size < 1024.0:
|
|
return f"{bytes_size:.1f} {unit}"
|
|
bytes_size /= 1024.0
|
|
return f"{bytes_size:.1f} PB"
|
|
|
|
|
|
def get_disk_usage():
|
|
"""
|
|
Get current disk usage.
|
|
|
|
Returns:
|
|
dict with total, used, available, percent
|
|
"""
|
|
try:
|
|
result = subprocess.run(
|
|
['df', '-k', '/'],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
lines = result.stdout.strip().split('\n')
|
|
if len(lines) >= 2:
|
|
# Parse df output
|
|
parts = lines[1].split()
|
|
total_kb = int(parts[1])
|
|
used_kb = int(parts[2])
|
|
available_kb = int(parts[3])
|
|
percent = int(parts[4].rstrip('%'))
|
|
|
|
return {
|
|
'total': total_kb * 1024,
|
|
'used': used_kb * 1024,
|
|
'available': available_kb * 1024,
|
|
'percent': percent,
|
|
'timestamp': datetime.now().isoformat()
|
|
}
|
|
except:
|
|
pass
|
|
|
|
return None
|
|
|
|
|
|
def save_snapshot(name):
|
|
"""Save disk usage snapshot to file."""
|
|
snapshot_dir = Path.home() / '.macos-cleaner'
|
|
snapshot_dir.mkdir(exist_ok=True)
|
|
|
|
snapshot_file = snapshot_dir / f'{name}.json'
|
|
|
|
usage = get_disk_usage()
|
|
if usage:
|
|
with snapshot_file.open('w') as f:
|
|
json.dump(usage, f, indent=2)
|
|
print(f"✅ Snapshot saved: {snapshot_file}")
|
|
return True
|
|
else:
|
|
print("❌ Failed to get disk usage")
|
|
return False
|
|
|
|
|
|
def load_snapshot(name):
|
|
"""Load disk usage snapshot from file."""
|
|
snapshot_dir = Path.home() / '.macos-cleaner'
|
|
snapshot_file = snapshot_dir / f'{name}.json'
|
|
|
|
if not snapshot_file.exists():
|
|
print(f"❌ Snapshot not found: {snapshot_file}")
|
|
return None
|
|
|
|
with snapshot_file.open('r') as f:
|
|
return json.load(f)
|
|
|
|
|
|
def generate_report(before, after):
|
|
"""Generate comparison report."""
|
|
print("\n" + "=" * 60)
|
|
print("📊 Cleanup Report")
|
|
print("=" * 60)
|
|
|
|
# Time
|
|
before_time = datetime.fromisoformat(before['timestamp'])
|
|
after_time = datetime.fromisoformat(after['timestamp'])
|
|
duration = after_time - before_time
|
|
|
|
print(f"\nCleanup Duration: {duration}")
|
|
print(f"Before: {before_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
print(f"After: {after_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
# Disk usage comparison
|
|
print("\n" + "-" * 60)
|
|
print("Disk Usage")
|
|
print("-" * 60)
|
|
|
|
before_used = before['used']
|
|
after_used = after['used']
|
|
recovered = before_used - after_used
|
|
|
|
print(f"Before: {format_size(before_used):>12} ({before['percent']}%)")
|
|
print(f"After: {format_size(after_used):>12} ({after['percent']}%)")
|
|
print("-" * 60)
|
|
|
|
if recovered > 0:
|
|
print(f"✅ Recovered: {format_size(recovered):>12}")
|
|
percent_recovered = (recovered / before_used) * 100
|
|
print(f" ({percent_recovered:.1f}% of used space)")
|
|
elif recovered < 0:
|
|
print(f"⚠️ Space increased: {format_size(abs(recovered)):>12}")
|
|
print(" (This may be due to system activity during cleanup)")
|
|
else:
|
|
print("No change in disk usage")
|
|
|
|
# Available space
|
|
print("\n" + "-" * 60)
|
|
print("Available Space")
|
|
print("-" * 60)
|
|
|
|
before_avail = before['available']
|
|
after_avail = after['available']
|
|
gained = after_avail - before_avail
|
|
|
|
print(f"Before: {format_size(before_avail):>12}")
|
|
print(f"After: {format_size(after_avail):>12}")
|
|
print("-" * 60)
|
|
|
|
if gained > 0:
|
|
print(f"✅ Gained: {format_size(gained):>12}")
|
|
elif gained < 0:
|
|
print(f"⚠️ Lost: {format_size(abs(gained)):>12}")
|
|
else:
|
|
print("No change")
|
|
|
|
# Recommendations
|
|
print("\n" + "=" * 60)
|
|
|
|
if after['percent'] > 90:
|
|
print("⚠️ Warning: Disk is still >90% full")
|
|
print("\n💡 Recommendations:")
|
|
print(" - Consider moving large files to external storage")
|
|
print(" - Review and delete old projects")
|
|
print(" - Check for large application data")
|
|
elif after['percent'] > 80:
|
|
print("⚠️ Disk usage is still high (>80%)")
|
|
print("\n💡 Recommendations:")
|
|
print(" - Run cleanup again in 1-2 weeks")
|
|
print(" - Monitor large file creation")
|
|
else:
|
|
print("✅ Disk usage is healthy!")
|
|
print("\n💡 Maintenance Tips:")
|
|
print(" - Run cleanup monthly")
|
|
print(" - Empty Trash regularly")
|
|
print(" - Clear browser caches weekly")
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate cleanup reports'
|
|
)
|
|
parser.add_argument(
|
|
'--snapshot',
|
|
choices=['before', 'after'],
|
|
required=True,
|
|
help='Snapshot type (before or after cleanup)'
|
|
)
|
|
parser.add_argument(
|
|
'--compare',
|
|
action='store_true',
|
|
help='Compare with before snapshot (use with --snapshot after)'
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.snapshot == 'before':
|
|
# Save before snapshot
|
|
print("📸 Capturing disk usage before cleanup...")
|
|
if save_snapshot('before'):
|
|
usage = get_disk_usage()
|
|
print(f"\nCurrent Usage: {format_size(usage['used'])} ({usage['percent']}%)")
|
|
print(f"Available: {format_size(usage['available'])}")
|
|
print("\n💡 Run cleanup operations, then:")
|
|
print(" python3 cleanup_report.py --snapshot after --compare")
|
|
return 0
|
|
|
|
elif args.snapshot == 'after':
|
|
# Save after snapshot
|
|
print("📸 Capturing disk usage after cleanup...")
|
|
if not save_snapshot('after'):
|
|
return 1
|
|
|
|
if args.compare:
|
|
# Load before snapshot and compare
|
|
before = load_snapshot('before')
|
|
after = load_snapshot('after')
|
|
|
|
if before and after:
|
|
generate_report(before, after)
|
|
else:
|
|
print("❌ Cannot compare: missing snapshots")
|
|
return 1
|
|
else:
|
|
usage = get_disk_usage()
|
|
print(f"\nCurrent Usage: {format_size(usage['used'])} ({usage['percent']}%)")
|
|
print(f"Available: {format_size(usage['available'])}")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|