Auto Deploy from GitHub to Local Server on Git Push
Published: 2020-06-26 06:12:55

As developers (oh yeah, as devops guys), it's a no-brainer to automate our workflows to make life simple and easy for us. In this article, I'll demonstrate how to automatically deploy a nodejs web app from GitHub to a production or test server on git push. You can follow the same instructions to deploy any application written in any language.
<h2>Tools used in this demo</h2>
We use quite a few tools in this demo. Each one has a specific role to play in the Continuous Deployment(CD) pipeline we are about to setup. Each of these tools are briefly explained below.
<h3>Nodemon</h3>
Monitors any changes in your node.js application and automatically restarts the server. Though Nodemon is typically used in development for quick re-spins, we'll use it in production in this demo to simply explain you the overall concept.
<h3>Webhook</h3>
Incoming webhooks handler which can execute shell commands.
<h3>SocketXP</h3>
Enables us to receive webhooks from the internet and relay it into your local network without exposing the app to the internet. SocketXP eliminates the need to own a public IP.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8cxc9v6gqlzwx5oxwtti.png)

<h3>Demo app</h3>
We'll use the following simple nodejs web application for this demo.
<pre class="source-code">var http = require('http');
var port = 8080
var handleRequest = function(request, response) {
  console.log('Received HTTP request for URL: ' + request.url);
  response.writeHead(200);
  response.end('Hello World!, v1.0');
};
var server = http.createServer(handleRequest);
server.listen(port);
</pre>
You can find the source code used in this demo at the following github repo:
<a href="https://github.com/socketxp-com/webhook-autoupdate">https://github.com/socketxp-com/webhook-autoupdate</a>
<h3># Git clone the demo app</h3>
First clone the demo app from GitHub.
<pre class="source-code">$ git clone https://github.com/socketxp-com/webhook-autoupdate.git
</pre>
<h3># Install Nodemon</h3>
Change the working directory to the <b>"webhook-autoupdate"</b> git folder you just cloned above. Install nodemon globally and run it.
<pre class="source-code">$ cd webhook-autoupdate

