Automated PHP Deployment With Capistrano

Sure, I'd like to spend my days and nights writing Ruby and _cap deploy_ ing it to success, but I have to face the facts. h2. I Still Have PHP Apps To Maintain. I have no one to blame but myself. I wrote them. And I still work where they were written. There's no escape. After using "Capistrano": more than one time, you'll be completely spoiled. But Capistrano is only for crazy Rails apps right? Nope. Its simply a platform for automation. It just so happens that deployment usually requires quite a bit of steps. Hmmm…lots of steps…automation… a match made in heaven. So sure, Capistrano was thought up with Rails in mind, but created openly so you're able to use it any way you'd like. Capistrano's power is in its remoting features. You tell it what to do on that _other server_, and it does it. Beauty. So, if your PHP deployment process requires an _svn export_ combined with a few other repetitive steps you have to do *_every_* time, Capistrano is here to help you too. Without going into all the details of "Capistrano": (following the link will give you a nice overview), it uses a deploy script (recipe) to understand your deployment environment and required tasks. h2. Ok ok ok, Lets Get On With It So, we have a PHP app that we need to deploy to server X. It needs to be sucked out of its repository, its version symlinked to a doc_root, and then a shared directory symlinked into the site. This is a pretty common deployment scenario, so lets go with this. First, we need a Capistrano recipe. Currently, it can only be auto generated for a Rails app, so psst, "here it is": Download this and create a directory at the root of your PHP app and name it _config_.
mkdir config
cd config
Capistrano wants its recipe to be in the _config_ directory, so do as it likes. Now lets open it up and make it work with us. Once again, a complete description of the Capistrano recipe file is bit out of scope for this little post, so please "read up": We'll edit all the normal items
set :application, "php_app"
set :repository, "{application}/trunk"
We only deploy to one server, so edit…
role :web, ""
Now you need to add the directory you want to deploy to. Add…
set :deploy_to, "/home/www/#{application}"
Capistrano defaults to _checkout_ when pulling from subversion. I prefer an export, if you do too, then add…
set :checkout, "export"
Ok, so if you're familiar with Capistrano, you know exactly what we did. If you're not, the most important thing to know is that Capistrano is going to suck your code from the repository defined, do everything it's told to do in _/home/www/php_app_ on your deployment server, h2. Set'r Up Believe it or not, we're half way done. Next, lets let Capistrano get the directory we just defined ready for its magic.
cap setup
If you look on your deployment server, you should see 2 directories that were just created.
drwxrwxr-x    2 user  group      4096 Aug 16 01:48 releases
drwxr-sr-x    4 user  group      4096 Aug 16 01:48 shared
WOW! We're already saving time! _releases_ is where your app will be sucked down to. Capistrano exports your PHP app into a directory named after the current date/time. Each version you deploy will be kept in this directory. Shared is where you can keep files like uploaded assets or images that aren't kept in your repository, but need to be around every time you export from subversion. If your users upload files all day long, and you redeploy your PHP app from subversion, all your uploaded files will be gone. So we keep those in here. Here's the deployment rub. Every time you deploy, you have to go symlink these directories from your /shared directory, into your current version of your PHP app. And a small bit of math can calculate the complexity when you start adding 2,3, or 6 asset folders. Yuck. That's where Capistrano helps ya out. h2. Capistrano, Take Me Away Ok, so our deploy directory is set up and ready to get our files. Now we need to tell Capistrano exactly what to do when we deploy. Get it? Recipe… Define a new task called _deploy_
desc "This will deploy the app"
task :deploy do

Inside the block we'll just tell Capistrano exactly what it needs to do in order to deploy our app.
desc "This will deploy the app"
task :deploy do
  run "svn --quiet #{checkout} #{repository} #{release_path}"
  run "ln -nfs #{release_path} #{current_path}"
  run "ln -nfs #{shared_path}/photos #{current_path}/photos"
Ok, lets walk through exactly what this does. First, Capistrano uses subversion to suck your app out of the repository and into the _releases_ directory. That _releases_path_ is just a nice convenient variable for _/home/www/php_site/releases/200608162012_. Next, it will create a _current_ symlink to that version of your app in the _releases_ directory. Then we told it to symlink the directory _photos_ from _shared_ into the current version of the application. And since you're hosting a PHP app with Apache (a 99% guess), there's no need to restart any servers. You're done. You're new version will be deployed safely and linked. The *IMPORTANT* thing to note, is that your new _doc_root_ for your site will be _/home/www/php_app/current_. h2. Deploy! Now, all ya gotta do to redeploy your site is…
cap deploy
Capistrano will go on to do everything you told it to do, on your deployment server. When its done, your directory should look like this…
lrwxrwxrwx   1 user group   42 2006-08-16 01:32 current -> /home/www/php_site_/releases/200608162012
drwxrwxr-x   4 user group 4096 2006-08-16 01:31 releases
drwxr-xr-x   4 user group 4096 2006-06-24 02:08 shared
And there you have it. Automated PHP deployment, with one simple command. Peering inside the current directory, you'll see where the photos directory has been symlinked into it. The moral is, Capistrano isn't just for Rails deployment. Its powerful remote execution can be exploited to do anything you want. While our Rails apps get a lot for free when it comes to recipes, creating your own isn't complicated. Especially if its replacing repetitive tasks you've done over and over and over and over and over again. You'll see the payoff the minute you get this set up. Especially on a Friday night at 5:30p when you're walking out the door. And its not just for Rails!