Peter Marklund

Peter Marklund's Home

Mon Mar 26 2007 02:04:32 GMT+0000 (Coordinated Universal Time)

Capistrano is not just for deployment

I'm appreciating Capistrano more and more. It has such an elegant and powerful API, and you can use it for pretty much any commands you need to execute on one or more of your servers. Here is a simple example. For the development of this weblog I found myself quite often wanting to copy the production data to my development server. Of course at first I was doing this on the command line. The next natural step would have been to write a bash script, but these days I tend to write most of my scripts in Ruby. Rather than stick a small Ruby script under the Rails script directory I'm starting to create Rake tasks under lib/tasks though. One advantage of having scripts as Rake tasks is that you can cagalog them nicely with "rake --tasks". Here is the Rake task:

namespace :db do
  desc "Update the development database with a fresh dump of production"
  task :update_dev do
    host = "deploy@marklunds.com"
    db = "marklunds_production"
    system("ssh #{host} \"mysqldump -u root --set-charset #{db} > /tmp/#{db}.dmp\"")
    system("scp #{host}:/tmp/#{db}.dmp ~/tmp")
    system("mysql -u root marklunds_development > ~/tmp/#{db}.dmp")
  end
end

Looking at this code I realized it needed information in the Capistrano config/deploy.rb file, such as the domain of the production server. Also, what the task really does it execute some commands on a remote server, and that's exactly what Capistrano is built for. So I moved the task over to deploy.rb:

require File.dirname(__FILE__) + '/../config/environment'

...the usual deploy.rb stuff here...

desc "Copy production database to development database"
task :update_dev_db, :roles => :db do
  prod_db = ::ActiveRecord::Base.configurations['production']['database']
  dev_db = ::ActiveRecord::Base.configurations['development']['database']
  run <<-CMD
    mysqldump -u root --set-charset #{prod_db} > /tmp/#{prod_db}.dmp
  CMD
  system("scp #{user}@#{domain}:/tmp/#{prod_db}.dmp ~/tmp")
  system("mysql -u root #{dev_db} < ~/tmp/#{prod_db}.dmp")
end

It seems a bit unclean that I have to source the whole Rails environment from Capistrano and it increases the startup time a bit. The reason I do that is to get the database names from ActiveRecord instead of hard coding them. Notice the flexibility of Capistrano tasks - executing local commands is just as easy as executing remote ones.