A small, modular Bash CLI to update common macOS and Linux tooling (Homebrew, global npm packages, global Python packages, etc.).
This script can be disruptive (it updates global environments). Use --dry-run and scope with --only / --skip.
See SPEC.md for the full CLI/module contract, exit codes, and release invariants.
Using the Makefile:
make install
# Recommended (user-writable; enables self-update without sudo):
# make install PREFIX=$HOME/.local
# or: make install PREFIX=/opt/homebrewManual install:
chmod +x ./updates
sudo mkdir -p /usr/local/bin
sudo install -m 0755 ./updates /usr/local/bin/updatesupdates
updates --dry-run
updates --only brew,node
updates --only linux --non-interactive
updates --full
updates --skip python --log-file ./updates.logExample output (trimmed):
Starting updates...
==> brew START
Homebrew 🍺
==> brew END (OK) (12s)
==> SUMMARY ok=1 skip=0 fail=0 total=12s
Done in 12s. 🎉
List available modules:
updates --list-modulesShow help:
updates --helpModules are auto-detected: if the underlying command isn’t installed, the module is skipped (unless you used --only, in which case it’s an error).
brew: update/upgrade Homebrew formulae (+ casks when enabled via--brew-casks/--full)shell: update Oh My Zsh and custom git plugins/themes (auto-detected)linux: upgrade Linux system packages (auto-detectsapt-get/dnf/yum/pacman/zypper/apk)node: upgrade global npm packages viancu+npmpython: upgrade global Python packages viapython3 -m pipmas: upgrade Mac App Store apps viamas(disabled by default; enable with--mas-upgradeor--full)pipx: upgrade pipx-managed apps viapipx upgrade-allrustup: update Rust toolchains viarustup updateclaude: update Claude Code CLI viaclaude updatemacos: list available macOS software updates viasoftwareupdate -l(disabled by default; enable with--macos-updatesor--full)
Install what you actually use:
brew(Homebrew)git(for theshellmodule)ncu(npm-check-updates):npm install -g npm-check-updatesmas:brew install maspipx:brew install pipxrustup: from https://rustup.rsclaude(Claude Code CLI) for theclaudemodule- On Linux: a supported system package manager (
apt-get,dnf,yum,pacman,zypper, orapk) andsudo(if not running as root)
./scripts/lint.sh
./scripts/test.sh- This script updates global environments (
npm -g,pip), which can be disruptive. - Use
--dry-runfirst, and consider--only/--skipto control scope. updatescan self-update from GitHub Releases; disable with--no-self-updateorUPDATES_SELF_UPDATE=0. Self-update works best when installed to a user-writable location (e.g.PREFIX=$HOME/.local).- On macOS, Homebrew casks are disabled by default; enable with
--brew-casks(or--full). On macOS 26+, cask upgrades may be blocked unless your terminal app is allowed under Privacy & Security → App Management (e.g. Ghostty). If you see a system notification like “<Terminal App> tried modifying your system…”, enable App Management or rerun with--no-brew-casks. - On WSL, updates apply to the Linux distro (not Windows itself).
- Output uses ANSI colors when run in a TTY; disable with
--no-colororNO_COLOR=1. When--log-fileis used, colors are disabled to keep logs clean. - If Python is externally-managed (PEP 668),
updatesupgrades user-site packages by default; use--python-break-system-packagesto override (dangerous).
See CONTRIBUTING.md.
MIT — see LICENSE.