Cron for Homebrew

Homebrew is a wonderful package manager and, for macOS users, it’s the only sensible option around. However, keeping up to date is really tedious. In this article we set a cron entry to update everything on a schedule.

I value my time.

And if you’re here, probably you value yours too. Now, brew install always triggers a brew update before installing new packages anyway, so, after a long period of time without upgrading your packages you install a new one it can take an hour and a half of your life updating all your setup.

In this article we’ll:

  1. Create a script with the update commands.
  2. Add a new crontab entry to execute said script.
  3. Monitor executions.

1. Create a script with the update commands.

This is a simple task, for me the idea was to update homebrew and the gcloud (GCP’s CLI) utility. My script looks like this:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# script to update the system

# absolute paths*
HOMEBREW_BIN=/absolute/path/to/brew
GCLOUD_BIN=/absolute/path/to/gcloud

# update commands.
$HOMEBREW_BIN update && && $HOMEBREW_BIN upgrade && $HOMEBREW_BIN cleanup;
$GCLOUD_BIN components update --quiet;

You can use the which(1) command to figure out the absolute path for the binary of any executable you may need to reference. Remember that the execution environment from cron can vary from system to system and it’s a good idea in these scenarios to be absolutely specific.

2. Add a new crontab entry to execute said script.

Always backup cron’s data before any modification is made, you never now.

In order to access the crontab(1), we’ll need to edit the user’s crontab:

1
$ crontab -e

Within the file, we’ll add a new entry for our script:

*/30 * * * * /path/to/your/script.sh &

If you need help setting up your cron expressions, I’d recommend you to visit the crontab.guru configurator so you can experiment and setup more complicated schedules.

3. Monitor executions

As you may already know, the way crontab(1) works is essentially asynchronous from your terminal. Any output produced from the execution context can be monitored by two mechanisms:

  1. Use the default crontab(1) behavior and check the mail(1) command to get our inbox of messages from Cron.
  2. Pipe the output into a specific logfile by using the > or >> operators in bash.

Using the #2 would be the best way to do it since you will have a searchable and meaningful log. However I would advice against it in this case as the executions may be thousands per week so the file will grow in size exponentially and can cause system disruption.

Using the mail(1) command we can monitor the execution in the following way, this is the output of an interactive session:

❯ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/humbertowoody": 1 messages 1 new
>N  1 humbertowoody@cpe-17  Tue Feb 28 16:00  21/968   "Cron <humbertowoody@cpe-172-100-67-24> source /Users/humbertowoody/tools/update-macos"
? 1
Message 1:
From humbertowoody@cpe-172-100-67-24.twcny.res.rr.com  Tue Feb 28 16:00:44 2023
X-Original-To: humbertowoody
Delivered-To: humbertowoody@cpe-172-100-67-24.twcny.res.rr.com
From: humbertowoody@cpe-172-100-67-24.twcny.res.rr.com (Cron Daemon)
To: humbertowoody@cpe-172-100-67-24.twcny.res.rr.com
Subject: Cron <humbertowoody@cpe-172-100-67-24> source /Users/humbertowoody/tools/update-macos.sh && update_macos &
X-Cron-Env: <SHELL=/bin/zsh>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=humbertowoody>
X-Cron-Env: <USER=humbertowoody>
Date: Tue, 28 Feb 2023 16:00:11 -0600 (CST)

Already up-to-date.
Beginning update. This process may take several minutes.

All components are up to date.

? q
(end of session)

Here Cron will send an email with each execution so we can monitor it by using the mail command.

That’s it!

This script has helped me generate a better experience out of Homebrew in my local machine. I hope it helps you on your developer workflow.