Tag-based Deployment with Capistrano

Mon, 31 Jul 2006 06:16:00 -0700
capistrano, deployment, rake, ruby

I've discovered that Capistrano is a useful deployment utility out-of-the-box if your needs are relatively simple. By simple, I mean that there is a single SCM that is accessible by all machines that you might need to do deployment to. This isn't always the case, especially when developing applications for large organizations. In such situations, you won't always get the access that you need to make deployment a straight-forward process. We've recently completed the deployment process for the project that I am currently working on, and it was one of those not-so-straight-forward scenarios.

Our source code is stored in an SVN repos that is seperate from the client's repos, and only used by the dev team (for CI and such). However, the client servers do not connect to our dev repos, and they want deployment to be possible from their own repos. To do this, we've developed a deployment policy, along with supporting logic in the deployment process, around making periodic deployments to the client SCM.

Basically, we tag the revision that we want to deploy on the dev repos, then do an export from the dev repos and import it to the client repos into a subdirectory that has the same name as the tagged version on the dev repos. We then communicate to the client that " ver X.Y.Z is now available for deployment". After that, either they or we can log into a machine on their network (which has access to their own SCM), do a checkout of the tagged deployment, and run the deployment process. The rest is your usual Capistrano stuff.

The first step is to add a Rake task to support the tag/build part of the process:

task :tag_build do
  local_repository = "svn+ssh://local/repos"
  remote_repository = "svn+ssh://remote/repos"

  build = TagBuild.new(local_repository, remote_repository)
  build.tag(ENV['tag'], ENV['rev'])
end

This allows the user to tag/build from the command line like this:

rake tag_build tag=x.y.z rev=123

In the above example, the "tag" argument is the tag name to apply to both the local and remote repositories. The "rev" argument is the revision number that you want to take from the local repository. The TagBuild class contains the logic to tag the local repository and then check that tagged version into the remote repository. I leave that code as an exercise for the reader.

The next step in this process is to add tag-based deployment capabilities to your Capistrano script. This first requires a modification of the "remote:deploy" Capistrano Rake task that passes a given tag to Capistrano. Here is an example of that modification (from lib/tasks/capistrano.rake):

task(:deploy) { cap :deploy, "-Stag=#{ENV['tag']}" }

This allows the user to invoke the deployment from the command-line like:

rake deploy tag=X.Y.Z

And last but not least, a modification to the deploy.rb file must be made to consider the new "tag" value.

  if symbol_defined? "tag"
    set :repository, "#{base_repository}/tags/#{tag}"
  else
    set :repository, "#{base_repository}/trunk"
  end