# `system-hook`
[](https://crates.io/crates/system-hook)
[](https://docs.rs/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`](https://github.com/cargo-bins/cargo-binstall):
```shell
cargo binstall system-hook
```
### Github Releases
`shook` can be downloaded from [Github Releases](https://github.com/beaconbrigade/system-hook/releases/latest)
### Build from Source
`shook` can be built from source using cargo:
```shell
cargo install system-hook
```
Or, locally:
```shell
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:
```nginx
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](https://cli.github.com/) to help with this. Refer to [here](https://docs.github.com/en/webhooks-and-events/webhooks/receiving-webhooks-with-the-github-cli)
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.
## 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`](https://github.com/console-rs/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:
```toml
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` will `su` to this user to run `git pull` so that the proper `https` or `ssh` verification is
applied.
* repo_path: `shook` will use this directory as its working directory, pull changes here, and find the `shook.toml`
here.
* remote: This is the remote `shook will pull from with git` it is the `origin` in `git 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 will `chgrp` the socket to this group
* socket_user: If `shook` is configured to listen on a unix socket, it will `chown` 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`.
### `shook daemon`
The `daemon` command is a simple proxy over `systemctl`. It can be easily bypassed without causing any harm.