While working on my current project I use Bundler. There are many resources on the web which says how to use it in pair with Capistrano.
Most of them, however, fails when it comes to
- rollback Capistrano transaction as well as
- rollback Capistrano task.
Let’s say you used already a solution similar to this.
namespace :bundler do
task :create_symlink, :roles => :app do
shared_dir = File.join(shared_path, 'bundle')
release_dir = File.join(current_release, '.bundle')
run("mkdir -p #{shared_dir} && ln -s #{shared_dir} #{release_dir}")
end
task :bundle_new_release, :roles => :app do
bundler.create_symlink
run "cd #{release_path} && bundle install --without test"
end
end
- Rollback Capistrano transaction
Default deployment path includes transaction with update_code and symlink tasks.
task :update do
transaction do
update_code
symlink
end
end
If you hook bundle_new_release task before these tasks or within the transaction and do not provide bundler_install rollback action you will get into trouble.
If any of the tasks ran after bundle_new_release fail you will end up with bundle required / installed versions mismatch.
Why bother? Maybe update_code and symlink are not the most likely tasks to fail but you have probably already plenty hooks placed before and after them which may fail…
- Rollback Capistrano transaction
You can rollback last Capistrano deployment by simply running
cap deploy:rollback
Simple, huh?
Unfortunately, if you made any change to your bundle in just deployed commits it will not be reverted what will make your application fail to restart.
So now it is time for the solution.
namespace :bundler do
task :create_symlink, :roles => :app do
shared_dir = File.join(shared_path, 'bundle')
release_dir = File.join(release_path, '.bundle')
run("mkdir -p #{shared_dir} && ln -s #{shared_dir} #{release_dir}")
end
task :install, :roles => :app do
run "cd #{release_path} && bundle install"
on_rollback do
if previous_release
run "cd #{previous_release} && bundle install"
else
logger.important "no previous release to rollback to, rollback of bundler:install skipped"
end
end
end
task :bundle_new_release, :roles => :db do
bundler.create_symlink
bundler.install
end
end
after "deploy:rollback:revision", "bundler:install"
after "deploy:update_code", "bundler:bundle_new_release"
Here are some points to notice:
- it is useful to put bundle_new_release task within the transaction
- provide the code run on the transaction rollback
- provide the code run on the capistrano deploy:rollback task
As a hint I can also say that I find it useful to hook my BackgrounDRb restart task into transaction in the default deployment path.
In this way I can be sure that when the transaction is finished the bundle is installed correctly and the application boots up. It is important as I use <a href=http://unicorn.bogomips.org/’>Unicorn and restart it with sending USR2 signal which will make Unicorn to die silently in case of unbootable application…