Skip to content

Instantly share code, notes, and snippets.

@mrflory
Last active August 7, 2026 19:58
Show Gist options
  • Select an option

  • Save mrflory/da0c82c9ee3e84da4b4549a4a829cd38 to your computer and use it in GitHub Desktop.

Select an option

Save mrflory/da0c82c9ee3e84da4b4549a4a829cd38 to your computer and use it in GitHub Desktop.
Script to update all packages on macOS (brew, node.js via nvm, npm, python via uv, uv tools)
#!/usr/bin/env bash
set -euo pipefail
confirm() {
local prompt="$1"
read -r -p "$prompt [y/N]: " answer
case "$answer" in
[Yy]|[Yy][Ee][Ss]) return 0 ;;
*) return 1 ;;
esac
}
run_step() {
local title="$1"
shift
echo
echo "==> $title"
if confirm "Continue with: $title?"; then
"$@"
echo "Done: $title"
else
echo "Skipped: $title"
fi
}
update_brew() {
if ! command -v brew >/dev/null 2>&1; then
echo "Homebrew not found, step will be skipped."
return 0
fi
echo "Currently installed Homebrew packages:"
brew list --versions
brew update
local outdated
outdated="$(brew outdated || true)"
if [ -z "$outdated" ]; then
echo "No outdated Homebrew packages found."
return 0
fi
echo "Outdated Homebrew packages:"
echo "$outdated"
if confirm "Upgrade the listed Homebrew packages now?"; then
brew upgrade
else
echo "Homebrew upgrade skipped after showing the list."
fi
}
cleanup_brew() {
if ! command -v brew >/dev/null 2>&1; then
echo "Homebrew not found, step will be skipped."
return 0
fi
echo "Current Homebrew cache size:"
du -sh "$(brew --cache)" 2>/dev/null || echo "Unable to determine Homebrew cache size."
brew cleanup
if confirm "Prune all Homebrew cached downloads?"; then
brew cleanup --prune=all
else
echo "Kept reusable Homebrew cached downloads."
fi
echo "Homebrew cache size after cleanup:"
du -sh "$(brew --cache)" 2>/dev/null || echo "Unable to determine Homebrew cache size."
}
load_nvm() {
if command -v nvm >/dev/null 2>&1; then
return 0
fi
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
if [ -s "$NVM_DIR/nvm.sh" ]; then
# shellcheck disable=SC1090
. "$NVM_DIR/nvm.sh"
command -v nvm >/dev/null 2>&1
return $?
fi
return 1
}
update_node_nvm() {
if ! load_nvm; then
echo "nvm not found, step will be skipped."
return 0
fi
echo "Currently installed Node versions:"
nvm list
local current
local target_lts
current="$(nvm current 2>/dev/null || true)"
target_lts="$(nvm version-remote --lts 2>/dev/null || true)"
if [ -n "$current" ] \
&& [ "$current" != "none" ] \
&& [ "$current" != "system" ] \
&& [ -n "$target_lts" ] \
&& [ "$target_lts" != "N/A" ] \
&& [ "$current" != "$target_lts" ]; then
nvm install --lts --reinstall-packages-from="$current"
if confirm "Uninstall old Node version $current from nvm?"; then
nvm uninstall "$current" || echo "Failed to uninstall $current; remove it manually if needed."
else
echo "Kept old Node version $current."
fi
else
nvm install --lts
fi
nvm alias default "lts/*"
nvm use --lts >/dev/null
}
update_npm_global() {
if ! command -v npm >/dev/null 2>&1; then
echo "npm not found, step will be skipped."
return 0
fi
echo "Currently installed global npm packages:"
npm list -g --depth=0
npm -g update
}
python_version_gt() {
local lhs="$1"
local rhs="$2"
local lhs_major lhs_minor lhs_patch rhs_major rhs_minor rhs_patch
IFS=. read -r lhs_major lhs_minor lhs_patch <<< "$lhs"
IFS=. read -r rhs_major rhs_minor rhs_patch <<< "$rhs"
if ((lhs_major != rhs_major)); then
((lhs_major > rhs_major))
return $?
fi
if ((lhs_minor != rhs_minor)); then
((lhs_minor > rhs_minor))
return $?
fi
((lhs_patch > rhs_patch))
}
list_installed_uv_cpython_versions() {
uv python list --managed-python --only-installed 2>/dev/null \
| awk '
$1 ~ /^cpython-[0-9]+\.[0-9]+\.[0-9]+-/ {
version = $1
sub(/^cpython-/, "", version)
sub(/-.*/, "", version)
seen[version] = 1
}
END {
for (version in seen) {
print version
}
}
'
}
update_uv_python() {
if ! command -v uv >/dev/null 2>&1; then
echo "uv not found, step will be skipped."
return 0
fi
echo "Currently installed Python versions via uv:"
uv python list
if uv python upgrade --help >/dev/null 2>&1; then
uv python upgrade
elif uv python install --help 2>/dev/null | grep -q -- "--upgrade"; then
uv python install --upgrade
else
echo "uv does not support Python upgrade commands, step will be skipped."
return 0
fi
local versions=()
local version
while IFS= read -r version; do
versions+=("$version")
done < <(list_installed_uv_cpython_versions)
if [ "${#versions[@]}" -eq 0 ]; then
echo "No uv-managed CPython versions found to clean up."
return 0
fi
local newest="${versions[0]}"
for version in "${versions[@]}"; do
if python_version_gt "$version" "$newest"; then
newest="$version"
fi
done
echo "Newest uv-managed CPython version: $newest"
local old_versions=()
for version in "${versions[@]}"; do
if [ "$version" != "$newest" ]; then
old_versions+=("$version")
fi
done
if [ "${#old_versions[@]}" -eq 0 ]; then
echo "No old uv-managed CPython versions found."
return 0
fi
echo "Old uv-managed CPython versions:"
printf ' %s\n' "${old_versions[@]}"
if confirm "Uninstall old uv-managed CPython versions?"; then
uv python uninstall "${old_versions[@]}" \
|| echo "Failed to uninstall one or more old Python versions; remove them manually if needed."
else
echo "Kept old uv-managed CPython versions."
fi
}
update_uv_global() {
if ! command -v uv >/dev/null 2>&1; then
echo "uv not found, step will be skipped."
return 0
fi
echo "Currently installed global uv tools:"
uv tool list
uv tool upgrade --all
}
update_global_skills() {
if ! command -v npx >/dev/null 2>&1; then
echo "npx not found, step will be skipped."
return 0
fi
npx skills update --global
}
update_hermes() {
if ! command -v hermes >/dev/null 2>&1; then
echo "hermes not found, step will be skipped."
return 0
fi
hermes update
}
echo "Interactive update script started."
run_step "Update Homebrew packages" update_brew
run_step "Clean Homebrew cache" cleanup_brew
run_step "Update Node.js (nvm, LTS)" update_node_nvm
run_step "Update global npm packages" update_npm_global
run_step "Update Python via uv" update_uv_python
run_step "Update global uv tools" update_uv_global
run_step "Update globally installed skills" update_global_skills
run_step "Update Hermes" update_hermes
echo
echo "All steps completed."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment