Bitbucket Auto Deploy

This is my solution how to automate a repeating task of doing a manual ssh, git pull kind of deployment. Basically I made a simple http server which will trigger a custom shell script.

These are the notes how to:

  1. Create a http server and execute a deployment script
  2. Configure Bitbucket webhooks

My Opinion

First thing to note, I'm not working on some super fancy projects with some CI, git flow, etc. No feature branches - everything is done on the master branch. However, what is important to me is to be able to deploy as easy as possible. That means when I have a working website on my local machine, I want to push it to the Bitbucket and have it online. Things will brake, but really, it isn't that big issue to make me go through trouble of using more complex systems.

Introduction

Butbucket has a webhooks which are triggered on the certain events - like commits. It sends a notification in a form of the http request to the predefined URL. So we need a running http server which will listen for the request and react.

Easiest way is a simple nodejs server. When receives a trigger (commit event) it will run a custom script. That custom script in my case is a shell script deploy.sh. From there I can do usual stuff I used to do manually.

Creating a HTTP server

I'm using a nodejs since it is easy to make a http server and trying to avoid any additional npm packages. Make a deploy folder somewhere (in home dir for example) and create the server.js file.

var http = require('http'),
    exec = require('child_process').exec,
    fs = require('fs'),
    LOG = __dirname + '/log',
    SCRIPT = 'sh ' + __dirname + '/deploy.sh';

fs.openSync(LOG, 'a');

http.createServer(function(req, res) {

    if (req.URL === '/deploy') {

        exec([SCRIPT, '>>', LOG, '2>&1'].join(' '));

        res.writeHead(200);
        return res.end('Okay');
    }

    if (req.URL === '/log') {
        res.writeHead(200);
        return fs.createReadStream(LOG).pipe(res);
    }

    res.writeHead(405);
    res.end('Unknown Method');

}).listen(43307);

You should configure firewall to allow requests only from the Bitbucket servers.

Test it with a command node server.js. And visit the URL on http://myserver.com:43307. If everything is fine you will see Unknown Method.

Deployment script

URL /deploy will execute deploy.sh script. There you should place your git pull and other stuff you usually do when deploying to server. For example:

cd ~/myapp
git pull
npm install

Output of the script is appended to the log file which is located in the same folder. Our server has a /log route which shows the content of the file.

Run server in the background

I used to use supervisord for controlling and watching processes but recently i heard about pm2 npm package and since it is a part of the node ecosystem it fits with this app.

pm2

After installing the package npm install -g pm2 start deployment server app that we previously built.

pm2 start server.js -n "Deployment Server"

To check a status type pm2 list.

Configuring Bitbucket

Setting up a deployment keys

In order to pull changes from the remote repository without entering the password, you need to change the remote from https to ssh, and add deployment keys in the Bitbucket settings.

I need to write down this process

Setting up a webhooks

Open your Bitbucket repository page and click on the settings page, then webhooks and then add webhook.

Bitbucket Webhooks

Enter the name of the webhook for example Test Server, enter the URL or IP with the port of the server. Here the port is 43307. For example it would be something like http://mywebsite.com:43307/deploy. Leave everything else as is and click save.

New webhook

That is all. Now next time when you push to remote, Bitbucket will notify your server about it which will execute a deployment script.

Here you can find an attempt to make adding webhooks and development keys with bitbucket easier.

Additional notes

Bitbucket Webhook request doesn't have a data about the branch name which could be used to make distinction and conditionally decide on the deploy method.

Request data has a repository name and it is possible to use that parameter to handle many webhooks from different repositories with a single server.

Author

I plan to write more articles about common laravel components. If you are interested let’s stay in touch.
comments powered by Disqus