Skip to content

Capistrano and CVS

In the world of Rails, Capistrano is a great tool to automate deployments. I started using it several weeks ago and it makes rolling out new versions of your web app very easy. Installation was as easy as doing ‘gem install capistrano’ and then doing a ‘cap -A’ in my application directory.

The only glitch I came up against was that my production server doesn’t have remote access to my cvs repository. By default, Capistrano works with svn by remotely downloading the application from the repository to the production server. There is a cvs module for Capistrano, but it still requires the production server to have access to the cvs repo. I dug around a bit on the rails mailing list and found this post which not only clued me into the fact that any of the standard deploy targets can be overridden in my deploy.rb file, but also gave me the code to override the update_code task to do exactly what I wanted.

The following makes a local tarball of the application code, uploads the tarball to the production server and untars it into the release directory. The rest of the deployment is the same as normal. Works like a charm. Thanks to Jim Morris for the above posting. I tweaked the target for my own environment, but the credit mostly goes to him.


desc <<DESC
Update all servers with the latest release of the source code.
This is a modified version that copies a local copy to the remote site
DESC

task :update_code, :roles => [:app, :db, :web] do
    on_rollback { delete release_path, :recursive => true }

    # puts "doing my update_code"
    temp_dest="tmp_code"
    tar_file="#{application}_update.tar.gz"

    #puts "...get a local copy of the code into #{temp_dest} from local svn"
    # but this could also just be your local development folder
    system("mkdir #{temp_dest}")
    system("cd #{temp_dest} && cvs export -d #{temp_dest} -r HEAD #{application} 
                && cd -")

    #puts "...tar the folder"
    # you could exclude files here that you don't want on your production server
    system("tar -C #{temp_dest}/* -c -z -f #{tar_file} .")

    #puts "...Sending tar file to remote server"
    put(File.read("#{tar_file}"), "#{tar_file}")

    #puts "...detar code on server"
    run <<-CMD
        mkdir -p #{release_path} &&
        tar -C #{release_path} -x -z -f #{tar_file} &&
        rm -rf #{tar_file} &&
        rm -rf #{release_path}/log #{release_path}/public/system &&
        ln -nfs #{shared_path}/log #{release_path}/log &&
        ln -nfs #{shared_path}/system #{release_path}/public/system
    CMD

    #puts "...cleanup"
    system("rm -rf #{temp_dest} #{tar_file}")
end