Simple deploy script for PHP Applications

Here at RJMetrics, we roll out features and fixes to our code multiple times per day. As our organization has grown the complexity of coordinating and executing these releases has increased exponentially, and we have been spending a lot of time lately solidifying and standardizing our deployment process.

Coming off developing some personal projects in Ruby on Rails, the Capistrano deployment system was fresh in my mind, and had left a favorable impression on me for its versatility, ease of use, and lack of verbose XML configuration files. Although it’s written in Ruby, Capistrano is general enough to deploy just about anything – and in fact there are several guides that detail how to use it with PHP. So, I plodded ahead with it – and spent a few hours installing the Capistrano Multistage extension (to support both testing and production environments) and writing my own recipes for several of our PHP projects.

And, for all of my hard work, I continually ran into small gotchas and errors that would crop up when I switched between my OS X and Ubuntu development environments. After a day of tweaking, I couldn’t get Capistrano to act consistently, and, the more I stared at my recipes, the more I realized that I wasn’t really leveraging any of Capistrano’s power, but rather going out of my way to disable its most useful Rails-specific features. At the end of the day I was just using Capistrano and the Net::SSH Ruby library as a conduit for sending commands to remote machines – something that can be more easily accomplished via ssh.

The next morning I sat down and wrote the following bash scripts to replicate the Capistrano features that we need to deploy PHP projects from our git repositories here at RJMetrics, all in about 50 lines of code.

[deploy.sh]
#!/bin/bash ################################################# # General deploy script. Depends on # environment-specific script in deploy/ dir ################################################# set -e APP_NAME=RJM_Web GIT_ROOT=localhost:/repo GIT_USER=chris APP_REPO=$GIT_ROOT/$APP_NAME.git # Detect exactly 1 argument if (($# == 1)); then # Include .sh from the deploy folder DEPLOY_ENV=$1 DEPLOY_FILE=deploy/$DEPLOY_ENV.sh if [ -f $DEPLOY_FILE ]; then source $DEPLOY_FILE else echo "Could not find deploy file for $DEPLOY_ENV environment, it should be located in $DEPLOY_FILE" exit 1 fi echo "Deploying $APP_NAME to $DEPLOY_ENV environment." else echo "Usage: deploy.sh <environment-name>" exit 1 fi CURRENT_DIR=$DEPLOY_PATH/$APP_NAME/current RELEASE_NAME=`date +"%Y-%m-%d-%H%M%S"` CURRENT_RELEASE=$DEPLOY_PATH/$APP_NAME/releases/$RELEASE_NAME # From local machine, get hash of the head of the desired branch # Required to checkout the branch - is there a better way to do this? APP_HASH=`git ls-remote $APP_REPO $BRANCH | awk -F "\t" '{print $1}'` for SERVER in ${DEPLOY_SERVER[@]} do echo "Deploying on $SERVER" ssh -t $DEPLOY_USER@$SERVER "cd $DEPLOY_PATH/$APP_NAME/releases && git clone -q $GIT_USER@$APP_REPO $RELEASE_NAME && cd $RELEASE_NAME && git checkout -q -b deploy $APP_HASH && ln -nfs $CURRENT_RELEASE $CURRENT_DIR" done echo "Finished successfully"
[deploy/dev.sh]
################################################# # Environment-specific script # sourced by deploy.sh ################################################# BRANCH=develop DEPLOY_SERVER=(192.168.20.100 192.168.20.101 192.168.20.102) DEPLOY_PATH=/home/deploy DEPLOY_USER=deploy

The first snippet is the main deploy.sh script, which by default reads in a second, environment-specific script, like the one seen in the second snippet, from the deploy directory. So, for example, if you call

> ./deploy.sh dev

then the script will look for a second file named dev.sh in the deploy directory – analogous to the way the Capistrano Multistage extension works. Some of the conventions we use internally here at RJMetrics are implied in these scripts – e.g. we always name our git repository $APP_NAME.git, and we always deploy to a folder named $APP_NAME – these are, of course, customizable.

So there you have it – a simple, effective way to deploy PHP projects, without all of the overhead and Rails-specific functionality of Capistrano. Some features that I plan to add are roll-back functionality, and the ability to define a remote cache for each deployment server to speed up the git clone. Stay tuned.

4 Comments

  1. Posted February 1, 2010 at 10:32 am | Permalink

    Thanks, Chris! Great first post and welcome to the team!

  2. Jeremy Luebke
    Posted February 1, 2010 at 3:31 pm | Permalink

    The one glaring hole is the rollback. Or am I missing it? I can’t tell you how many times a rollback has saved me 🙂

  3. cmerrick
    Posted February 1, 2010 at 3:35 pm | Permalink

    Jeremy: Agreed – I suppose I shouldn’t wait until I need it to write it!

  4. Posted February 1, 2010 at 3:47 pm | Permalink

    I look forward to seeing your follow up!


Post a Comment

Required fields are marked *
*
*

%d bloggers like this: