Syndication Icon
Published February 1, 2021 Updated March 28, 2021
Cover
Machine Upkeep

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:

  1. Updating Homebrew by pulling down recent updates to formulas and casks.

  2. Upgrading all formulas and casks to their latest versions.

  3. 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:

  1. Updates RubyGems to the latest version for the entire system.

  2. Updates all gems for the current version of Ruby you are using.

  3. 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:

  1. fsck - Verifies the connectivity and validity of all repository objects so I’ll know immediately if anything is corrupt.

  2. repack - Packs unpacked objects into a single pack and removes redundant packs.

  3. 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.

  4. 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.