mirror of
https://github.com/ivuorinen/dotfiles.git
synced 2026-02-06 14:50:05 +00:00
362 lines
9.7 KiB
Bash
Executable File
362 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# This script checks if all versions of tools installed via asdf are in use
|
|
# in any .tool-versions file in the home directory or its subdirectories,
|
|
# excluding some directories that shouldn't be scanned.
|
|
#
|
|
# It will print out the tools and versions that are not in use and remove them.
|
|
#
|
|
# This script is useful for cleaning up old versions of tools that are no longer
|
|
# in use and are taking up extra space on your system.
|
|
#
|
|
# Usage: x-asdf-cleanup
|
|
# Author: Ismo Vuorinen <https://github.com/ivuorinen>
|
|
# License: MIT
|
|
#
|
|
# vim: set ft=sh ts=2 sw=2 et: ft=sh
|
|
|
|
# set -euo pipefail
|
|
|
|
VERSION="1.0.0"
|
|
BASENAME=$(basename "$0")
|
|
|
|
# Check if asdf is installed
|
|
if ! command -v asdf &> /dev/null; then
|
|
echo "(!) asdf itself is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v fd &> /dev/null; then
|
|
echo "(!) Required tool fd is not installed or not in PATH"
|
|
echo "It's used to find .tool-versions files faster"
|
|
echo "Install it with asdf:"
|
|
echo "asdf plugin add fd && asdf install fd latest"
|
|
exit 1
|
|
fi
|
|
|
|
# Enable debugging: DEBUG=1 x-asdf-cleanup
|
|
# or run with --debug option: x-asdf-cleanup --debug
|
|
if [ "_$DEBUG" != "_" ]; then
|
|
set -x
|
|
fi
|
|
|
|
# Define the base directory to search for .tool-versions files
|
|
BASE_DIR="$HOME"
|
|
|
|
# Define the exclude patterns
|
|
EXCLUDE_PATTERNS=("Library" "Photos" ".cache" ".local/state" ".git" ".Trash")
|
|
|
|
# Dry run flag
|
|
# If set to true, the script will only print out the versions that would be uninstalled
|
|
DRYRUN=false
|
|
|
|
usage() {
|
|
echo "Usage: $BASENAME [OPTIONS]"
|
|
echo
|
|
echo "Options:"
|
|
echo " --base-dir=DIR Specify the base directory to search for .tool-versions files"
|
|
echo " --dry-run Perform a dry run without uninstalling any versions"
|
|
echo " --exclude=DIR Exclude a directory from the search path, can be used multiple times"
|
|
echo " --debug Show debug information and exit"
|
|
echo " --verbose Enable verbose output"
|
|
echo " -h, --help Show this help message and exit"
|
|
echo " -v, --version Show the version of the script and exit"
|
|
exit 1
|
|
}
|
|
|
|
# Function to parse arguments
|
|
parse_arguments() {
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
-h|--help)
|
|
usage
|
|
;;
|
|
-v|--version)
|
|
version
|
|
;;
|
|
--base-dir=*)
|
|
BASE_DIR="${arg#*=}"
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRYRUN=true
|
|
shift
|
|
;;
|
|
--exclude=*)
|
|
EXCLUDE_PATTERNS+=("${arg#*=}")
|
|
shift
|
|
;;
|
|
--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
--debug)
|
|
DEBUG=true
|
|
shift
|
|
debug_info "$@"
|
|
;;
|
|
*)
|
|
echo "Unknown option: $arg"
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Function to check if the parameter given exists and is a directory
|
|
# Usage: is_dir "directory"
|
|
# Output: "yes" or "no"
|
|
is_dir() {
|
|
local dir="$1"
|
|
if [ -d "$dir" ]; then
|
|
echo "yes"
|
|
else
|
|
echo "no"
|
|
fi
|
|
}
|
|
|
|
# Function to display debug information
|
|
#
|
|
# It will print out the script variables, tool versions
|
|
# and the remaining arguments.
|
|
#
|
|
# Usage: debug_info "$@"
|
|
# Where $@ is the list of arguments passed to the script
|
|
# Output: Debug information
|
|
debug_info() {
|
|
echo "----------------------------------------"
|
|
echo "Script variables:"
|
|
echo "----------------------------------------"
|
|
echo " BASE_DIR = $BASE_DIR"
|
|
echo " BASE_DIR IS DIR = $(is_dir "$BASE_DIR")"
|
|
echo " DEBUG = $DEBUG"
|
|
echo " DRY_RUN = $DRYRUN"
|
|
echo " EXCLUDE_PATTERNS = ${EXCLUDE_PATTERNS[*]}"
|
|
echo " VERBOSE = $VERBOSE"
|
|
echo " VERSION = $VERSION"
|
|
echo " Remaining arguments:"
|
|
for var in "$@"; do
|
|
echo " $var"
|
|
done
|
|
echo "----------------------------------------"
|
|
echo "Tool versions:"
|
|
echo "----------------------------------------"
|
|
echo "asdf version: $(asdf --version)"
|
|
echo "fd version: $(fd --version)"
|
|
echo "----------------------------------------"
|
|
exit 0
|
|
}
|
|
|
|
version() {
|
|
echo "$BASENAME $VERSION"
|
|
echo "Author: Ismo Vuorinen <https://github.com/ivuorinen>"
|
|
exit 0
|
|
}
|
|
|
|
# Trim whitespace from a string
|
|
# Usage: trim_whitespace " hello world "
|
|
# Output: "hello world"
|
|
trim_whitespace() {
|
|
local var="$1"
|
|
var="${var#"${var%%[![:space:]]*}"}" # Remove leading whitespace
|
|
var="${var%"${var##*[![:space:]]}"}" # Remove trailing whitespace
|
|
echo "$var"
|
|
}
|
|
|
|
# Function to process each .tool-versions file
|
|
# Usage: process_file "file"
|
|
# Output: "tool version"
|
|
process_file() {
|
|
local file="$1"
|
|
awk '{for (i=2; i<=NF; i++) print $1, $i}' "$file"
|
|
}
|
|
|
|
# Function to find .tool-versions files using fd
|
|
# It will exclude directories defined in EXCLUDE_PATTERNS
|
|
# Usage: find_tool_versions_files
|
|
# Output: List of .tool-versions files found
|
|
find_tool_versions_files() {
|
|
local fd_command="fd --base-directory $BASE_DIR --glob '.tool-versions' --hidden"
|
|
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
|
|
fd_command="$fd_command --exclude $pattern"
|
|
done
|
|
eval "$fd_command"
|
|
}
|
|
|
|
# Function to read and combine the contents of all found files.
|
|
# It will store the tool names and versions in an associative array.
|
|
# The key is "name version" and the value is 1.
|
|
# Uses process_file to process each file.
|
|
#
|
|
# Usage: read_defined_versions
|
|
# Output: defined_versions associative array
|
|
read_defined_versions() {
|
|
for file in $files; do
|
|
while read -r name version; do
|
|
defined_versions["$name $version"]=1
|
|
done < <(process_file "$BASE_DIR/$file")
|
|
done
|
|
}
|
|
|
|
# Function to get the list of installed versions from asdf list command
|
|
# Usage: read_installed_versions
|
|
# Output: keep_version associative array
|
|
# (tools set as global in asdf are kept)
|
|
# (system versions are ignored)
|
|
# (tools with no versions installed are removed)
|
|
read_installed_versions() {
|
|
local current_tool=""
|
|
local line
|
|
# Use IFS to read lines with spaces, this is needed for asdf list command
|
|
while IFS= read -r line; do
|
|
# Read the tool name
|
|
if [[ "$line" =~ ^[^[:space:]] ]]; then
|
|
current_tool=$(trim_whitespace "$line")
|
|
continue
|
|
fi
|
|
|
|
# We are now processing the versions for a tool
|
|
line=$(trim_whitespace "$line")
|
|
if [[ "$line" =~ ^\* ]]; then
|
|
local version="${line:1}" # Remove the leading '*'
|
|
version=$(trim_whitespace "$version") # Trim any remaining whitespace
|
|
keep_version["$current_tool $version"]=1
|
|
[ "$VERBOSE" = true ] && echo "(*) Keep: $current_tool $version ($line)"
|
|
fi
|
|
if [[ "$line" == "No versions installed" ]]; then
|
|
# Remove all versions for the tool that has no versions installed
|
|
# This way we are not confusing the uninstall script later
|
|
[ "$VERBOSE" = true ] && echo "(?) No versions installed for $current_tool"
|
|
for key in "${!defined_versions[@]}"; do
|
|
if [[ "$key" =~ ^$current_tool ]]; then
|
|
unset defined_versions["$key"]
|
|
fi
|
|
done
|
|
continue
|
|
fi
|
|
|
|
# If version starts with * remove it
|
|
if [[ "$line" =~ ^\* ]]; then
|
|
line="${line:1}"
|
|
fi
|
|
installed_versions["$current_tool $line"]=1
|
|
|
|
done < <(asdf list)
|
|
}
|
|
|
|
# Function to display all defined versions
|
|
# List the tools and versions found in .tool-versions files
|
|
# Output: defined_versions
|
|
display_defined_versions() {
|
|
echo "All Defined Versions:"
|
|
for key in "${!defined_versions[@]}"; do
|
|
echo "$key"
|
|
done
|
|
echo
|
|
}
|
|
|
|
display_installed_versions() {
|
|
echo "All Installed Versions:"
|
|
for key in "${!installed_versions[@]}"; do
|
|
echo "$key"
|
|
done
|
|
echo
|
|
}
|
|
|
|
# Function to display versions to keep
|
|
# List the tools and versions set as global in asdf
|
|
# Output: keep_version
|
|
display_versions_to_keep() {
|
|
echo ""
|
|
echo "Versions to Keep (tools set as global):"
|
|
for key in "${!keep_version[@]}"; do
|
|
echo "$key"
|
|
done
|
|
echo
|
|
}
|
|
|
|
# Function to determine versions to uninstall
|
|
# Compare defined_versions and keep_version arrays
|
|
# Output: uninstall_list array
|
|
# (versions to uninstall)
|
|
# (versions set as global are not uninstalled)
|
|
# (versions not found in .tool-versions files are uninstalled)
|
|
determine_versions_to_uninstall() {
|
|
echo ""
|
|
echo "Versions to Uninstall:"
|
|
for key in "${!installed_versions[@]}"; do
|
|
if [[ -z ${keep_version[$key]} ]]; then
|
|
echo "$key"
|
|
uninstall_list+=("$key")
|
|
fi
|
|
done
|
|
echo
|
|
}
|
|
|
|
# Function to uninstall versions
|
|
# It will prompt the user for confirmation before uninstalling
|
|
# If DRYRUN is set to true, it will only print out the versions that would be uninstalled
|
|
#
|
|
# Usage: uninstall_versions
|
|
# Output: Uninstall the versions in uninstall_list array
|
|
uninstall_versions() {
|
|
if [[ ${#uninstall_list[@]} -gt 0 ]]; then
|
|
if [ "$DRYRUN" = true ]; then
|
|
confirm="y"
|
|
fi
|
|
if [ ! "$DRYRUN" = true ]; then
|
|
read -p "Do you want to proceed with uninstallation? (y/N): " confirm
|
|
fi
|
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
|
for key in "${uninstall_list[@]}"; do
|
|
local name="${key% *}"
|
|
local version="${key#* }"
|
|
if [ "$DRYRUN" = true ]; then
|
|
echo "(?) Dry run: would uninstall $name $version"
|
|
else
|
|
echo "(*) Uninstalling $name: $version"
|
|
asdf uninstall $name $version
|
|
fi
|
|
done
|
|
else
|
|
echo "Uninstallation aborted by user."
|
|
fi
|
|
else
|
|
echo "No versions to uninstall."
|
|
fi
|
|
}
|
|
|
|
# Main script execution
|
|
main() {
|
|
parse_arguments "$@"
|
|
|
|
BASEDIR_IS_DIR=$(is_dir "$BASE_DIR")
|
|
if [ "$BASEDIR_IS_DIR" = "no" ]; then
|
|
echo "(!) Base directory $BASE_DIR does not exist or is not a directory"
|
|
exit 1
|
|
fi
|
|
|
|
files=$(find_tool_versions_files)
|
|
echo "Found .tool-versions files:"
|
|
echo "$files"
|
|
echo
|
|
|
|
# Declare associative arrays
|
|
declare -A defined_versions
|
|
declare -A installed_versions
|
|
declare -A keep_version
|
|
|
|
read_defined_versions
|
|
display_defined_versions
|
|
|
|
read_installed_versions
|
|
display_installed_versions
|
|
display_versions_to_keep
|
|
|
|
determine_versions_to_uninstall
|
|
uninstall_versions
|
|
}
|
|
|
|
# Execute the main function
|
|
main "$@"
|
|
|