split your cap tasks into different files
10 Sep 2012
I worked on a large project, it contains a lot of capistrano tasks, which makes it more and more difficult to maintain.
Before
After application grows, I saw too much tasks written in config/deploy.rb file, it was messy. e.g.
# config/deploy.rb
require 'capistrano_colors'
require 'bundler/capistrano'
set :user, 'huangzhi'
......
role :web, "app.example.com"
role :app, "app.example.com"
role :db, "db.example.com", :primary => true
after "deploy", "cron:update"
after "deploy", "sitemap:refresh"
set :assets_dependencies, %w(app/assets lib/assets vendor/assets Gemfile.lock config/routes.rb)
namespace :cron do
task :update do
update_app
update_db
end
task :update_app do
# update cron jobs on app servers
end
task :update_db do
# update cron jobs on db servers
end
end
namespace :sitemap do
task :refresh, :roles => :app do
# generate sitemap.xml for SEO
end
end
namespace :deploy do
namespace :assets do
task :precompile, :roles => :web, :except => { :no_release => true } do
# precompile asset only when asset changes
end
end
end
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
# restart app servers
end
end
After
Inspired by require external capistrano recipes (like bundler and rvm), I prefer splitting the tasks into different files according to the functionalities, like
# config/deploy/recipes/asset_pipeline.rb
set :assets_dependencies, %w(app/assets lib/assets vendor/assets Gemfile.lock config/routes.rb)
namespace :deploy do
namespace :assets do
task :precompile, :roles => :web, :except => { :no_release => true } do
# precompile asset only when asset changes
end
end
end
# config/deploy/recipes/cron.rb
after "deploy", "cron:update"
namespace :cron do
task :update do
update_app
update_db
end
task :update_app do
# update cron jobs on app servers
end
task :update_db do
# update cron jobs on db servers
end
end
# config/deploy/recipes/sitemap.rb
after "deploy", "sitemap:refresh"
namespace :sitemap do
task :refresh, :roles => :app do
# generate sitemap.xml for SEO
end
end
We have 3 capistrano recipes, asset_pipeline, cron and sitemap, each with one functionality, and then you can easily load them in config/deploy.rb.
# config/deploy.rb
require 'capistrano_colors'
require 'bundler/capistrano'
set :user, 'huangzhi'
......
role :web, "app.example.com"
role :app, "app.example.com"
role :db, "db.example.com", :primary => true
load 'config/deploy/recipes/asset_pipeline'
load 'config/deploy/recipes/cron'
load 'config/deploy/recipes/sitemap'
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
# restart app servers
end
end
config/deploy.rb looks pretty clean now.
Furthermore, you can autoload custom recipes by changing following code in Capfile
Dir['vendor/plugins/*/recipes/*.rb', 'config/deploy/recipes/*.rb'].each { |plugin| load(plugin) }
You can easily create your own capistrano recipes like this and reuse them in the next projects.
Thank chrishein to share the idea to autoload custom recipes.
Tags deployment capistrano