This gem extends capistrano deployments to allow certain tasks to only be run under certain conditions -- i.e. conditionally.
Add to your Gemfile:
group :development do
gem 'capistrano-conditional', :require => false # <-- This is important!
end
And then modify your deploy.rb to include this at the top:
require "capistrano-conditional"
Your application must already be using capistrano for deployments, and (for now at least) you need to be using git.
capistrano-conditional
adds logic to be run before cap deploy
or cap deploy:migrations
that compares the local (to be deployed) code with the existing remote (currently deployed) code and lists all files that will be updated by the current deploy. It then checks the list of conditional statements that you've provided and runs any that you want run -- e.g. if you're using whenever and you only want to run the deploy:update_crontab
task if config/schedule.rb
has been changed, you'd add a block like this to your deploy.rb:
ConditionalDeploy.register :whenever, :watchlist => 'config/schedule.rb' do
after "deploy:symlink", "deploy:update_crontab"
end
This example registers a conditional named "whenever" (names aren't programmatically important, but they're used to report what's going to be run at the beginning of each deploy). The contents of the block will be run only if the list of changed files includes a path that matches config/schedule.rb
.
There are currently four logic conditions available (well, five, but :watchlist
is just an alias for :any_match
):
:any_match
=> file_list:none_match
=> file_list:if
=> Proc:unless
=> Proc
Where file_list is either a string or an array of strings which will be matched against the list of changed filenames from git (so :any_match => ['db/migrate']
would be true if ANY migration file was added, modified, or deleted).
:any_match
(aliased as :watchlist
) executes the block if ANY of the provided strings match ANY of file paths git reports changed.
:none_match
executes the block if NONE of the provided strings match ANY of file paths git reports changed.
If you need more custom control, :if
and :unless
expect a Proc (which will be passed the list of changed files, if one argument is expected, or the list of changes and the git object itself, if two arguments are expected and you really want to dive into things yourself).
ConditionalDeploy.register :whenever, :watchlist => 'config/schedule.rb' do
after "deploy:symlink", "deploy:update_crontab"
end
ConditionalDeploy.register :sphinx, :watchlist => ['db/schema.rb', 'db/migrate'] do
before "deploy:update_code", "thinking_sphinx:stop"
before "deploy:start", "thinking_sphinx:start"
before "deploy:restart", "thinking_sphinx:start"
end
ConditionalDeploy.register :jammit, :watchlist => ['public/images/embed', 'public/stylesheets', 'public/javascripts', 'public/assets', 'config/assets.yml'] do
after 'deploy:symlink', 'deploy:rebuild_assets'
end
# Restart the resque workers unless the only changes were to static assets, views, or controllers.
ConditionalDeploy.register(:resque, :unless => lambda { |changed| changed.all?{|f| f['public/'] || f['app/controllers/'] || f['app/views/'] } }) do
before "deploy:restart", "resque:workers:restart"
end
# ... note that you still have to actually DEFINE the tasks laid out above (e.g. deploy:update_crontab)
I've got cap deploy
in muscle memory, and I used to find myself forgetting to run cap deploy:migrations
until after I tested the new changes and found staging wasn't working right. I now add the following code to my apps, so I never have to worry about it again:
if ARGV.any?{|v| v['deploy:migrations']} # If running deploy:migrations
# If there weren't any changes to migrations or the schema file, then abort the deploy
ConditionalDeploy.register :unneeded_migrations, :none_match => ['db/schema.rb', 'db/migrate'] do
abort "You're running migrations, but it doesn't look like you need to!"
end
else # If NOT running deploy:migrations
# If there were changes to migration files, run migrations as part of the deployment
ConditionalDeploy.register :forgotten_migrations, :any_match => ['db/schema.rb', 'db/migrate'], :msg => "Forgot to run migrations? It's cool, we'll do it for you." do
after "deploy:update_code", "deploy:migrate"
end
end
Since I use it on every project, I've wrapped that logic up in a single command:
ConditionalDeploy.monitor_migrations(self)
By default capistrano-conditional
will abort the deployment if you have uncommited changes in your working directory. You can skip this check on an individual run by setting the ALLOW_UNCOMMITED environment variable (e.g. cap deploy ALLOW_UNCOMMITTED=1
).
If you need to force a particular conditional to run, you can also do that via the environment. Given the examples above, if you want to run the conditional named whenever
even though config/schedule.rb hasn't been changed, just run cap deploy RUN_WHENEVER=1
. Similarly, if you needed to skip the whenever
conditional which would otherwise be run, you can use cap deploy SKIP_WHENEVER=1
.
Copyright © 2011 Deviantech, Inc. and released under the MIT license.