The Passionate Craftsman

Ruby, PHP, MySql, Software engineering and more.

Wednesday, 20 January 2010

Rails deployment with Capistrano and SVN

I spent one day and half trying to deploy my first Rails application with Capistrano. Where I work, we use SVN as SCM. My PC runs Windows XP and the production server is Solaris 10. The server uses Apache, MySql and Passenger, aka mod_rails. I have used this recipe:


set :application, "apps"
set :user, 'my_username'
#default_run_options[:pty] = true
set :domain, "solaris10.ds.domain.com"
set :repository, "https://svn.domain.co.uk/svn/apps"
set :local_scm_command, "C:/applications/svn/svn"
set :scm_command, "/usr/local/bin/svn"
set :use_sudo, false
set :deploy_to, "/sites/rails-data/#{application}"
set :database_conf, "/sites/rails-data/database_conf"

role :app, domain
role :web, domain
role :db, domain, :primary => true

namespace :deploy do
task :start, :roles => :app do
run "touch #{current_release}/tmp/restart.txt"
end

task :stop, :roles => :app do
# Do nothing.
end

desc "Restart Application"
task :restart, :roles => :app do
run "touch #{current_release}/tmp/restart.txt"
end
end

desc "Echo environment vars"
namespace :env do
task :echo do
run "echo printing out cap info on remote server"
run "echo $PATH"
run "echo $LD_LIBRARY_PATH"
end
end

# optional task to reconfigure databases
after "deploy:update_code" , :configure_database
desc "copy database.yml into the current release path"
task :configure_database, :roles => :app do
db_config = "#{database_conf}/database_#{application}.yml"
run "cp #{db_config} #{release_path}/config/database.yml"
end


I have lost a huge amount of time trying to understand why Capistrano was not able to find the SVN client and some libraries. First tip, you must have an SVN client in your production server. When you run Capistrano, it connects to the production server with SSH. The main difference is that is an SSH session in non-interactive mode, so your user path is not loaded! Second tip, create a file in ~/.ssh name environment and add your PATH variable and your LD_LIBRARY_PATH, so the SVN client will be able to find the libraries that it needs. Third tip, do not use the export command in the environment file. Fourth tip, your sshd_config must have:

PermitUserEnvironment yes

Without this setting sshd will not load the environment file. In my Capistrano recipe there are three new tasks added to deploy, start, restart. The new two task are used to restart Passenger, so that tasks cannot be used if you have Mongrel. If you run 'cap env:echo' you will be able to see the PATH and LD_LIBRARY_PATH used by Capistrano during the SSH session.

If Capistrano is not able to set the PATH of the local and remote SVN clients you can use:

set :local_scm_command, "C:/applications/svn/svn" # set local svn client path
set :scm_command, "/usr/local/bin/svn" # set remote svn path

The standard path, for svn command, is typically /bin:/usr/bin:/usr/sbin.

You should not keep your database.yml in your SCM. I keep it in a directory and I tell to Capistrano to copy this file during the deployment, otherwise the migration will fail. Now what am I missing? Well a Continuos Integration (CI) solution. Yesterday I have evaluated CruiseControl.rb by ThoughtWorks. It seems very easy to install and use, but it still have not read the documentation completely and I still do not know if I can integrate CruiseControl.rb with Capistrano and if it is useful at all, but this will be another post.

After many releases, the release path will start to have many versions and if we want to clean the directory, this is the command to use:

cap deploy:cleanup

This tasks will leave only the last five releases. If you want to set the number of releases to keep in the production server;

set :keep_releases, 3 # where 3 is the number of releases

I have a directory of videos, named uploads, in Rails root directory. Every relaease will move your files with the old code. So I removed uploads directory from SVN (ignore list), then I configured an additional task in deploy.rb:

deploy.task :symlinks do
run "ln -nfs #{shared_path}/uploads #{release_path}/uploads"
end

This task create a symlink to the shared path of Capistrano. The shared path is there to be used by any release. With this technique you can keep your files outside your code, all you need is a symlink!

I see Capistrano as a very useful tool, very flexible and it solves most of my deployment needs. Everytime I think it is time to update the production environment, I commit all the modified files with SVN, than I run 'cap deploy', enter the password to access the server and... I have my fresh code updated. Of course before updating, all the tests should be executed successfully.

Links:
pretheory - Capistrano path and environment issues
Capistrano - From the Beginning
The Pragmatic Bookself - Agile Web Development with Rails, Third Edition

Labels: ,

2 Comments:

At 13 January 2011 at 16:09 , Anonymous Anonymous said...

where is shared_path defined?

 
At 13 January 2011 at 23:32 , Blogger Riccardo Tacconi said...

shared_data = "#{deploy_to}/shared", you don't need to define that dir, it should be pre-defined

 

Post a Comment

Subscribe to Post Comments [Atom]

<< Home