disclaimer: I am not underestimating the universe's ability to produce idiots, the point I'm trying to make is that I haven't managed to make any deploy mistakes using this approach. Yet.
Once upon a time, a long time ago, I went onto a conference stage for the very first time and said that I thought I might be the world's ditsiest PHP developer. I actually still think that is pretty true, and if you work with me then you will know that I mostly break and fix things in approximately equal measure. With this in mind, when I launched my own product recently (BiteStats, a thing to automatically email you a summary of your analytics stats every month), I knew that I would need a really robust way of deploying code. I've been doing a few different things for a few years, and I've often implemented these tools with or for other organisations, but I don't have much code in production in my own right, weirdly. I decided Phing was the way to go, got it installed, and worked out what to do next.
I created a file called
build.xml in the root of my project directory, and initially it contained only the following:
default attribute says which task to run if no task is specified, and the
property tag initialises a variable I'll need in a couple of places later on. In fact it's the build directory, a directory that will be used to hold all the artefacts of the build.
Tasks are defined in the build file, inside the project tag. I have two really simple ones which I think most projects have: build and clean. These create the build directory ready for me to use when I run other tasks, and remove it again. So with those included, my file now looks like this:
To run the tasks, you just do
phing prepare or
phing clean respectively.
Real Deploy Tasks
To run the deployment itself, I have the default task that you saw declared in the first snippet,
deploy. The task definition looks like this:
A quick recap of what actually happens here:
- We work out what number the next tag would be and tag the codebase. I use Mercurial for source control and host my code in a private repo on BitBucket
- We use
hg archive, which is basically like SVN export, to get the appropriate code version and then tar and zip it.
- The code is scp-ed to the server, by the apache user
- On the remote server, a few more commands get run all in one go. Firstly, the code is unpacked
- A symlink called "next" is created and pointed to the new folder (I blogged about my symlink strategy already if you are interested)
- A symlink is created to the library code, in this case ZF but you'd also create symlinks to config files, upload/cache directories, and anything else that's needed at this point too
- Any database patches needed are run at this point (again, I blogged this script before in case you want to see it)
- We switch the symlink so that the new content is live
- Finally, we stop and start the gearman worker so it picks up new code (one worker, all on the one server, there really isn't a lot of load on this site!). My gearman processes are overseen by supervisord, but it only answer to my user. It actually prompts me for my password at this point
Having a Deployment Process
The whole process takes a few seconds and so far it has worked like a charm. I'm really happy with it, and really happy I took the time to put this in place. Every time I deploy I feel a little bit smug :) I know it can be hard to get deployment stuff set up, after all we can do all the individual steps pretty easily and everyone has plenty of other stuff to worry about - but I think it's important and even though the "right" solution will be different for every application and platform, I wanted to share what works for me. What works for you? Leave me a comment!