Benjamin Sago / ogham / cairnrefinery / etc…

Technical notes Maintain package lists with Specsheet

I find it useful to maintain a list of Homebrew packages I have installed on my Mac computers, as well as apt packages I have installed on my Linux servers, so when I need to set one up anew, I have an easy way to tell how much more configuration I have left to do.

One Homebrew-specific way to do this is by using brew bundle, which creates a file containing the name of every formula you’ve installed. Homebrew then lets you automatically install everything specified in that file, or check whether everything listed in there is present. For more information, you can read this tutorial on it.

I use Specsheet instead of Homebrew itself to do this. For one thing, it’s my own software. For another, it lets me write the “is this package installed?” checks in the same way as I write all my others. And for a third, I prefer that Specsheet merely checks whether a package is installed, rather than installing whatever’s missing — in the years that tend to go between installations, I often find I can clear the list out as I scan through it manually, removing things I don’t use anymore.

I have a check document, command-line.toml, that uses the short TOML table definition to check that a bunch of Homebrew formulae are installed:

spec/command-line.toml
homebrew = [
    { formula = 'parallel' },
    { formula = 'telnet' },
    { formula = 'tree' },
    { formula = 'wget' },
]

The actual file is much longer than this, but you get the idea.

For Homebrew Cask, I have a separate check document, apps.toml, that lists my taps and installed casks:

spec/apps.toml
homebrew_tap = [
    { tap = 'dteoh/sqa' },
    { tap = 'melonamin/formulae' },
]

homebrew_cask = [
    { cask = 'docker' },
    { cask = 'slowquitapps' },
    { cask = 'swiftbar' },
]

The reason I use Specsheet for this is that I can have other checks alongside the checks for packages. For example, you have to run a command before running GNU Parallel properly, otherwise it will nag you. Neither Homebrew nor Specsheet will run this command automatically, but at least I can check that it’s been run:

spec/command-line.toml
[[fs]]
name = "I don’t have to pay the GNU project €10,000"
path = '~/.parallel/will-cite'
kind = 'file'

I also have a couple of cmd checks to make sure Homebrew has linked the right versions of the programs. Because both the parallel and moreutils formulae install a binary named parallel, you have to do a whole song-and-dance to get them to co-exist. I wrote a check to make sure the right versions of each are being used:

spec/command-line.toml
[[cmd]]
name = "GNU parallel, not moreutils parallel, is being used"
shell = "parallel --help"
stdout = { string = "GNU Parallel" }
status = 0
tags = [ 'paramore' ]

[[cmd]]
name = "moreutils has been linked"
shell = "errno 42"
stderr = { empty = true }
status = 0
tags = [ 'paramore' ]

Finally, it lets me check that packages are not installed. For example, I started using my own fork of the gist script, because I was unhappy with where the original puts the file containing the token. In order to ensure I don’t accidentally install the official one again, I wrote a check specifically for that case:

spec/command-line.toml
[[homebrew]]
name = "The Homebrew non-XDG gist script is not installed"
formula = 'gist'
state = 'missing'

I find these scripts really useful for keeping my packages in check.