Deployment of PHP or Python Applications using Mercurial and Fabfile
Deployments are usually very painful. We generally write scripts to make it automated as much as possible. I wanted my method to be as easy as running single line on command prompt from anywhere in the world. I didn't want to worry about anything. At last with some experimentation I have found my way. This blog talks about the deployment of php/python applications using mercurial as code repo and fabfile. Well you actually can use any scripting format instead of fabfile. But fabile makes it easy to log into a remote machine and perform tasks. Also the scripting language of fabfile is python. This gives a lot of flexibility to customize and I dont have to learn anything new.
This process is inspired by Heroku git deployment feature. This tutorial works with hg, git and mostly with any other DVCS with minor alteration. It has two major steps
STEP 1: On your *nix Server
- Install mercurial on your server - it should be easy
- Setup SSH access to mercurial repository
Your server should be able to login to code repository and pull the latest code. Its easier to use SSH than passwords.
On your server machine:- Open terminal
- Enter
ssh-keygen
- Give a name or you can use the default name id_rsa
- When it asks "Enter passphrase (empty for no passphrase):" press enter. No Password
- Once the key generation is complete. You can verify the same using
ls -a ~/.ssh
- Add this new identity to SSH agent
ssh-add ~/.ssh/id_rsa
- Now we need to add this public key to bitbucket or any other provider
cat ~/.ssh/id_rsa.pub
. Copy the output - For bitbucket, go to account -> SSH keys, add the above output to your ssh keys
Now your server is set to access your repositories with out the need of password.
- Now we need to setup the Hg repository inside web accessible directory. For example, your web accessible folder could be
or
/home/user_home/public_html/var/www/html
or in case of phython it can be anywhere/home/user_home/my_project_code
- To deploy php application tweet4blood, clone the repo inside the directory using ssh url
cd /home/user_home/public_html
hg clone ssh://hg@bitbucket.org/thejeshgn/tweet4blood tweet4blood.com
- Make sure to make the .hg folder (actual repository) inaccessible to the webserver either by .htaccess rule or changing the permissions etc
STEP 2: On your desktop
- Install fabfile or fabric. On ubuntu search for fabric in Synaptic Package Manager
- Create your fabfile, you can find the latest version of below example fabfile in my snippets project
- To call any method in fabfile
$ cd /home/thej/my_deployment_scripts/tweet4blood/
$ fab hello
- Fabric can also chain the calls
$ cd /home/thej/my_deployment_scripts/tweet4blood/
$ fab test hello
Here it calls the test method first which sets the env variables and then calls hello - None of the env variables are necessary but providing env.user, env.hosts, env.password will avoid typing them everytime
- BTW env.user, env.hosts, env.password are that of SERVER machine
- To deploy the latest version to test
$ cd /home/thej/my_deployment_scripts/tweet4blood/
$ fab test deploy:tip
In this case,- test method sets the env variables corresponding to TEST env
- test method also sets application env specific consumer_key which later we will use to setup config.php, similarly you can use define databas_name, database_user_name etc
- then as per chain deploy method is called with input variable version whose value now is "tip"
- inside deploy the first call is cd (change directory) on remote server
- at this point fab logs into the remote server using env.user, env.hosts, env.password
- then control goes to the repo directory
- runs
hg pull
which gets everything from bitbucket - runs
hg update -C tip
which is clean update to "tip" version - then CDs into auth folder
- uses the Linux sid command to replace the env specific values in config.php
- To deploy any other version to test. I usually tag the versions, so I will pass the tag name
$ cd /home/thej/my_deployment_scripts/tweet4blood/
$ fab test deploy:v.0.1.0
- In case you want to shutdown the apache before the deployment and restart later, you can chain them too
$ cd /home/thej/my_deployment_scripts/tweet4blood/
$ fab test apache_stop deploy:tip apache_start
STEP 3: Go deploy
Below I have embedded a rough version of fabfile.py for quick refrence. But as I told you can find the latest version of the same in my snippets.
Questions and suggestions are welcome.
################################################################ # It uses fabfile to build and deploy # Ref: https://thejeshgn.com/2012/02/01/deployment-php-python-app-using-mercurial-and-fabfile # usage: # cd to the folder # $ fab hello # #To deploy tagged version to test: # cd to the folder which has fab file # $fab test deploy:REPO_TAG_NAME # #To deploy latest version to test: # cd to the folder which has fab file # $fab test deploy:tip # #To deploy to prod: # cd to the folder which has fab file # $fab prod deploy:REPO_TAG_NAME # # ################################################################ from __future__ import with_statement from fabric.api import * from fabric.contrib.console import confirm def hello(): print("Welcome to web deployment.") def test(): env.user = 'user_name' env.hosts = ['100.100.100.100'] env.password ='password' env.consumer_key ="test_xGxRxzXrxoxSxrxYxuxdxnxLxBxzQU00Q" def prod(): env.user = 'user_name' env.hosts = ['200.200.200.200'] env.password ='password' env.consumer_key ="prod_xGxRxzXrxoxSxrxYxuxdxnxLxBxzQU00Q" def update(version): with cd('/home/user_home/public_html/tweet4blood.com'): run('hg pull') run('hg update -C '+version) with cd('/home/user_home/public_html/tweet4blood.com/html/auth/'): consumer_key_defnition = "define('CONSUMER_KEY', '"+ +"');" run("sed -i 's/define('CONSUMER_KEY', 'xGxRxzXrxoxSxrxYxuxdxnxLxBxzQU00Q');/"+consumer_key_defnition+"/' config.php") def apache_restart(): sudo('/etc/init.d/apache2 restart') def apache_stop(): sudo('/etc/init.d/apache2 stop') def apache_start(): sudo('/etc/init.d/apache2 start') def apache_status(): sudo('ps aux | egrep "(PID|apache2)"') def deploy(version): update(version)
Actually, I use a rakefile and git to achieve pretty much the same for my blog. Deploying is a one-liner:
rake deploy:prod
on the source tree :)For some reason its always been
git and ruby
mercurial and python
together. I wonder.
I tend to use Makefiles for deployment. It’s usually (a) rsync to server (b) ssh some-command-at-server. Probably because my compilation process already uses make.