
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 also be applied to your tools. Specifically, in this case, I’ll demonstrate how to sharpen the saw via the care and upkeep of your machine.
While I happen to use macOS for development purposes, this maintenance philosophy can apply to any operation system. For the purpose of this article, I’ll remain focused on macOS, Bash scripting, and the languages I use to power my craft. Hold on tight, you’re about to learn what it’s like to live proactively through a little script automation! 🚀
Frequency
As a general rule of thumb, I perform machine maintenance on a weekly basis. Sometimes, depending open source activity, I’ll update my machine mid-week to pick up changes based on news coming into 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() {
hbsu
gemuc
rustup update
docker system prune --force
docker buildx prune --force
(
cd $HOME/Engineering/OSS
bca
bua
gvaca
)
}
The above messages several aliases and functions. To demystify, I’ll walk you through the terminology.
Homebrew
The first line in the eup
function 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"
The above alias is:
-
Updating Homebrew by pulling down recent updates to formulas and casks.
-
Upgrading all formulas and casks to their latest versions.
-
Deleting 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:
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH" # Ensure Homebrew in the path.
export HOMEBREW_BAT=1 # Enable Bat colorization.
export HOMEBREW_CURL_RETRIES=3 # Retry connection three times before giving up.
export HOMEBREW_FORCE_BREWED_CURL=1 # Default to using Homebrew managed `curl`.
export HOMEBREW_FORCE_BREWED_GIT=1 # Default to using Homebrew managed `git`.
export HOMEBREW_NO_ANALYTICS=1 # Disable any kind of analytics tracking.
export HOMEBREW_NO_AUTO_UPDATE=1 # Disable auto-update so you can control manually.
export HOMEBREW_NO_INSECURE_REDIRECT=1 # Disable any kind of insecure redirect.
export HOMEBREW_NO_INSTALL_CLEANUP=1 # Disable auto-cleanup so you can control manually.
export HOMEBREW_PREFIX="$(brew --prefix)" # Enhance alias/function performance by defining your own prefix.
💡 Bat is amazing and highly recommend installing if not using already. You might enjoy my screencast, on this subject, for further usage.
RubyGems
The second line in the eup
function 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.
Rust
The third line in the eup
function messages the rustup
Rust
command for ensuring your are running the latest version of Rust. In this case, the command is
simple enough that no Bash alias or function is required.
Docker
The fourth line in the eup
function messages the docker
Docker
command to ensure all unused imagers are forcefully pruned. Docker images can consume a large
portion disk space quickly so always good to keep this under control.
Bundler
The next couple of lines in the eup
function 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, I don’t need to keep this binary around so it is safe to delete. When managing ~30+ projects, like this, 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.
Git
The last line in the eup
function is devoted to Git repository
maintenance. 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
rerere
is so valuable.
Conclusion
I’m primarily a Ruby engineer, so your machine maintenance might vary, but hopefully this information will encourage you to keep your machine in top notch working condition.
Additionally, you can use and/or pilfer my Dotfiles or macOS Configuration projects. A ton of knowledge is captured in those projects so take the time to level up as necessary.
By being proactive instead of reactive about your machine maintenance, you’ll be the first to know of breaking changes, course correct, and prevent falling into maintaining legacy software that’s extremely costly, not only to you but your business.