PROFILE

NodeJS Git Github Bash

Webhooks for Auto Deployments in NodeJS

Since I mostly use git for my projects, in my servers, I clone a copy of the project's repo, so that I don't have to FTP files in order to deply. So I'm going to show how to auto deploy the project. Whenever a commit gets pushed to the repo, a POST request will be made to our web server, a git pull will be done on the server via a shell script which will be executed with NodeJS.
Note: For this to automatically update a NodeJS app, nodemon, supervirsor or a module/script that watches for code changes, must be in place

Github and Bitbucket has a feature called webhooks(these are the only ones I've used, so I'm not sure if other project hosting service provides the same feature), which essentially, makes a POST with the commit's data to a URL specified. This way, the repository can communicate with a web server whenever the repo is pushed to. I'm going to focus on how to do it with github.

First of all, we need to set the webhook in github. From your repo's home, go to 'Settings' in the right side of the screen. The click on 'Webhooks & Services' on the left side menu. In here, click on 'Add Webhook', and in form, put the url(in my case http://luismatute.me/deploy/) you want github to send the POST request with the details of event you chose. There are a lots of events, but for what we need, POST will be enough.

After adding the URL to POST to in github, we need to process it on our server. I'm going to build a simple NodeJS-Express app to show how to do this. The implementation in another programming language, is somewhat alike. So here is my small server to show this:

var express = require('express'),  
    http = require('http'),
    app = express();

app.set('port', process.env.PORT || 8080);

app.post('/deploy/', function (req, res) {  
     var spawn = require('child_process').spawn,
        deploy = spawn('sh', [ './deploy.sh' ]);

    deploy.stdout.on('data', function (data) {
        console.log(''+data);
    });

    deploy.on('close', function (code) {
        console.log('Child process exited with code ' + code);
    });
    res.json(200, {message: 'Github Hook received!'})
});

http.createServer(app).listen(app.get('port'), function(){  
  console.log('Express server listening on port ' + app.get('port'));
});

What this server is doing is grabbing a POST request to '/deploy/', launches a new child_process with node's child_process module sending the command and arguments to execute (in other words...executes the deploy.sh), captures stdout to show messages comming from the shell script, shows a message confirming that the child process ended correctly and finally responds with a JSON saying that the hook was received.

Now, for the actual deployment, we run the deploy.sh file which looks like the following:

git reset --hard origin/master  
git clean -f  
git pull  
git checkout master  

A couple of things happen here. I actually do more than just a git pull just to make sure that the pull doesn't fail. First, do a reset to the local master branch to look exactly like remote origin master branch. Then, clean off any files that don't need to be there. The next step is to make the actual pull just to be doubly sure we have all the latest changes. Finally, the last line is to be triple sure that we have the lastest from our repo (this is actually not that necessary, but I'm a freak in making sure).

The deploy.sh file can be executed with any other programming language and will work fine. You only need to handle the POST request sent from github and execute this file.

Edit: Here is the link to the demo in github for reference: https://github.com/pipa/Webhook_Example

comments powered by Disqus