Starting with extsmail I seem to have developed
the accidental hobby of occasionally developing new tools in the Unix
tradition. In this post I’m going to introduce snare, a minimalistic GitHub webhooks runner
daemon for those cases where something like GitHub actions don’t give you
sufficient control. I’ve been using snare in production for over 2 years
for tasks from running CI using specific hardware to sending out email diffs to
automatically restarting Unix daemons when their configuration has been
changed. It’s a simple, but now stable, tool .
What is a webhooks runner? Well, whenever something happens to a GitHub
repository – for example, a new PR is raised, or a commit is pushed to a
PR – GitHub can send an HTTP request to a specified URL informing it of
what happened. Configuring a webhook for a given GitHub repository is
relatively simple: go to that repository, then Settings > Webhooks >
.
Add webhook
snare is a simple program which listens for GitHub webhook events and runs
Unix shell commands based on them. You need a server of your own to run snare
on. When you add a GitHub webhook, you then need to specify
http://yourmachine.com:port/
, a secret (in essence, a shared
password between GitHub and snare) and then choose which events you wish GitHub
to deliver. For example, the default “Just the push event” works well if you
want to send email diffs whenever someone pushes commits to a repository.
snare needs a configuration file to tell it what to do when an event comes
in. A very simple snare.conf
file looks as follows :
listen = ": "; github { match ".*" { cmd = "somecmd"; secret = " "; } }
In essence, snare will listen on
for webhook events, verifying that they were created with the secret
. Each request is relative to a repository:
match
blocks match against a “owner/repository
”
string using Rust’s regex crate for
regular expressions”. Thus ".*"
matches against any repository and
the Unix shell command somecmd
will be run when (any) event is
received for that repository.
Let’s imagine we want to send out email diffs when someone pushes to the
repositories “owner/repo1” or “owner/repo2”. We might create a github
block along the lines of the following:
github { match "owner/repo[12]" { cmd = "ghemaildiff %o %r %e %j email@example.com"; secret = ""; } }
This only matches against the particular repositories we wanted
to match. The command we’re now going to execute is called
ghemaildiff
(I’ll show an example of this below) and it takes five
or more arguments: the repository’s owner (%o
), name
(%r
), the GitHub event type (%e
), a path to the full
JSON of the GitHub event (%j
), and one or more email addresses to
send diffs to. As you’ve probably guessed, snare searches for text like
%e
and replaces it with other text; %%
escapes
percentage characters, should you need to do so.
One of the big problems when executing commands like this is when something
goes wrong — it’s easy for the error to sit unnoticed in a log. Instead,
snare allows one to add an errorcmd
,
which is very similar to cmd
, except a) it’s only executed when
cmd
fails b) it has an additional %s
modifier, which
is a path to a file with the stdout / stderr of the failed command. I typically
use it as follows:
github { match "user/repo[12]" { cmd = "ghemaildiff %o %r %e %j email@