loading...

Webhook to auto-deploy on git push to Github

rrampage profile image Raunak Ramakrishnan ・3 min read

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

rrampage profile

Raunak Ramakrishnan

@rrampage

Passionate about databases, distributed systems and functional programming.

Discussion

pic
Editor guide
 

Have you heard of GitHub Actions?

 

Yes. I use them for running tests, static analysis and building artifacts.

 

Okay, I know you can use it to do same thing.