Capistrano* is a lightweight web application deployment tool suited to all codebases. It's written in ruby and hooks into the rake gem, but you don't need to be deploying a Rails app for it to come in useful. Ruby only needs to be installed on the box doing the deploying. Check out the other assumptions it makes. It's very practical and i highly recommend it for web shops with a cluster of web/db servers.

Having come out of the rails community, it sticks to some of the design principles of said project, including 'convention over configuration'. However, the creator's conventions aren't neccessarily your team's conventions - there was one core convention that didn't hold true in my deployment scenario: Capistrano deploys the latest revision of your repo and uses timestamps to identify the release on the participating servers. However, we use subversion and we deploy 'tags'.

Tags are named snapshots of your repository (more on tags). We typically use version numbers as tags, e.g. 'v1.2.2' or some such scheme. This allows us to say with certainty what is deployed at any one time, without having to scan logs etc. Tags give context to a release name (you can easily associate the release with project milestones) that revision numbers and timestamps do not. Tags are usually kept in a 'tags' subfolder, in the top level of the repository.

I've produced a small task library which allows you to deploy and rollback subversion tags with this kind of syntax:

rake remote:deploy_tag [tagname]
  • Your 'releases' folder will hold your releases by tag name, not timestamp.
  • The library adds a capistrano variable, 'svn_tag_dir' which defaults to 'tags', to describe your tag subdirectory.
  • You can customize the deploy_tag script for your particular deploy scenario.

To install just place the task library in 'lib/tasks' in your particular rails app, and add the following to your 'config/deploy.rb' script:

require 'tasks/capistrano_svn_tags'

You can then use the new deploy_tag task. Assumptions made:


  • Your :repository variable will now have to point to the top level of your application's repository.

  • Your tag names follow an enumerable convention like v1, v2, v3 (ie sortable by Array#sort)

If you want to use the default capistrano deployment convention again (last revision, timestamps) then just comment out the require statement and use the default deployment tasks.

Download the task library:

* Wins my award for Most Emo Sounding Dev Tool of All Time.

Great! Automating ubiquitously is the new pink. Once we realize that software development differs fundamentally from an engineering discipline we can move past the stigma and embrace the new age of development with dsls, soa, reuse, and relentless automation. 'You hear that Mr. Anderson? That is the sound of inevitability'

mathie says

Ooh, this looks like it could be really useful for some of my deployment scenarios. Thanks! I'll check it out properly tonight and let you know how it works out.

Ziemek says

Thanks for the post! It's very useful, but I am having trouble running all the command via rake. When I include the file you posted into the deploy.rb, I am able to run the commands via cap, but not rake. The error I get is "don't know how to build task 'remote:deploy_tag'". I realize that there are no rappers inside the rake file for the custom task you built for capsitrano, but your post does not go into added them, therefore I think that I am doing something wrong. Keep in mind that I am fairly new to ruby and rails, so I might be something dead obvious. Thanks for the post again and I would be very greatful if you could point me in the right direction.

Sava Chankov says

It works, but you didn't mention to wrap tasks in capistrano.rake (that has confused Ziemek):

task(:deploy_tag) { cap :deploy_tag }
task(:update_tag) { cap :update_tag }

Running rake as you suggest (

rake remote:deploy_tag tagname
) will always result in aborting after doing actual deploy, because it assumes all commandline arguments to be task names. If you apply this patch:
<     set :release, ARGV[1] if ARGV
---
> set :release, ENV['TAG'] if ENV['TAG']

and run rake as
rake remote:deploy_tag TAG=tagname
it won't complain.

James says

Um... so using your example, what happens when you get to version 10?

["v1", "v2", "v8", "v9", "v10"].sort
=> ["v1", "v10", "v2", "v8", "v9"]

?

Seigel says

I guess you'd have to use a numbering scheme that is sortable.