Stephen Coyle

BuildWatcher

BuildWatcher is a very simple, scriptable, status-monitoring tool. Its window shows its current status, and the time it was set. It floats on top of all other windows, and persists across all desktop Spaces. It was borne of my tendency to get distracted while waiting for long Xcode builds to finish. Sounds effects and notifications just aren't intrusive enough, so this is kind of a deliberately annoying thing to have on-screen.

It has five states:

unset
error
failed
building
success

Usage

There are two ways to set its status: its URL scheme and a command-line interface. There are slight differences in how each method behaves, but they use the same state keywords shown above.

CLI

This is my preferred way to set status. It doesn't cause the app to steal focus when it updates, and it doesn't launch the app if it's not currently open. It works by running the binary inside the app bundle1 with the set argument followed by a keyword. In other words:

  • .../BuildWatcher.app/Contents/MacOS/BuildWatcher set keyword

Assuming you install the app in /Applications, the commands would be:

  • /Applications/BuildWatcher.app/Contents/MacOS/BuildWatcher set success

  • /Applications/BuildWatcher.app/Contents/MacOS/BuildWatcher set failed etc.

URL scheme

The url scheme is install-directory-agnostic, and it'll launch the app if it's not already running. It also unavoidably(?) causes app to steal focus when the URL is invoked. I find these behaviours a little too annoying for my purposes. The scheme is:

  • buildwatcher://keyword

And some example URLs:

(those are actual links; if you've got BuildWatcher open, you can click one to test it out)

Scripting

For my own usage, I've created a few shell scripts to run the update commands above, and I've set them to run when Xcode starts a build, etc. But you could run them anywhere you like, for any other task that runs long and is easy to forget about.

Download BuildWatcher.


  1. Reusing the same binary for both the app and the command-line interface saved a lot of pain around sandboxing. Embedded command-line helper tools need to be sandboxed for App Store distribution. A shared sandbox is a non-starter, though, as the tool wouldn't be run from within the parent app's process. It also seems not to be possible to give a command-line utility its own independent sandbox. The shared binary approach means everything is signed and sandboxed properly. ↩︎