system-hook
shook at its core is a web server that listens for webhooks from Github
and then will automatically pull new changes to your repository and restart
your production servers with the new code. Shook assumes your server is running
through systemd and will automatically pull new changes and restart the service.
Installation
note: shook is designed to run on linux systems that use systemd.
cargo-binstall
shook can be installed using cargo-binstall:
cargo binstall system-hook
Github Releases
shook can be downloaded from Github Releases
Build from Source
shook can be built from source using cargo:
cargo install system-hook
Or, locally:
git clone https://github.com/beaconbrigade/system-hook.git
cd system-hook
cargo build --release
Usage
shook has three main commands: init, serve and daemon. To prepare shook
navigate to the repository you want to watch, and run sudo shook init. Sidenote: shook
usually needs to run as root because it interacts with systemctl or needs to write
files in the /etc/systemd/system/ directory. shook will guide you through creating a config
and it will generate the shook.toml in your repository's directory and /etc/systemd/system/shook.service.
The shook.toml file tells shook how to run.
After generating a shook.toml, shook can be run using shook serve which starts the server
in your terminal, or by running sudo shook daemon . start which starts the shook systemd service.
For testing out shook it is good to play with shook serve, you can use command line arguments
to augment values in the shook.toml file. When running in production it would probably be more helpful
to run sudo shook daemon . enable so shook is started when your computer starts. Note: shook daemon
just runs systemctl start, stop and enable under the hood, so you can bypass shook and run those
directly if you want.
Sample behind nginx
If your main server is running behind nginx, your webhook proxy might look like this:
http {
server {
# example route to serve static files
location / {
root /www-data;
}
# proxy `shook` behind nginx
location /webhook {
# remove the '/webhook' part of the url so requests to https://yourserver.com/webhook
# are POSTed to '/' on `shook` (as it expects).
rewrite /webhook(.*) /$1 break;
# pass requests onto `shook`
proxy_pass http://unix://var/run/shook.sock;
}
}
}
Testing
As a side note, it can be really handy to test if your webhook server is working. You can use the
Github CLI to help with this. Refer to here
to set up webhook testing. To test shook I created a test repository on Github with a script
update.sh that appends data to the README.md file, then commits and pushes. Then, running shook serve --log-level=trace
in one terminal, gh webhook ... in another and ./update.sh in a third you can test your deployment.
Details
shook creates its own systemd service to start listening for events. The shook service simply runs
shook serve from the right directory and sets a default logging level and log file to /var/log/shook.log.
The github-webhook-extract crate provides route extractors for a Github webhook event (note: github-webhook-extract supports
very few events at the moment). The text-completions crate provides environment variable and path tab
completions for the shook init command.
shook init
The init command will generate both the systemd service file for shook and the shook.toml for
your repository. The values for each config value can be optionally passed by command line, and if
they aren't present, they will be read from stdin using dialoguer.
The init command will store each config value in shook.toml stored in your given repositories directory.
The shook.service file generated by shook will invoke shook serve setting the log file to /var/log/shook.log
and will also put the working directory to your repositories path.
shook serve
The serve command will read the shook.toml file to configure itself. When the server receives a POST message
it will extract a Github payload from it, and then check if the event matches the allowed events in your config.
If there's a match, it will then use git to pull the most recent changes then systemctl restart your
service. Each config field influences the server, here's an example:
= "rcullen"
= "/home/rcullen/rust/test-webhooks"
= "origin"
= "master"
= "test-restart"
= ["push"]
= "www-data"
= "www-data"
= ":"
= "shook.service"
[]
= "Unix"
= "/var/run/shook.sock"
- username:
shookwillsuto this user to rungit pullso that the properhttpsorsshverification is applied. - repo_path:
shookwill use this directory as its working directory, pull changes here, and find theshook.tomlhere. - remote: This is the remote
shook will pull from with gitit is theoriginingit pull origin main - branch: The branch
shookwill try to pull from - system_name: This is the system that
shookwill restart when it receives a webhook payload - update_events: A list Github webhook events that
shookwill pull code after receiving - socket_group: If
shookis configured to listen on a unix socket, it willchgrpthe socket to this group - socket_user: If
shookis configured to listen on a unix socket, it willchownthe socket to this user - addr: The address shook will listen on: either a Unix socket (file path) or TCP socket (socket address)
Final note: if shook serves through a unix socket, it will chmod the socket with 0o666.
shook daemon
The daemon command is a simple proxy over systemctl. It can be easily bypassed without causing any harm.