Skip to content

Instantly share code, notes, and snippets.

@dantman
Created April 20, 2025 20:48
Show Gist options
  • Save dantman/64aae22aa2ef903bdf5552a33192aba4 to your computer and use it in GitHub Desktop.
Save dantman/64aae22aa2ef903bdf5552a33192aba4 to your computer and use it in GitHub Desktop.
A Bash script that automatically manages Node.js versions with NVM, keeping specified versions updated and removing unused ones.
#!/bin/bash
# Authored by: Kagi Assistant
# https://kagi.com/assistant/6995ca12-6df0-4f88-9d91-848e993a2870
# Source NVM to make it available to the script
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
# Check if NVM is available
if ! command -v nvm &> /dev/null; then
echo "Error: NVM is not installed or not properly sourced."
echo "Please make sure NVM is installed and try again."
exit 1
fi
# Configuration file to track installed versions and their patterns
CONFIG_FILE="$HOME/.config/nvm-version-tracker.conf"
mkdir -p "$(dirname "$CONFIG_FILE")"
touch "$CONFIG_FILE"
# Define version patterns to maintain (edit these as needed)
VERSION_PATTERNS=(
"14" # Latest patch for Node.js 14
"16" # Latest patch for Node.js 16
"18" # Latest patch for Node.js 18
"20" # Latest patch for Node.js 20
"22" # Latest patch for Node.js 22
"node" # Latest stable release
)
# Function to resolve a pattern to an actual version
resolve_version() {
local pattern="$1"
local version
# Handle exact version numbers (x.y.z)
if [[ $pattern =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
version="$pattern"
# Handle major-only version numbers (x)
elif [[ $pattern =~ ^[0-9]+$ ]]; then
version=$(nvm ls-remote "$pattern" --no-colors | grep -v "rc\|alpha\|beta" | tail -1 | grep -o "v[0-9]\+\.[0-9]\+\.[0-9]\+" | tr -d 'v')
# Handle special patterns
else
version=$(nvm ls-remote "$pattern" --no-colors | tail -1 | grep -o "v[0-9]\+\.[0-9]\+\.[0-9]\+" | tr -d 'v')
fi
echo "$version"
}
# Update the config file with the latest resolved version for each pattern
update_config() {
local pattern="$1"
local version="$2"
# Remove old entry for this pattern
sed -i "/^$pattern:/d" "$CONFIG_FILE"
# Add new entry
echo "$pattern:$version" >> "$CONFIG_FILE"
}
# Get unique installed versions
get_installed_versions() {
# Get only the actual installed versions, not aliases
local raw_output=$(nvm ls --no-colors)
# Extract only the lines with version numbers that have spaces before them (actual installations)
local versions=$(echo "$raw_output" | grep -E '^\s+v[0-9]+\.[0-9]+\.[0-9]+' | grep -o "v[0-9]\+\.[0-9]\+\.[0-9]\+" | tr -d 'v')
# Use sort and uniq to remove duplicates
local unique_versions=$(echo "$versions" | sort -V | uniq)
echo "$unique_versions"
}
# Main script execution
echo "Resolving version patterns..."
VERSIONS_TO_KEEP=()
# Process each version pattern
for pattern in "${VERSION_PATTERNS[@]}"; do
resolved_version=$(resolve_version "$pattern")
if [ -n "$resolved_version" ]; then
# Check if this version is already in our keep list (avoid duplicates)
if [[ ! " ${VERSIONS_TO_KEEP[*]} " =~ " ${resolved_version} " ]]; then
VERSIONS_TO_KEEP+=("$resolved_version")
update_config "$pattern" "$resolved_version"
echo "Pattern '$pattern' resolves to Node.js v$resolved_version"
else
echo "Pattern '$pattern' resolves to Node.js v$resolved_version (already in keep list)"
fi
fi
done
echo "Versions to keep: ${VERSIONS_TO_KEEP[*]}"
# Get all installed versions (unique)
INSTALLED=()
while read -r version; do
if [ -n "$version" ]; then
INSTALLED+=("$version")
fi
done < <(get_installed_versions)
echo "Currently installed versions: ${INSTALLED[*]}"
# Install any missing versions that should be kept
for keep_version in "${VERSIONS_TO_KEEP[@]}"; do
if [[ ! " ${INSTALLED[*]} " =~ " ${keep_version} " ]]; then
echo "Installing Node.js v$keep_version"
nvm install "$keep_version"
else
echo "Already installed: Node.js v$keep_version"
fi
done
# Remove versions that shouldn't be kept
for installed_version in "${INSTALLED[@]}"; do
if [[ ! " ${VERSIONS_TO_KEEP[*]} " =~ " ${installed_version} " ]]; then
echo "Removing Node.js v$installed_version"
nvm uninstall "$installed_version"
else
echo "Keeping Node.js v$installed_version"
fi
done
# Check for updates to pattern-based versions
echo "Checking for updates to pattern-based versions..."
for pattern in "${VERSION_PATTERNS[@]}"; do
# Skip exact version patterns
if [[ $pattern =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
continue
fi
# Get the current version for this pattern from config
current_version=$(grep "^$pattern:" "$CONFIG_FILE" | cut -d: -f2)
# Get the latest version for this pattern
latest_version=$(resolve_version "$pattern")
if [ "$current_version" != "$latest_version" ] && [ -n "$latest_version" ]; then
echo "Pattern '$pattern' has a new version: v$latest_version (was v$current_version)"
# Install new version with packages from old version
if [ -n "$current_version" ] && nvm which "$current_version" >/dev/null 2>&1; then
echo "Updating from v$current_version to v$latest_version"
nvm install "$latest_version" --reinstall-packages-from="$current_version"
nvm uninstall "$current_version"
else
echo "Installing v$latest_version"
nvm install "$latest_version"
fi
# Update config
update_config "$pattern" "$latest_version"
fi
done
echo "Version management complete!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment