Created
April 20, 2025 20:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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