In the book The 7 Habits of Highly Effective People, Stephen Covey writes about Sharpening the Saw which emphasizes keeping your skills sharp but can be applied to your tools as well. I’ll show you, with a little macOS automation, how you and adapt and keep your machine up-to-date in your own workflow.
Frequency
As a rule of thumb, performing machine maintenance on a two-day basis with a weekly fallback, if otherwise, is a good practice. Sometimes, depending community activity, I’ll update my machine daily to pick up changes based on news in my feed reader.
Your mileage may vary so figure out what works best for you and stick with it!
Shell Script
If you’re like me, any kind of manual maintenance will make your hair stand on end. Automation is
key to making machine maintenance frictionless as possible. In my case, I’ve automated the full
process via a single Bash function: eup. The function name is a mnemonic shorthand for:
[e]nvironment + [up]date = eup. Here’s the Bash source code:
# Label: Environment Update
# Description: Update environment with latest software.
eup() {
softwareupdate --all --install --agree-to-license
xcode-select --install
hbsu
rustup update
cargo install-update --all
tldr --update
docker system prune --force
docker buildx prune --force
gpua
gvaca
gemuc
bca
bua
noti --title "Environment Update"
}
The above messages several aliases and functions. To demystify, I’ll walk you through each line of the script.
macOS
The first couple of lines ensure macOS and Xcode are up-to-date.
Homebrew
The next line messages the hbsu Homebrew alias. Here’s the source code:
# [h]ome[b]rew + [s]oftware + [u]date = hbsu
alias hbsu="brew update && brew upgrade && brew cleanup"
This alias:
-
Updates Homebrew by pulling down recent updates to formulas and casks.
-
Upgrades all formulas and casks to their latest versions.
-
Deletes old formulas and casks so you only have the latest versions on your system in order to reduce disk space consumed.
When using Homebrew, there are several environment variables you’ll want to configure in order to
optimize your Homebrew experience. I recommend adding the following to your .bashrc file:
# Ensure Homebrew in your path.
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
# Enable Bat colorization.
export HOMEBREW_BAT=1
# Retry the connection three times before giving up.
export HOMEBREW_CURL_RETRIES=3
# Default to using Homebrew managed `curl`.
export HOMEBREW_FORCE_BREWED_CURL=1
# Default to using Homebrew managed `git`.
export HOMEBREW_FORCE_BREWED_GIT=1
# Disable analytics tracking.
export HOMEBREW_NO_ANALYTICS=1
# Disable auto-update so you can control manually.
export HOMEBREW_NO_AUTO_UPDATE=1
# Disable any kind of insecure redirect.
export HOMEBREW_NO_INSECURE_REDIRECT=1
# Disable auto-cleanup so you can control manually.
export HOMEBREW_NO_INSTALL_CLEANUP=1
# Enhance alias/function performance by defining your own prefix.
export HOMEBREW_PREFIX="$(brew --prefix)"
💡 Bat is a super charged cat with color coding, syntax highlighting, and much more. Highly recommend, if not using already.
Rust
The next couple of lines message the rustup Rust
command for ensuring you’re running the latest version of Rust. This includes use of the install-update crate for keeping all globally installed crates up-to-date and the tldr crate for keeping documentation up-to-date.
Docker
The next couple of lines use the docker Docker command to ensure all unused images are forcefully pruned. Docker images (and cross platform images) can consume a large portion disk space so always good to keep this under control.
Git
The next couple of lines are devoted to Git repository maintenance. The first, gpua, is short for Git Pull All to ensure all projects are up-to-date with the remote repository. The second, gvaca, is for cleaning the repository. Here’s the source code:
# Label: Git Verify and Clean (all)
# Description: Verify and clean objects for projects in current directory.
gvaca() {
while read -r project; do
(
cd "$project"
if [[ -d ".git" ]]; then
printf "\n33[36m${project}33[m:\n" # Outputs in cyan color.
git fsck && git repack -Ad && git maintenance run --task=gc && git rerere gc
fi
)
done < <(ls -A1)
}
The above walks through each open source project as follows:
-
fsck - Verifies the connectivity and validity of all repository objects so I’ll know immediately if anything is corrupt.
-
repack - Packs unpacked objects into a single pack and removes redundant packs.
-
maintenance - Handles garbage collection by removing unnecessary repository files and optimizing the repository for improved performance. It does all of this by respecting the object database lock as described further in the Git Maintenance article.
-
rerere - Prunes old records of conflicted merges no longer necessary. If you’ve read my Git Rebase article you’ll know why
rerereis so valuable.
RubyGems
The next line messages the gemuc RubyGems alias. Here’s the source code:
# [g]em + [u]pdate + [c]lean = gemuc
gem update --system && gem update && gem cleanup
Much like the Homebrew alias, this performs the following for all RubyGems on your system:
-
Updates RubyGems to the latest version for the entire system.
-
Updates all gems for the current version of Ruby you are using.
-
Deletes older versions of gems recently updated in order to reduce disk space consumed.
Bundler
The next couple focus on Bundler. The first function called is: bca. Here’s the source code:
# Label: Bundle Clean (all)
# Description: Clean projects of gem artifacts (i.e. pkg folder).
bca() {
while read -r project; do
(
cd "$project"
if [[ -f "Gemfile.lock" ]]; then
printf "33[36m${project}33[m: " # Outputs project in cyan color.
# Print status if found, otherwise a checkmark for passing status.
if [[ -d "pkg" ]]; then
rm -rf pkg
printf "%s\n" "Cleaned gem artifacts."
else
printf "✓\n"
fi
fi
)
done < <(ls -A1)
}
The above function cleans up all disk space consumed from publishing new gem versions of my open source work. Take, for instance, the Git Lint project. Every time I publish a new version of Git Lint, I’ll end up with the following artifact:
git-lint/pkg/git-lint-x.x.x.gem
Once published, you don’t need to keep this binary around so is safe to delete. When managing multiple projects, you can end up with a lot of artifacts over time and this is a nice way to reduce the number of packages on your system.
The second function is bua and here’s the source code:
# Label: Bundle Update (all)
# Description: Update gems for projects in current directory.
bua() {
while read -r project; do
(
cd "$project"
if [[ -f "Gemfile.lock" ]]; then
rm -f Gemfile.lock
bundle install --quiet
# Print project status if Bundler activity is detected, otherwise a checkmark for passing status.
printf "33[36m${project}33[m: " # Outputs project in cyan color.
if [[ $(git diff | wc -l | tr -d ' ') -gt 0 ]]; then
printf "↑\n"
else
printf "✓\n"
fi
fi
)
done < <(ls -A1)
}
Much like the bca function, this ensures all open source projects are updated to the latest gem
dependencies. It’s one of the fastest ways to ensure all of your open source work is up to date and
works as an early warning sign to know if there are breaking changes that need addressing.
Noti
The last line uses Noti so you get a notification when the script is done.
Conclusion
Being proactive, instead of reactive about, your machine maintenance allows you to be the first to know of breaking changes, course correct, and quickly update as needed. Lack of maintenance gets costly quickly so avoid this burden by tackling the cost in small increments as they pop up. Make it a habit and you’ll never have to spend weeks or, worse, months digging yourself out the hole you’ve built due to neglect.