snare

snare is a minimalistic GitHub webhooks runner daemon. When snare receives a webhook event from a given repository, it authenticates the request, and then executes a user-defined “per-repo program” with information about the webhook event.

Download and docs

Latest release: snare-0.4.11 (2024-07-25)

  • Handle both JSON and urlencoded requests.

All releases

Repository (issues, PRs, etc.)

Man pages for the latest release:

Recipes

Similar programs

Quick setup

snare has the following command-line format:

Usage: snare [-c <config-path>] [-d]

where:

The man page for snare contains more details.

The minimal recommended configuration file is:

listen = "<ip-address>:<port>";

github {
  match ".*" {
    cmd = "/path/to/prps/%o/%r %e %j";
    errorcmd = "cat %s | mailx -s \"snare error: github.com/%o/%r: %x %?\" someone@example.com";
    secret = "<secret>";
  }
}

where:

The man page for snare.conf contains the complete list of configuration options.

Commands

snare can be used to run any command runnable from the Unix shell. The “per-repo program” model as documented above is one common way of doing this. For example, snare’s GitHub repository is https://github.com/softdevteam/snare. If we set up a web hook up for that repository that notifies us of pull request events, the command:

/path/to/prps/softdevteam/snare pull_request /path/to/json

will be executed, where:

The softdevteam_snare program can then execute whatever it wants. In order to work out precisely what event has happened, you will need to read GitHub’s webhooks documentation.

Example per-repo program

Users can write per-repo programs in whatever system/language they wish, so long as the matching file is marked as executable. The following simple example uses shell script to send a list of commits and diffs to the address specified in $EMAIL on each push to master. It works for any public GitHub repository:

#! /bin/sh

set -eufx

# A list of email addresses separated by spaces.
EMAILS="someone@example.com someone.else@example.com"
# A GitHub URL either https or git.
REPO_URL="git@github.com:owner/repo.git"

if [ "$1" != "push" ]; then
    exit 0
fi

ref=`jq .ref "$2" | tr -d '\"'`
if [ "$ref" != "refs/heads/master" ]; then
    exit 0
fi

repo_fullname=`jq .repository.full_name "$2" | tr -d '\"'`
repo_url=`jq .repository.html_url "$2" | tr -d '\"'`
before_hash=`jq .before "$2" | tr -d '\"'`
after_hash=`jq .after "$2" | tr -d '\"'`
echo "$before_hash" | grep -E "^[a-fA-F0-9]+$" 2>&1 > /dev/null
echo "$after_hash" | grep -E "^[a-fA-F0-9]+$" 2>&1 > /dev/null

git clone "$REPO_URL" repo
cd repo
for email in `echo "$EMAILS"`; do
    git log --reverse -p "$before_hash..$after_hash" | mail -s "Push to $repo_fullname" "$email"
done

where jq is a command-line JSON processor. Depending on your needs, you can make this type of script arbitrarily more complex and powerful (e.g. not cloning afresh on each pull).

Note that this program is deliberately untrusting of external input: it is careful to quote all arguments obtained from JSON; and it uses a fixed directory name (repo) rather than use a file name from JSON that might include characters (e.g. ../..) that would cause the script to leak data about other parts of the file system.

Integration with GitHub

snare runs an HTTP server which GitHub can send webhook requests to. Configuring a webhook for a given GitHub repository is relatively simple: go to that repository, then Settings > Webhooks > Add webhook. For payload, specify http://yourmachine.com:port/, specify a secret (which you will then reuse as the secret in snare.conf) and then choose which events you wish GitHub to deliver. For example, the default “Just the push event” works well with the email diff sending per-repo program above, but you can specify whichever events you wish.

HTTPS/TLS

snare runs an HTTP server. If you wish, as is recommended, to send your webhooks over an encrypted connection, you will need to run a proxy in front of snare e.g. nginx or relayd.