From 7432c710b05456d03f65a07934496bd44a0a0067 Mon Sep 17 00:00:00 2001 From: seenmyfate Date: Fri, 1 Nov 2013 11:59:13 +0000 Subject: [PATCH] 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 deploy` - to trigger after individual tasks use `set :deploying, true` This closes #708 and replaces https://github.com/capistrano/capistrano/pull/720 --- features/deploy_failure.feature | 17 +++++++++++++++++ features/step_definitions/assertions.rb | 15 +++++++++++++++ features/step_definitions/setup.rb | 10 ++++++++++ features/support/remote_command_helpers.rb | 4 ++++ lib/capistrano/application.rb | 8 ++++++++ lib/capistrano/dsl/task_enhancements.rb | 10 ++++++++++ lib/capistrano/i18n.rb | 1 + lib/capistrano/tasks/deploy.rake | 2 ++ lib/capistrano/tasks/framework.rake | 1 + spec/support/tasks/fail.cap | 7 +++++++ spec/support/tasks/failed.cap | 5 +++++ 11 files changed, 80 insertions(+) create mode 100644 features/deploy_failure.feature create mode 100644 spec/support/tasks/fail.cap create mode 100644 spec/support/tasks/failed.cap diff --git a/features/deploy_failure.feature b/features/deploy_failure.feature new file mode 100644 index 00000000..5c754399 --- /dev/null +++ b/features/deploy_failure.feature @@ -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 diff --git a/features/step_definitions/assertions.rb b/features/step_definitions/assertions.rb index 8b24040e..ddc34db8 100644 --- a/features/step_definitions/assertions.rb +++ b/features/step_definitions/assertions.rb @@ -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 diff --git a/features/step_definitions/setup.rb b/features/step_definitions/setup.rb index 7379deb4..16fabc2e 100644 --- a/features/step_definitions/setup.rb +++ b/features/step_definitions/setup.rb @@ -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 diff --git a/features/support/remote_command_helpers.rb b/features/support/remote_command_helpers.rb index 47b56ef8..7c045608 100644 --- a/features/support/remote_command_helpers.rb +++ b/features/support/remote_command_helpers.rb @@ -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) diff --git a/lib/capistrano/application.rb b/lib/capistrano/application.rb index 4f2a67bb..bf91f709 100644 --- a/lib/capistrano/application.rb +++ b/lib/capistrano/application.rb @@ -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 diff --git a/lib/capistrano/dsl/task_enhancements.rb b/lib/capistrano/dsl/task_enhancements.rb index 29090652..84f0a879 100644 --- a/lib/capistrano/dsl/task_enhancements.rb +++ b/lib/capistrano/dsl/task_enhancements.rb @@ -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 diff --git a/lib/capistrano/i18n.rb b/lib/capistrano/i18n.rb index e5dec4c5..cb69e0df 100644 --- a/lib/capistrano/i18n.rb +++ b/lib/capistrano/i18n.rb @@ -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' diff --git a/lib/capistrano/tasks/deploy.rake b/lib/capistrano/tasks/deploy.rake index 8312955e..25429bd6 100644 --- a/lib/capistrano/tasks/deploy.rake +++ b/lib/capistrano/tasks/deploy.rake @@ -200,4 +200,6 @@ namespace :deploy do end end + task :failed + end diff --git a/lib/capistrano/tasks/framework.rake b/lib/capistrano/tasks/framework.rake index 6ff02eef..e2d94052 100644 --- a/lib/capistrano/tasks/framework.rake +++ b/lib/capistrano/tasks/framework.rake @@ -57,6 +57,7 @@ end desc 'Deploy a new release.' task :deploy do + set(:deploying, true) %w{ starting started updating updated publishing published diff --git a/spec/support/tasks/fail.cap b/spec/support/tasks/fail.cap new file mode 100644 index 00000000..aec54a79 --- /dev/null +++ b/spec/support/tasks/fail.cap @@ -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 diff --git a/spec/support/tasks/failed.cap b/spec/support/tasks/failed.cap new file mode 100644 index 00000000..d1f6b58c --- /dev/null +++ b/spec/support/tasks/failed.cap @@ -0,0 +1,5 @@ +after 'deploy:failed', :failed do + on roles :all do + execute :touch, shared_path.join('failed') + end +end