Leveraging config.gem in Capistrano's deploy:check

05 Feb 2010

Capistrano lets you enumerate your Rails application's dependencies so you can check them at deploy time. Mislav Marohnić did a good description of it a while back; here are some example depend entries:

depend :remote, :gem, "tzinfo", ">=0.3.3"
depend :local, :command, "svn"
depend :remote, :directory, "/u/depot/files"

The problem with depend :remote, :gem, though, is that it duplicates the config.gem entries that you already have in config/environment.rb. It'd be much nicer if you could just reuse those.

So, here;ss some code that you can paste in your deploy.rb to do just that:

# Add dependencies on gems listed in config/environment.rb
class Collecter
 attr_accessor :dependencies
 def initialize
  @dependencies = {}
  File.read(File.join("config", "environment.rb")).split("\n").select do |line|
    line.match(/^(\s)*config.gem/)
  end.each { |line| self.instance_eval(line) }
 end
 def gem(name, args)
  @dependencies[name] = args
  args[:version] = ">=0.0.1" unless args.include?(:version)
 end
 def config
  self
 end
end
Collecter.new.dependencies.each do |name, args|
 depend :remote, :gem, name, args[:version]
end
after "deploy:setup", "deploy:check

This parses your config/environment.rb, extracts the config.gem calls, and evaluates them in the context of an object that gathers up the dependency arguments. Then it declares an after hook for deploy:setup that runs deploy:check, so when you set up the application on a new server it'll ensure that the right stuff is in place.

Here's a sample run from my military reading list app:

$ cap deploy:setup
 * executing `deploy:setup'
 * executing "mkdir -p /var/www/militaryreadinglists.com/"
[blah blah blah]
 command finished
 triggering after callbacks for `deploy:setup'
 * executing `deploy:check'
 * executing "test -d /var/www/militaryreadinglists.com/releases"
 servers: ["militaryreadinglists.com"]
 [militaryreadinglists.com] executing command
 command finished
[blah blah blah]
 * executing "gem specification --version '>=0.0.1' mislav-will_paginate 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }'"
 servers: ["militaryreadinglists.com"]
 [militaryreadinglists.com] executing command
 command finished
You appear to have all necessary dependencies installed

Good times. The nice thing here is that the developer only has to list the dependencies in one place, and the hook ensures that failures are loudly proclaimed during initial setup.

I'm not sure how to integrate this into Capistrano itself; if Lee Hambley or one of the other Capistrano gurus sees this perhaps they can weigh in... thanks!