Webhook to auto-deploy on git push to Github
Raunak Ramakrishnan
Posted on May 25, 2020
What is a webhook?
A webhook is an endpoint on your server which allows you to execute a particular task. Webhooks are usually triggered by some event. A good use-case for a webhook is running tests on a dedicated test server or deploying your latest master
branch to staging/production.
Github / Gitlab / Bitbucket allow you to specify a webhook URL in your repository settings. Github triggers the webhook which sends the event data on every push.
Webhook server
Webhook is a very useful golang project which runs any script you specify when a particular endpoint is hit.
Download and extract the binary for your operating system from the releases page. For Linux, it is here.
The program takes as config a hooks.json
file:
[
{
"id": "hello-world",
"execute-command": "/home/user/scripts/hello.sh",
"command-working-directory": "/home/user/webhook"
}
]
Replace user
with the username of your linux user.
The hello.sh
script.
#!/bin/bash
echo 'Hello!'
Make the script executable by running chmod +x hello.sh
Start webhook server as webhook -hooks hooks.json -hotreload -logfile webhooks.log
. The server will run on port 9000 by default. You can check if everything is working by running curl http://localhost:9000/hooks/hello-world
. This will print "Hello!" in the log file.
Deploy script
For the purpose of this post, I'll assume the script is called deploy
and is at location /home/user/scripts/deploy
. This script will vary depending on your tech stack and the complexity of your CI process.
A simple example deploy
script:
#!/bin/bash
# If you have a build server which creates binary/jar/artifact
wget 'ARTIFACT_URL'
# Else, git pull and build on the server itself
# Assuming this script stops old instance of your code and starts a new instance with latest artifact
restart-service.sh
Configuration to run deploy script
[
{
"id": "deploy-from-git",
"execute-command": "/home/user/scripts/deploy",
"command-working-directory": "/home/user/scripts",
"trigger-rule":
{
"and":
[
{
"match":
{
"type": "payload-hash-sha1",
"secret": "MyTotallySecretString",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature"
}
}
},
{
"match":
{
"type": "value",
"value": "refs/heads/master",
"parameter":
{
"source": "payload",
"name": "ref"
}
}
}
]
}
}
]
The trigger-rule in config above will ensure that the script is only triggered when header from Github request contains "X-Hub-Signature" with a secret string and the push has occured in master branch.
Make sure that the secret string ("secret" : "MyTotallySecretString") is randomly generated. This secret will need to be entered in Github settings as well.
For Gitlab and Bitbucket, example hook config can be found on repo page here
Expose your webhook server safely to the internet
There are 2 ways of exposing the webhook server to github:
- Proxy using Nginx
- Via a tunnel e.g by downloading ngrok and then running
ngrok http 9000
Nginx configuration
Preferably use HTTPS for your domain with Nginx. A good tutorial here.
Example Nginx config (HTTPS):
upstream webhook {
server localhost:9000;
}
server {
listen 443 ssl http2;
server_name YOUR.DOMAIN.COM;
ssl_certificate YOUR_CERT_CHAIN; # e.g /etc/letsencrypt/live/DOMAIN/fullchain.pem;
ssl_certificate_key YOUR_CERT_KEY; # e.g /etc/letsencrypt/live/DOMAIN/privkey.pem;
include /etc/nginx/options-ssl-nginx.conf;
ssl_dhparam /etc/nginx/ssl-dhparams.pem;
location ~ ^/hooks/(.+)$ {
proxy_pass http://webhook;
}
}
Add your webhook URL to Github
Go to the settings page of your Github repo then click on Webhook. Enter the URL of your webhook server. If using Nginx, it should be something like https://YOUR.DOMAIN.COM/hooks/deploy-from-git
. Make sure you select content type as application/json
and secret to the secret you generated earlier.
Bonus: Create a systemd user service for webhook (Linux)
Create a systemd unit file with path /home/user/.config/systemd/user/webhook.service . This service does not require sudo/root permissions and can be run by the unprivileged user.
[Unit]
AssertPathExists=/home/user/scripts
[Service]
WorkingDirectory=/home/user/scripts
ExecStart=/home/user/scripts/webhook -hooks hooks.json -hotreload -logfile webhooks.log
Restart=always
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=default.target
Do systemctl --user daemon-reload
and systemctl --user start webhook.service
. You can systemctl --user enable webhook.service
to ensure that the service always runs when your machine is booted.
Posted on May 25, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.