#github-webhook #webhook #systemd #ci #command-line

app system-hook

shook: webhook server to automatically update production servers

2 releases

0.1.1 Apr 9, 2023
0.1.0 Apr 9, 2023

#764 in HTTP server

MIT license

61KB
1K SLoC

system-hook

Crates.io Docs.rs

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 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. 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:

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.

Dependencies

~22–33MB
~463K SLoC