We're hiring!

We're actively seeking designers and developers for all three of our locations.

Deploying from Git with Capistrano

Justin and I provide operational support to the SME Toolkit project, an education portal for small to medium sized enterprises in developing countries sponsored by the IFC (which is the private sector development branch of the World Bank Group).

Recently, the source code for the Rails-based web application was migrated from Subversion to Git. This also changed how we deploy the application. Previously, we deployed a snapshot of code from a tarball placed on a bastion server. With a few changes to our Capistrano configuration, we are able to deploy directly from the source code repository.

Basic Deployment

First we switch from

set :scm, :none
to
set :scm, :git

and from

set :repository, "/path/to/unzipped/snapshot"
to
set :repository, "git@server:project/repository.git"

Then we’ll also want to use set :deploy_via, :remote_cache so that we’ll only need to pull down the commits since the last deploy each time rather than cloning the repository and pulling down the entire history every time.

Now we can deploy using a command like:
cap stage_name deploy -S branch=master

Deploying a Specific Tagged Version

But what if we want to deploy a specific tagged version? We should be able to use cap stage_name deploy -S tag=3.2.1.

Unfortunately, it’s the ‘branch’ variable that’s used as a ref by Capistrano internally to resolve the revision deployed. This is not well documented, but see: lib/capistrano/recipes/deploy/scm/base.rb:77, and lib/capistrano/recipes/deploy/scm/git.rb:119, and lib/capistrano/recipes/deploy.rb:L29.

The simplest way to address this is just to set :branch, tag in config/deploy.rb.
In order to run other tasks without specifying a tag however, we should probably qualify that with a conditional: set :branch, tag if exists?(:tag).

Updating the Version Number

There’s one last thing to be updated: the version number. Before, the developers would populate the REVISION file with a version number when creating a release tarball. Now the REVISION file is populated by Capistrano, with the SHA of the commit being deployed.

If we substitute a new file, say BUILD_VERSION for REVISION in environment.rb, then we can again control of the version number displayed in various places throughout the application. Rather than a long seemingly meaningless string of letters and numbers, we’d like to include the date the code was deployed and the semantic version number of the code.

We can do this with a small Capistrano task like:

  task :build_version, :except => { :no_release => true } do
    deploy_date = Time.now.strftime('%F')
    build_version = "#{deploy_date} #{tag}.build+#{current_revision}"
    put(build_version,"#{current_release}/BUILD_VERSION")
  end

If we put this task in the deploy namespace, we can ensure that it’s run at the end of each deployment with something like: after "deploy:create_symlink", "deploy:build_version"

Now when we deploy, the date and the tag are prepended to the SHA for a much more meaningful build version.

Mike English (30 Posts)

Professional Problem Solver

This entry was posted in DevOps & System Admin. and tagged , , , , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

2 Comments

  1. Posted November 22, 2012 at 12:39 am

    Hi Carl,
    Thanks for the blog post about Git — It’s a reminder to us that we need to support “vanilla” git just alongside Github which most users seem to be happy to use these days. We launched a RailsRumble project about a month ago called DeployButton, that’s getting a fair number of active users deploying using Capistrano every day.

    If you end up using it, please drop us a line and give us your feedback! :)

    Thanks,
    Ash


    DeployButton.com makes continuous delivery easy with a single button.

  2. Posted December 19, 2012 at 11:52 am

    Very nice! Though on deployment I get this error:

    sftp upload # -> ~/rails/releases/20121219163352/BUILD_VERSION
    [my.server.com] ~/rails/releases/20121219163352/BUILD_VERSION
    *** upload via sftp failed on my.server.com: Net::SFTP::StatusException (Net::SFTP::StatusException open ~/rails/releases/20121219163352/BUILD_VERSION (2, “no such file”))

    Any idea as how to solve this?