Files
cleanup/cleanup.sh
2024-01-06 07:12:56 +01:00

688 lines
20 KiB
Bash
Executable File

#!/usr/bin/env bash
#When a query returns a non-zero status, the -e flag stops the script. It also detects errors in the currently executing script.
set -E
trap cleanup SIGINT SIGTERM ERR EXIT
cleanup() {
trap - SIGINT SIGTERM ERR EXIT
}
#italic="\033[3m"
#underline="\033[4m"
#ita_under="\033[3;4m"
#bgd="\033[1;4;31m"
#red="\033[1;31m"
#bold="\033[1m"
#box="\033[1;41m"
#reset="\033[0m"
opt_cache=true
opt_log=true
opt_adobe=true
opt_chrome=false #
opt_ios_app=true
opt_ios_device=true
opt_xcode=true
opt_xcrun=true
opt_brew=false
opt_gem=false # ok
opt_npm=false # ok
opt_go=false # ok
opt_teams=true
opt_dns=true
opt_memory=true
opt_periodic=true
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-u]
A Mac Cleaning up Utility by fwartner
https://github.com/mac-cleanup/mac-cleanup-sh
Available options:
-h, --help Print this help and exit
-d, --dry-run Print approx space to be cleaned
-u, --update Run brew update
EOF
exit
}
# shellcheck disable=SC2034 # Unused variables left for readability
setup_colors() {
if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' ITALIC='\033[3m' BOLD='\033[1m'
else
NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' ITALIC='' BOLD=''
fi
}
msg() {
# Si PAS -d | --dry-run
#if [ -z "$dry_run" ]; then
echo >&2 -e "${1-}"
#fi
}
die() {
local msg=$1
local code=${2-1} # default exit status 1
msg "$msg"
exit "$code"
}
parse_params() {
# default values of variables set from params
update=false
while :; do
case "${1-}" in
-h | --help) usage ;;
#-v | --verbose) set -x ;;
-d | --dry-run) dry_run=true ;;
--no-color) NO_COLOR=1 ;;
#-u | --update) update=true ;; # update flag
-n) true ;; # This is a legacy option, now default behaviour
-?*) die "Unknown option: $1" ;;
*) break ;;
esac
shift
done
return 0
}
parse_params "$@"
setup_colors
# [ -z EMPTYSTRING ]
# [ -n NONEMPTYSTRING ]
if [ -n "$dry_run" ]; then
msg "${RED}Running dry run mode !\n${NOFORMAT}"
else
echo
fi
_bytesToHuman() {
b=$1
echo $b
}
bytesToHuman() {
b=${1:-0}
d=''
s=1
S=(Bytes {K,M,G,T,E,P,Y,Z}iB)
while ((b > 1024)); do
d="$(printf ".%02d" $((b % 1024 * 100 / 1024)))"
b=$((b / 1024))
((s++))
done
human_size="$b$d ${S[$s]}"
echo $human_size
}
count_dry() {
#echo "count_dry"
#local dry_results
#local temp_dry_results
for path in "${path_list[@]}"; do
if [ -d "$path" ] || [ -f "$path" ]; then
#echo "$path"
temp_dry_results=$(sudo du -ck "$path" | tail -1 | awk '{ print $1 }')
#echo "$path" "$temp_dry_results"
dry_results="$((dry_results+temp_dry_results))"
fi
done
echo $dry_results
}
remove_paths() {
if [ -z "$dry_run" ]; then
echo -e -n "Clean up $(bytesToHuman $count_section) ? (y/n) "
read -r -s -n1 clean_dry_run
if [[ $clean_dry_run = "y" ]]; then
for path in "${path_list[@]}"; do
msg "Remove $path"
#rm -rfv "$path" &>/dev/null
done
totalFree="$((totalFree+count_section))"
fi
fi
unset path_list
}
collect_paths() {
path_list+=("$@")
}
# Ask for the administrator password upfront
sudo -v
HOST=$(whoami)
# Keep-alive sudo until `mac-cleanup.sh` has finished
while true; do
sudo -n true
sleep 60
kill -0 "$$" || exit
done 2>/dev/null &
# Enable extended regex
shopt -s extglob
# Space cleaned counter
totalFree=0
### Display disk free size ###
msg "${BOLD}Free space on HDD...${NOFORMAT}\n"
oldAvailable=$(df / | tail -1 | awk '{print $4}')
msg "${BLUE}$(bytesToHuman $oldAvailable) available on /${NOFORMAT}"
echo
### Empty Trashes ###
msg "${BOLD}Emptying the Trash 🗑 on all mounted volumes and the main HDD...${NOFORMAT}"
collect_paths /Volumes/*/.Trashes/*
collect_paths ~/.Trash/*
count_section=$(count_dry)
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
echo "count_section $count_section"
echo "totalFree $totalFree"
### Empty System Cache Files ###
if [[ $opt_cache = true ]]; then
msg "\n${BOLD}Clearing System Cache Files...${NOFORMAT}"
collect_paths /Library/Caches/*
collect_paths /System/Library/Caches/*
collect_paths ~/Library/Caches/*
collect_paths /private/var/folders/bh/*/*/*/*
count_section=$(count_dry)
echo -e "${RED}System cache: $count_section${NOFORMAT}"
#totalFree="$((totalFree+count_section))"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
echo "totalFree $totalFree"
fi
### Empty System Log Files ###
if [[ $opt_log = true ]]; then
msg "\n${BOLD}Clearing System Log Files...${NOFORMAT}"
collect_paths /private/var/log/asl/*.asl
collect_paths /Library/Logs/DiagnosticReports/*
collect_paths /Library/Logs/CreativeCloud/*
collect_paths /Library/Logs/Adobe/*
collect_paths /Library/Logs/adobegc.log
collect_paths ~/Library/Containers/com.apple.mail/Data/Library/Logs/Mail/*
collect_paths ~/Library/Logs/CoreSimulator/*
count_section=$(count_dry)
echo -e "${RED}System log: $count_section${NOFORMAT}"
#totalFree="$((totalFree+count_section))"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
echo "totalFree $totalFree"
fi
### Adobe Cache ###
if [[ $opt_adobe = true ]]; then
if [ -d ~/Library/Application\ Support/Adobe/ ]; then
msg "\n${BOLD}Clearing Adobe Cache Files...${NOFORMAT}"
collect_paths ~/Library/Application\ Support/Adobe/Common/Media\ Cache\ Files/*
count_section=$(count_dry)
echo -e "${RED}Adobe: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
fi
echo "totalFree $totalFree"
fi
### Google Chrome Cache ###
if [[ $opt_chrome = true ]]; then
if [ -d ~/Library/Application\ Support/Google/Chrome/ ]; then
msg "\n${BOLD}Clearing Google Chrome Cache Files...${NOFORMAT}"
collect_paths ~/Library/Application\ Support/Google/Chrome/Default/Application\ Cache/*
count_section=$(count_dry)
echo -e "${RED}Chrome: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
fi
fi
### iOS Applications ###
if [[ $opt_ios_app = true ]]; then
if [ -d ~/Music/iTunes/ ]; then
msg "\n${BOLD}Cleaning up iOS Applications...${NOFORMAT}"
collect_paths ~/Music/iTunes/iTunes\ Media/Mobile\ Applications/*
count_section=$(count_dry)
echo -e "${RED}iOS app: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
fi
echo "totalFree $totalFree"
fi
### iOS Device Backups ###
if [[ $opt_ios_device = true ]]; then
msg "\n${BOLD}Removing iOS Device Backups...${NOFORMAT}"
collect_paths ~/Library/Application\ Support/MobileSync/Backup/*
count_section=$(count_dry)
echo -e "${RED}iOS device: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
echo "totalFree $totalFree"
fi
### Xcode ###
if [[ $opt_xcode = true ]]; then
if [ -d ~/Library/Developer/Xcode/ ]; then
msg "\n${BOLD}Cleaning up XCode Derived Data and Archives...${NOFORMAT}"
collect_paths ~/Library/Developer/Xcode/DerivedData/*
collect_paths ~/Library/Developer/Xcode/Archives/*
collect_paths ~/Library/Developer/Xcode/iOS Device Logs/*
count_section=$(count_dry)
echo -e "${RED}xcode: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}\n"
unset path_list
fi
fi
echo "totalFree $totalFree"
fi
# Xcrun
if [[ $opt_xcrun = true ]]; then
if type "xcrun" &>/dev/null; then
msg "\n${BOLD}Cleaning up iOS Simulators...${NOFORMAT}\n"
if [ -z "$dry_run" ]; then
# List available devices, device types, runtimes, or device pairs.
# xcrun simctl list
# Shutdown a device. (<device> | all)
# Specifying all will shut down all running devices
# xcrun simctl shutdown all &>/dev/null
# Erase a device's contents and settings. ([... <device n>] | all)
# Specifying all will erase all existing devices.
# xcrun simctl erase all &>/dev/null
echo -e "Running ${ITALIC}xcrun simctl delete unavailable${NOFORMAT}"
# Delete specified devices, unavailable devices, or all devices. ([... <device n>] | unavailable | all)
# Specifying unavailable will delete devices that are not supported by the current Xcode SDK.
# xcrun simctl delete unavailable
else
collect_paths ~/Library/Developer/CoreSimulator/Devices/*/data/!(Library|var|tmp|Media)
collect_paths /Users/wah/Library/Developer/CoreSimulator/Devices/*/data/Library/!(PreferencesCaches|Caches|AddressBook)
collect_paths ~/Library/Developer/CoreSimulator/Devices/*/data/Library/Caches/*
collect_paths ~/Library/Developer/CoreSimulator/Devices/*/data/Library/AddressBook/AddressBook*
echo ${path_list[@]}
count_section=$(count_dry)
echo -e "${RED}xcrcun: $count_section${NOFORMAT}"
unset path_list
fi
fi
fi
### Brew ###
if [[ $opt_brew = true ]]; then
if type "brew" &>/dev/null; then
msg "\n${BOLD}Cleaning up brew cache...${NOFORMAT}\n"
collect_paths "$(brew --cache)"
if [ -z "$dry_run" ]; then
# Remove stale lock files and outdated downloads for all formulae and casks, and remove old versions of installed formulae.
# If arguments are specified, only do this for the given formulae and casks. Removes all downloads more than 120 days old.
# This can be adjusted with HOMEBREW_CLEANUP_MAX_AGE_DAYS.
# -s: Scrub the cache, including downloads for even the latest versions. Note that downloads for any installed formulae or
# casks will still not be deleted. If you want to delete those too: rm -rf "$(brew --cache)"
echo -e "Running ${ITALIC}brew cleanup -s${NOFORMAT}"
# brew cleanup -s &>/dev/null
free=$(brew cleanup -s --dry-run | grep -o -E " [0-9]{1,3}(B|KB|MB|GB) ")
msg "${BLUE}\nApprox $free of space has been cleaned up${NOFORMAT}\n"
echo -e "Running ${ITALIC}brew autoremove${NOFORMAT}"
# Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed.
brew autoremove --dry-run
# Migrate tapped formulae from symlink-based to directory-based structure.
# brew tap --repair &>/dev/null
else
echo -e "Running ${ITALIC}brew cleanup -s --dry-run${NOFORMAT}"
free=$(brew cleanup -s --dry-run | grep -o -E " [0-9]{1,3}(B|KB|MB|GB) ")
msg "${BLUE}\nApprox $free of space will be cleaned up${NOFORMAT}\n"
echo -e "Running ${ITALIC}brew autoremove --dry-run${NOFORMAT}"
brew autoremove --dry-run
fi
unset path_list
fi
fi
### gem ###
# The cleanup command removes old versions of gems from GEM_HOME that are not required to meet a dependency.
# If a gem is installed elsewhere in GEM_PATH the cleanup command won't delete it.
# If no gems are named all gems in GEM_HOME are cleaned.
if [[ $opt_gem = true ]]; then
if type "gem" &>/dev/null; then # TODO add count_dry
msg "\n${BOLD}Cleaning up any old versions of gems...${NOFORMAT}"
if [ -z "$dry_run" ]; then
echo -e "Running ${ITALIC}gem cleanup${NOFORMAT}"
#gem cleanup &>/dev/null
else
echo -e "Running ${ITALIC}gem cleanup -V --dry-run${NOFORMAT}"
gem cleanup -V --dry-run
fi
fi
fi
### npm ###
# prune: This command removes "extraneous" packages. If a package name is provided, then only packages matching one of the supplied names are removed.
# Extraneous packages are those present in the node_modules folder that are not listed as any package's dependency list.
# clean: Delete all data out of the cache folder. Note that this is typically unnecessary, as npm's cache is self-healing and resistant to data corruption issues.
if [[ $opt_npm = true ]]; then
if type "npm" &>/dev/null; then
msg "\n${BOLD}Cleaning up npm cache...${NOFORMAT}"
collect_paths ~/.npm/_cacache/*
count_section=$(count_dry)
echo -e "${RED}npm: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
echo -e "Running ${ITALIC}npm cache clean --force${NOFORMAT}"
#npm cache clean --force &>/dev/null
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space has been cleaned up${NOFORMAT}."
echo -e "Running ${ITALIC}npm prune${NOFORMAT}"
npm_prune=$(npm prune)
[[ ! "$npm_prune" =~ "up to date in" ]] && echo "$npm_prune"
else
# npm cache clean --dry-run doesn't exist
msg "${BLUE}Approx $(bytesToHuman $count_section) of space will be cleaned up by running ${ITALIC}npm cache clean --force${NOFORMAT}"
echo -e "Running ${ITALIC}npm prune --dry-run${NOFORMAT}"
npm_prune=$(npm prune --dry-run)
[[ ! "$npm_prune" =~ "up to date in" ]] && echo "$npm_prune"
fi
unset path_list
totalFree="$((totalFree+count_section))"
echo -e "${RED}TOTAL: $totalFree${NOFORMAT}"
fi
fi
# Docker
# Pyenv
# yarn
# pnpm
# pod
### Cargo ###
# https://blog.rust-lang.org/2023/12/11/cargo-cache-cleaning.html
# https://github.com/matthiaskrgr/cargo-cache
# cargo cache --autoclean
# cargo cache --dry-run
# Size changed 842.71 MB => 660.87 MB (-181.84 MB, -21.57%)
# Remove artifacts from the target directory that Cargo has generated in the past.
# With no options, cargo clean will delete the entire target directory.
# When no packages are selected, all packages and all dependencies in the workspace are cleaned.
# cargo clean --dry-run
# cargo clean gc
# Remove one or more dependencies from a Cargo.toml manifest.
# cargo remove --dry-run
### go ###
# ~/Library/Caches/go-build
# 1 This directory holds cached build artifacts from the Go build system.
# 2 Run "go clean -cache" if the directory is getting too large. The -cache flag causes clean to remove the entire go build cache.
# 3 Run "go clean -fuzzcache" to delete the fuzz cache. The -fuzzcache flag causes clean to remove files stored in the Go build
# cache for fuzz testing. The fuzzing engine caches files that expand code coverage, so removing them may make fuzzing less effective until
# new inputs are found that provide the same coverage. These files are distinct from those stored in testdata directory; clean does not remove
# those files.
# 4 The -modcache flag causes clean to remove the entire module download cache, including unpacked source code of versioned dependencies.
# GOCACHE='/Users/bruno/Library/Caches/go-build'
# GOMODCACHE='/Users/bruno/go/pkg/mod'
# /Users/bruno/go/pkg/mod/cache
if [[ $opt_go = true ]]; then
if type "go" &>/dev/null; then
msg "\n${BOLD}Clearing Go module cache...${NOFORMAT}"
go_env=$(go env)
go_cache=$(echo "$go_env" | grep GOCACHE | awk -F"=" '{print $2}' | tr -d "'")
collect_paths "$go_cache"
if [ -n "$GOPATH" ]; then
collect_paths "$GOPATH/pkg/mod"
else
go_modcache=$(echo "$go_env" | grep GOMODCACHE | awk -F"=" '{print $2}' | tr -d "'")
collect_paths "$go_modcache"
fi
count_section=$(count_dry)
echo -e "${RED}go: $count_section{NOFORMAT}"
if [ -z "$dry_run" ]; then
echo -e "Remove the entire module download cache. Running ${ITALIC}go clean -modcache${NOFORMAT}"
go clean -modcache &>/dev/null
echo -e "Remove the entire go build cache. Running ${ITALIC}go clean -cache${NOFORMAT}"
go clean -cache &>/dev/null
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space has been cleaned up${NOFORMAT}"
else
msg "\n${BLUE}Approx $(bytesToHuman $count_section) of space will be cleaned up by running ${ITALIC}go clean -modcache${NOFORMAT}${BLUE} and ${ITALIC}go clean -cache${NOFORMAT}"
fi
unset path_list
totalFree="$((totalFree+count_section))"
echo -e "${RED}TOTAL: $totalFree{NOFORMAT}"
fi
fi
### Deletes all Microsoft Teams Caches and resets it to default - can fix also some performance issues ###
# -Astro
if [[ $opt_teams = true ]]; then
if [ -d ~/Library/Application\ Support/Microsoft/Teams ]; then
msg "\n${BOLD}Deleting Microsoft Teams logs and caches...${NOFORMAT}"
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/IndexedDB
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/Cache
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/Application\ Cache
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/Code\ Cache
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/blob_storage
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/databases
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/gpucache
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/Local\ Storage
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/tmp
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/*logs*.txt
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/watchdog
#collect_paths ~/Library/Application\ Support/Microsoft/Teams/*watchdog*.json
collect_paths ~/Library/Group\ Containers/UBF8T346G9.com.microsoft.teams/* # 776
collect_paths ~/Library/Containers/com.microsoft.teams2/* # 26872
# ~/Library/Containers/com.microsoft.teams2.launcher/
# ~/Library/Containers/com.microsoft.teams2.notificationcenter/
collect_paths ~/Library/Application\ Support/Microsoft/Teams/* #977048
[ -d ~/Library/Logs/Microsoft\ Teams/ ] && collect_paths ~/Library/Logs/Microsoft\ Teams/* # /
[ -d ~/Library/Logs/Microsoft\ Teams\ Helper/ ] && collect_paths ~/Library/Logs/Microsoft\ Teams\ Helper/* # /
[ -d ~/Library/Logs/Microsoft\ Teams\ Helper\ \(Renderer\)/ ] && collect_paths ~/Library/Logs/Microsoft\ Teams\ Helper\ \(Renderer\)/*
count_section=$(count_dry)
echo -e "${RED}teams: $count_section${NOFORMAT}"
if [ -z "$dry_run" ]; then
# QUIT Teams
/usr/bin/pkill -9 'Teams' &>/dev/null
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
remove_paths
else
msg "${BLUE}\nApprox $(bytesToHuman $count_section) of space will be cleaned up${NOFORMAT}"
unset path_list
fi
totalFree="$((totalFree+count_section))"
echo -e "${RED}TOTAL: $totalFree${NOFORMAT}"
fi
fi
if [[ $opt_dns = true ]]; then
if [ -z "$dry_run" ]; then
msg "\n${BOLD}Cleaning up DNS cache...${NOFORMAT}"
echo -e "Running ${ITALIC}sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder${NOFORMAT}"
#sudo dscacheutil -flushcache &>/dev/null
#sudo killall -HUP mDNSResponder &>/dev/null
fi
fi
if [[ $opt_memory = true ]]; then
if [ -z "$dry_run" ]; then
msg "\n${BOLD}Purging inactive memory...${NOFORMAT}"
echo -e "Running ${ITALIC}sudo purge${NOFORMAT}"
# force disk cache to be purged (flushed and emptied)
#sudo purge &>/dev/null
fi
fi
if [[ $opt_periodic = true ]]; then
if [ -z "$dry_run" ]; then
msg "\n${BOLD}Running the maintenance scripts...${NOFORMAT}"
echo -e "Running ${ITALIC}sudo periodic daily weekly monthly${NOFORMAT}"
#sudo periodic daily weekly monthly
fi
fi
# End
msg "${GREEN}\nSuccess!${NOFORMAT}"
msg "${BLUE}\nApprox $(bytesToHuman $totalFree) of space has been cleaned up${NOFORMAT}"
newAvailable=$(df / | tail -1 | awk '{print $4}')
msg "${BLUE}$(bytesToHuman $newAvailable) available on /${NOFORMAT}"