Add hook for deploy failure

If an error is raised during a deploy, the task `deploy:failed` will be
triggered.  Custom tasks can hook into this using `after`:

    after 'deploy:failed', :send_for_help do
      #
    end

I've also taken the opportunity to provide a marginally more useful
error message before triggering the task.

By default, this 'deploy:failed' will only be triggered when running
`cap <stage> deploy` - to trigger after individual tasks use `set
:deploying, true`

This closes #708 and replaces
https://github.com/capistrano/capistrano/pull/720
This commit is contained in:
seenmyfate 2013-11-01 11:59:13 +00:00
parent 3ace303dd9
commit 7432c710b0
11 changed files with 80 additions and 0 deletions

View File

@ -0,0 +1,17 @@
Feature: Deploy failure
Background:
Given a test app with the default configuration
And a custom task that will simulate a failure
And a custom task to run in the event of a failure
And servers with the roles app and web
Scenario: Triggering the custom task
When I run cap "deploy:starting"
But an error is raised
Then the failure task will not run
Scenario: Triggering the custom task
When I run cap "deploy"
But an error is raised
Then the failure task will run

View File

@ -92,3 +92,18 @@ end
Then(/^the task is successful$/) do
expect(@success).to be_true
end
Then(/^the failure task will run$/) do
failed = TestApp.shared_path.join('failed')
run_vagrant_command(test_file_exists(failed))
end
Then(/^the failure task will not run$/) do
failed = TestApp.shared_path.join('failed')
!run_vagrant_command(test_file_exists(failed))
end
When(/^an error is raised$/) do
error = TestApp.shared_path.join('fail')
run_vagrant_command(test_file_exists(error))
end

View File

@ -26,3 +26,13 @@ end
Given(/^the configuration is in a custom location$/) do
TestApp.move_configuration_to_custom_location('app')
end
Given(/^a custom task that will simulate a failure$/) do
safely_remove_file(TestApp.shared_path.join('failed'))
TestApp.copy_task_to_test_app('spec/support/tasks/fail.cap')
end
Given(/^a custom task to run in the event of a failure$/) do
safely_remove_file(TestApp.shared_path.join('failed'))
TestApp.copy_task_to_test_app('spec/support/tasks/failed.cap')
end

View File

@ -15,6 +15,10 @@ module RemoteCommandHelpers
def exists?(type, path)
%{[ -#{type} "#{path}" ] && echo "#{path} exists." || echo "Error: #{path} does not exist."}
end
def safely_remove_file(path)
run_vagrant_command("rm #{test_file}") rescue Vagrant::Errors::VagrantError
end
end
World(RemoteCommandHelpers)

View File

@ -30,6 +30,14 @@ module Capistrano
end
end
def exit_because_of_exception(ex)
if deploying?
exit_deploy_because_of_exception(ex)
else
super
end
end
private
# allows the `cap install` task to load without a capfile

View File

@ -49,5 +49,15 @@ module Capistrano
%w{install}
end
def exit_deploy_because_of_exception(ex)
warn t(:deploy_failed, ex: ex.inspect)
invoke 'deploy:failed'
exit(false)
end
def deploying?
fetch(:deploying, false)
end
end
end

View File

@ -18,6 +18,7 @@ en = {
mirror_exists: "The repository mirror is at %{at}",
revision_log_message: 'Branch %{branch} deployed as release %{release} by %{user}',
rollback_log_message: '%{user} rolled back to release %{release}',
deploy_failed: 'The deploy has failed with an error: %{ex}',
console: {
welcome: 'capistrano console - enter command to execute on %{stage}',
bye: 'bye'

View File

@ -200,4 +200,6 @@ namespace :deploy do
end
end
task :failed
end

View File

@ -57,6 +57,7 @@ end
desc 'Deploy a new release.'
task :deploy do
set(:deploying, true)
%w{ starting started
updating updated
publishing published

View File

@ -0,0 +1,7 @@
set :fail, proc { fail }
before 'deploy:starting', :fail do
on roles :all do
execute :touch, shared_path.join('fail')
end
fetch(:fail)
end

View File

@ -0,0 +1,5 @@
after 'deploy:failed', :failed do
on roles :all do
execute :touch, shared_path.join('failed')
end
end