Rails, Capistrano, and Ruby extension dependencies

26 Mar 2008

Sometimes if I hose up a Ruby install it won't have zlib or openssl or readline compiled in, which produces problems later when I try to get a Rails app running on there. But with a little code we can get Capistrano to check for Ruby extensions (or, really, Ruby libraries in general) via the deploy:check task.

Here's the new RemoteDependency implementation; you can just put this in your deploy.rb:

module ::Capistrano
  module Deploy
    class RemoteDependency
      def ruby_extension(extension_name, options={})
        output_per_server = {}
        try("ruby -r#{extension_name} -e ''", options) do |ch, stream, out|
          output_per_server[ch[:server]] ||= ''
          output_per_server[ch[:server]] += out
        end
        @success = true
        errored_hosts = []
        output_per_server.each_pair do |server, output|
          next if output.empty?
          errored_hosts << server
        end
        if errored_hosts.any?
          @hosts = errored_hosts.join(', ')
          output = output_per_server[errored_hosts.first]
          @message = "The Ruby interpreter was unable to load the extension #{extension_name}"
          @success = false
        end
        self
      end
    end
  end
end

Here's how to declare instances of this new dependency type; these go in deploy.rb also. Note that we're only checking for these dependencies on the app server; you may not even have Ruby installed on your web servers:

depend :remote, :ruby_extension, :zlib, :roles => :app
depend :remote, :ruby_extension, :openssl, :roles => :app
depend :remote, :ruby_extension, :readline, :roles => :app

And here's a sample run:

$ cap deploy:check
    triggering start callbacks for `deploy:check'
  * executing `multistage:ensure'
*** Defaulting to `production'
  * executing `production'
  * executing `deploy:check'
  * executing "test -d /var/www/myapp/releases"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "test -w /var/www/myapp"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "test -w /var/www/myapp/releases"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "which svn"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "which gem"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "ruby -rzlib -e ''"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "ruby -ropenssl -e ''"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
  * executing "ruby -rreadline -e ''"
    servers: ["myserver.com"]
    [myserver.com] executing command
    command finished
You appear to have all necessary dependencies installed

Note the boilerplate deploy:check commands up front followed by the custom checks for the libraries. As always, many thanks to Jamis Buck for Capistrano!