$ npm install -g nodemon
$nodemon
[nodemon] 2.0.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node http-server.js`
</pre>
<h3>Curl the demo web app</h3>
Access the NodeJS web app running at http://localhost:8080 using curl.
<pre class="source-code">$curl http://localhost:8080
Hello World!, v1.0
</pre>
<h3># Install Webhook app</h3>
To handle push notifications (or webhooks) from github (or any webhook sender application for that matter), you don't need to implement a webhook handler yourself. There are apps available to offload the job. One such app is <a href="https://github.com/adnanh/webhook"><b>Webhook app</b></a>.

Using the Webhook app easily create HTTP endpoints (webhooks handler) on your server, which you can use to execute configured commands. Webhook app also allows you to specify rules which must be satisfied in order for any hook to be triggered.

Download the Webhook app (choose one appropriate for your OS and Platform) from the <a href="https://github.com/adnanh/webhook/releases"><b>github repo release folder</b></a>
<pre class="source-code">$wget https://github.com/adnanh/webhook/releases/download/2.7.0/webhook-darwin-amd64.tar.gz
$ls
webhook-darwin-amd64.tar.gz
$tar -xvf webhook-darwin-amd64.tar.gz 
x webhook-darwin-amd64/
x webhook-darwin-amd64/webhook
$ls
webhook-darwin-amd64		webhook-darwin-amd64.tar.gz
$cd webhook-darwin-amd64
$ls
webhook
$
</pre>
You'll find a "hooks.json" file in the <b>"webhook-autoupdate"</b> GitHub repository that you cloned in the previous section. Update the hooks.json file with the correct directory names and secret value, as shown by the marked lines below.
<pre class="source-code">[
  {
    "id": "webhook",
    "execute-command": "/Users/gannygans/webhook-autoupdate/update-nodejs.sh",   // &lt;&lt;&lt;=== Update this field to your directory path
    "command-working-directory": "/Users/gannygans/webhook-autoupdate",    // &lt;&lt;&lt;=== Update this field to your directory path
    "response-message": "I got the payload!",
    "response-headers":
    [
      {
        "name": "Access-Control-Allow-Origin",
        "value": "*"
      }
    ],
    "pass-arguments-to-command":
    [
      {
        "source": "payload",
        "name": "head_commit.id"
      },
      {
        "source": "payload",
        "name": "pusher.name"
      },
      {
        "source": "payload",
        "name": "pusher.email"
      }
    ],
    "trigger-rule":
    {
      "and":
      [
        {
          "match":
          {
            "type": "payload-hash-sha1",
            "secret": "adsFgGdf1T23423",    // &lt;&lt;&lt;&lt;==== Update the secret 
            "parameter":
            {
              "source": "header",
              "name": "X-Hub-Signature"
            }
          }
        },
        {
          "match":
          {
            "type": "value",
            "value": "refs/heads/master",
            "parameter":
            {
              "source": "payload",
              "name": "ref"
            }
          }
        }
      ]
    }
  }
]
</pre>
Use this hooks.json file to kickstart the webhook app.
<pre class="source-code">$./webhook -hooks hooks.json -verbose
[webhook] 2020/06/25 16:27:42 version 2.7.0 starting
[webhook] 2020/06/25 16:27:42 setting up os signal watcher
[webhook] 2020/06/25 16:27:42 attempting to load hooks from hooks.json
[webhook] 2020/06/25 16:27:42 os signal watcher ready
[webhook] 2020/06/25 16:27:42 found 1 hook(s) in file
[webhook] 2020/06/25 16:27:42 	loaded: webhook
[webhook] 2020/06/25 16:27:42 serving hooks on http://0.0.0.0:9000/hooks/{id}
</pre>
Note: The above webhook URL "http://0.0.0.0:9000/hooks/{id}" is a localhost url. Github cannot send notifications to this IP address because it is not a publicly reachable IP address. Github needs a publicly reachable public URL to send push notifications.

This is where SocketXP solution comes in handy. We can use the SocketXP Secure Reverse Proxy Tunneling service to relay webhooks notifications from Github to the localhost Webhook app.
<h3># Install SocketXP agent</h3>
Download and install SocketXP agent on your system following the instructions here - <a href="https://www.socketxp.com/download/">SocketXP Downaload and Install</a>.

After SocketXP agent is installed, request the agent to relay the github webhook to your local webhook URL as shown below.
<pre class="source-code">$ socketxp relay http://0.0.0.0:9000/hooks/webhook

Connected.
Public URL -&gt; https://webhook.socketxp.com/ganeshvelrajan-0er32gfj
</pre>
Now copy the above SocketXP Public URL and use it to update the webhook settings in the Github page of your project repository as shown below. Also make sure you select the payload content type to "applicatin/json" in the dropdown box. Save the changes.

<img class="aligncenter wp-image-1078 size-full" src="https://www.socketxp.com/wp-content/uploads/2020/06/Auto-Deploy-Github-to-Server-.png" alt="Automatically Deploy from GitHub to Server using Webhook" width="1233" height="878" />
<h2>Begin Auto Deploy Demo</h2>
Let's begin the demo by committing a code change into Github.
<h3>Git commit and push</h3>
Commit a code change into your Github project. I have bumped up the version string in my http-server.js" to version 2.0 and committed the code changes.
<pre class="source-code">$git commit http-server.js -m "minor fix"
[master cae0d29] minor fix
 1 file changed, 1 insertion(+), 1 deletion(-)
$git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 286 bytes | 286.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/socketxp-com/webhook-autoupdate.git
   f4f1516..cae0d29  master -&gt; master
</pre>
<h3>SocketXP webhook relay agent</h3>
Check if the SocketXP agent has received the webhook notifications from Github.
<pre class="source-code">Webhook received:
POST /hooks/webhook HTTP/1.1
Host: webhook.socketxp.com
Accept: */*
Accept-Encoding: gzip
Content-Length: 8077
Content-Type: application/json
User-Agent: GitHub-Hookshot/3ea2330
X-Github-Delivery: b55dd9f6-b6d3-11ea-9b25-2831f872cb2c
X-Github-Event: push
X-Hub-Signature: sha1=43b9f9114ecf5c3da6e306976f95766f52c6bdd9

{"ref":"refs/heads/master","before":"714706d5f6f411581ec7abf1653c4a1e3cd6d53b","after":"49d0ec0d1310917338baf1dcda5e3bc99b2f2644","repository":{"id":274867773,"node_id":"MDEwOlJlcG9zaXRvcnkyNzQ4Njc3NzM=","name":"webhook-autoupdate","full_name":"socketxp-com/webhook-autoupdate","private":false,"owner":{"name":"socketxp-com","email":"64076796+socketxp-com@users.noreply.github.com",  ... }

</pre>
<h3>Check the webhook app</h3>
Check if the webhook app has received the webhook notification from the SocketXP agent and processed the webhook notification.
<pre class="source-code">$./webhook -hooks hooks.json -verbose
[webhook] 2020/06/25 16:27:42 version 2.7.0 starting
[webhook] 2020/06/25 16:27:42 setting up os signal watcher
[webhook] 2020/06/25 16:27:42 attempting to load hooks from hooks.json
[webhook] 2020/06/25 16:27:42 os signal watcher ready
[webhook] 2020/06/25 16:27:42 found 1 hook(s) in file
[webhook] 2020/06/25 16:27:42 	loaded: webhook
[webhook] 2020/06/25 16:27:42 serving hooks on http://0.0.0.0:9000/hooks/{id}
<b>[webhook] 2020/06/25 16:35:00 [0fd63b] incoming HTTP POST request from 127.0.0.1:57435
[webhook] 2020/06/25 16:35:00 [0fd63b] webhook got matched
[webhook] 2020/06/25 16:35:00 [0fd63b] webhook hook triggered successfully
[webhook] 2020/06/25 16:35:00 [0fd63b] 200 | 18 B | 531.245µs | webhook.socketxp.com | POST /hooks/webhook
[webhook] 2020/06/25 16:35:00 [0fd63b] executing /Users/gannygans/webhook-autoupdate/update-nodejs.sh (/Users/gannygans/webhook-autoupdate/update-nodejs.sh) with arguments ["/Users/gannygans/webhook-autoupdate/update-nodejs.sh" "49d0ec0d1310917338baf1dcda5e3bc99b2f2644" "socketxp-com" "64076796+socketxp-com@users.noreply.github.com"] and environment [] using /Users/gannygans/webhook-autoupdate as cwd
   057cadf..069d282  master     -&gt; origin/master
Updating 057cadf..069d282
Fast-forward
 http-server.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Current branch master is up to date.

[webhook] 2020/06/25 16:35:02 [0fd63b] command output: Already up to date.
Current branch master is up to date.

[webhook] 2020/06/25 16:35:02 [0fd63b] finished handling webhook</b>
</pre>
<h3>Check Nodemon has restarted the web server</h3>
<pre class="source-code">$nodemon
[nodemon] 2.0.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
<b>[nodemon] starting `node http-server.js`
[nodemon] restarting due to changes...</b>
</pre>
<h3>Curl the web server</h3>
<pre class="source-code">$curl http://localhost:8080
Hello World!, v2.0
</pre>
The hello-world application displays "v2.0" in the output, which proves that the auto deployment has worked as we expected.

That's all folks!

You can receive webhooks from any online service to your local server using SocketXP. SocketXP eliminates the need to own a public IP address to receive webhook notifications from online services. Moreover, SocketXP has a free-tier for developers.