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.
note: shook
is designed to run on linux systems that use systemd
.
shook
can be installed using cargo-binstall
:
cargo binstall system-hook
shook
can be downloaded from Github Releases
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
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.
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;
}
}
}
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 an 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.
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.
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.
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:
username = "rcullen"
repo_path = "/home/rcullen/rust/test-webhoks"
remote = "origin"
branch = "master"
system_name = "test-restart"
update_events = ["push"]
socket_group = "www-data"
socket_user = "www-data"
[addr]
type = "Unix"
value = "/var/run/shook.sock"
- username:
shook
willsu
to this user to rungit pull
so that the properhttps
orssh
verification is applied. - repo_path:
shook
will use this directory as its working directory, pull changes here, and find theshook.toml
here. - remote: This is the remote
shook will pull from with git
it is theorigin
ingit pull origin main
- branch: The branch
shook
will try to pull from - system_name: This is the system that
shook
will restart when it receives a webhook payload - update_events: A list Github webhook events that
shook
will pull code after receiving - socket_group: If
shook
is configured to listen on a unix socket, it willchgrp
the socket to this group - socket_user: If
shook
is configured to listen on a unix socket, it willchown
the 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
.
The daemon
command is a simple proxy over systemctl
. It can be easily bypassed without causing any harm.