diff --git a/CHANGELOG.md b/CHANGELOG.md index 25eb0afb..c90dd011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,20 @@ Reverse Chronological Order: +## `3.1.0` (not released) + + * `deploy:restart` task is no longer run by default. + From this version, developers who restart the app on each deploy need to declare it in their deploy flow (@kirs) + * Fixed bug when `deploy:cleanup` was executed twice by default (@kirs) + * Config location can now be changed with `deploy_config_path` and `stage_config_path` options (@seenmyfate) + * `no_release` option is now available (@seenmyfate) + * Raise an error if developer tries to define `:all` role, which is reserved (@kirs) + * `deploy:fallback` hook was added to add some custom behaviour on failed deploy (@seenmyfate) + * Correctly infer namespace in task enhancements (@seenmyfate) + ## `3.0.1` - * capify' not listed as executable (@leehambley) + * `capify` not listed as executable (@leehambley) * Confirm license as MIT (@leehambley) * Move the git ssh helper to application path (@mpapis) diff --git a/README.md b/README.md index cb683456..e21a8c0b 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ This method is widely used. ``` ruby desc "Ask about breakfast" task :breakfast do - breakfast = ask(:breakfast, "What would you like your colleagues to get you for breakfast?") + breakfast = ask(:breakfast, "What would you like your colleagues to bring you for breakfast?") on roles(:all) do |h| execute "echo \"$(whoami) wants #{breakfast} for breakfast!\" | wall" end diff --git a/features/configuration.feature b/features/configuration.feature new file mode 100644 index 00000000..31dd59da --- /dev/null +++ b/features/configuration.feature @@ -0,0 +1,15 @@ +Feature: The path to the configuration can be changed, removing the need to + follow Ruby/Rails conventions + + Background: + Given a test app with the default configuration + And servers with the roles app and web + + Scenario: Deploying with configuration in default location + When I run "cap test" + Then the task is successful + + Scenario: Deploying with configuration in a custom location + But the configuration is in a custom location + When I run "cap test" + Then the task is successful diff --git a/features/deploy.feature b/features/deploy.feature index 5342b3bc..a97a53b0 100644 --- a/features/deploy.feature +++ b/features/deploy.feature @@ -34,6 +34,7 @@ Feature: Deploy Then the task will be successful Scenario: Creating a release + Given I run cap "deploy:check:directories" When I run cap "git:create_release" as part of a release Then the repo is cloned And the release is created 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 efa002cd..ddc34db8 100644 --- a/features/step_definitions/assertions.rb +++ b/features/step_definitions/assertions.rb @@ -88,3 +88,22 @@ end Then(/^it will not recreate the file$/) do # 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/cap_commands.rb b/features/step_definitions/cap_commands.rb index f5b5c14d..401e25f2 100644 --- a/features/step_definitions/cap_commands.rb +++ b/features/step_definitions/cap_commands.rb @@ -1,8 +1,12 @@ When(/^I run cap "(.*?)"$/) do |task| - TestApp.cap(task) + @success = TestApp.cap(task) end When(/^I run cap "(.*?)" as part of a release$/) do |task| TestApp.cap("deploy:new_release_path #{task}") end +When(/^I run "(.*?)"$/) do |command| + @success = TestApp.run(command) +end + diff --git a/features/step_definitions/setup.rb b/features/step_definitions/setup.rb index 4d16620a..16fabc2e 100644 --- a/features/step_definitions/setup.rb +++ b/features/step_definitions/setup.rb @@ -23,3 +23,16 @@ Given(/^a custom task to generate a file$/) do TestApp.copy_task_to_test_app('spec/support/tasks/database.cap') 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/configuration.rb b/lib/capistrano/configuration.rb index 40637737..979cff3c 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -34,6 +34,10 @@ module Capistrano end def role(name, hosts, options={}) + if name == :all + raise ArgumentError.new("#{name} reserved name for role. Please choose another name") + end + servers.add_role(name, hosts, options) end diff --git a/lib/capistrano/configuration/server.rb b/lib/capistrano/configuration/server.rb index b81402db..1f151aa7 100644 --- a/lib/capistrano/configuration/server.rb +++ b/lib/capistrano/configuration/server.rb @@ -23,7 +23,7 @@ module Capistrano end def select?(options) - selector = Selector.new(options) + selector = Selector.for(options) selector.call(self) end @@ -103,6 +103,14 @@ module Capistrano @options = options end + def self.for(options) + if options.has_key?(:exclude) + Exclusive + else + self + end.new(options) + end + def callable if key.respond_to?(:call) key @@ -126,6 +134,17 @@ module Capistrano ->(server) { :all } end + class Exclusive < Selector + + def key + options[:exclude] + end + + def call(server) + !callable.call(server) + end + end + end end diff --git a/lib/capistrano/dsl/env.rb b/lib/capistrano/dsl/env.rb index a7bb858f..e6641344 100644 --- a/lib/capistrano/dsl/env.rb +++ b/lib/capistrano/dsl/env.rb @@ -43,6 +43,16 @@ module Capistrano env.roles_for(names) end + def release_roles(*names) + options = { exclude: :no_release } + if names.last.is_a? Hash + names.last.merge(options) + else + names << options + end + roles(*names) + end + def primary(role) env.primary(role) end diff --git a/lib/capistrano/dsl/paths.rb b/lib/capistrano/dsl/paths.rb index 020b2c7b..17e0a044 100644 --- a/lib/capistrano/dsl/paths.rb +++ b/lib/capistrano/dsl/paths.rb @@ -27,6 +27,14 @@ module Capistrano set(:release_path, releases_path.join(timestamp)) end + def stage_config_path + Pathname.new fetch(:stage_config_path, 'config/deploy') + end + + def deploy_config_path + Pathname.new fetch(:deploy_config_path, 'config/deploy.rb') + end + def repo_url require 'cgi' require 'uri' diff --git a/lib/capistrano/dsl/stages.rb b/lib/capistrano/dsl/stages.rb index 283421b0..3455a87a 100644 --- a/lib/capistrano/dsl/stages.rb +++ b/lib/capistrano/dsl/stages.rb @@ -3,7 +3,14 @@ module Capistrano module Stages def stages - Dir['config/deploy/*.rb'].map { |f| File.basename(f, '.rb') } + Dir[stage_definitions].map { |f| File.basename(f, '.rb') } + end + + def infer_stages_from_stage_files + end + + def stage_definitions + stage_config_path.join('*.rb') end def stage_set? diff --git a/lib/capistrano/dsl/task_enhancements.rb b/lib/capistrano/dsl/task_enhancements.rb index 29090652..c35caaba 100644 --- a/lib/capistrano/dsl/task_enhancements.rb +++ b/lib/capistrano/dsl/task_enhancements.rb @@ -6,9 +6,10 @@ module Capistrano end def after(task, post_task, *args, &block) - post_task = Rake::Task.define_task(post_task, *args, &block) if block_given? + Rake::Task.define_task(post_task, *args, &block) if block_given? + post_task = Rake::Task[post_task] Rake::Task[task].enhance do - invoke(post_task) + post_task.invoke end end @@ -49,5 +50,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 781ea2d9..cb69e0df 100644 --- a/lib/capistrano/i18n.rb +++ b/lib/capistrano/i18n.rb @@ -7,7 +7,6 @@ en = { start: 'Start', update: 'Update', finalize: 'Finalise', - restart: 'Restart', finishing: 'Finishing', finished: 'Finished', stage_not_set: 'Stage not set, please call something such as `cap production deploy`, where production is a stage you have defined.', @@ -19,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/setup.rb b/lib/capistrano/setup.rb index f08b470d..53493bbe 100644 --- a/lib/capistrano/setup.rb +++ b/lib/capistrano/setup.rb @@ -8,11 +8,12 @@ end stages.each do |stage| Rake::Task.define_task(stage) do - invoke 'load:defaults' - load 'config/deploy.rb' - load "config/deploy/#{stage}.rb" - load "capistrano/#{fetch(:scm)}.rb" set(:stage, stage.to_sym) + + invoke 'load:defaults' + load deploy_config_path + load stage_config_path.join("#{stage}.rb") + load "capistrano/#{fetch(:scm)}.rb" I18n.locale = fetch(:locale, :en) configure_backend end diff --git a/lib/capistrano/tasks/deploy.rake b/lib/capistrano/tasks/deploy.rake index 101e5662..eb1d8135 100644 --- a/lib/capistrano/tasks/deploy.rake +++ b/lib/capistrano/tasks/deploy.rake @@ -15,7 +15,6 @@ namespace :deploy do task :publishing do invoke 'deploy:symlink:release' - invoke 'deploy:restart' end task :finishing do @@ -42,7 +41,7 @@ namespace :deploy do namespace :check do desc 'Check shared and release directories exist' task :directories do - on roles :all do + on release_roles :all do execute :mkdir, '-pv', shared_path, releases_path end end @@ -80,7 +79,7 @@ namespace :deploy do namespace :symlink do desc 'Symlink release to current' task :release do - on roles :all do + on release_roles :all do execute :rm, '-rf', current_path execute :ln, '-s', release_path, current_path end @@ -133,7 +132,7 @@ namespace :deploy do desc 'Clean up old releases' task :cleanup do - on roles :all do |host| + on release_roles :all do |host| releases = capture(:ls, '-x', releases_path).split if releases.count >= fetch(:keep_releases) info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: releases.count) @@ -201,4 +200,7 @@ namespace :deploy do end end + task :restart + 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/lib/capistrano/tasks/git.rake b/lib/capistrano/tasks/git.rake index edf9037b..6e888029 100644 --- a/lib/capistrano/tasks/git.rake +++ b/lib/capistrano/tasks/git.rake @@ -9,7 +9,7 @@ namespace :git do desc 'Upload the git wrapper script, this script guarantees that we can script git without getting an interactive prompt' task :wrapper do - on roles :all do + on release_roles :all do execute :mkdir, "-p", "#{fetch(:tmp_dir)}/#{fetch(:application)}/" upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), "#{fetch(:tmp_dir)}/#{fetch(:application)}/git-ssh.sh" execute :chmod, "+x", "#{fetch(:tmp_dir)}/#{fetch(:application)}/git-ssh.sh" @@ -19,7 +19,7 @@ namespace :git do desc 'Check that the repository is reachable' task check: :'git:wrapper' do fetch(:branch) - on roles :all do + on release_roles :all do with fetch(:git_environmental_variables) do exit 1 unless test :git, :'ls-remote', repo_url end @@ -28,7 +28,7 @@ namespace :git do desc 'Clone the repo to the cache' task clone: :'git:wrapper' do - on roles :all do + on release_roles :all do if test " [ -f #{repo_path}/HEAD ] " info t(:mirror_exists, at: repo_path) else @@ -43,7 +43,7 @@ namespace :git do desc 'Update the repo mirror to reflect the origin state' task update: :'git:clone' do - on roles :all do + on release_roles :all do within repo_path do execute :git, :remote, :update end @@ -52,7 +52,7 @@ namespace :git do desc 'Copy repo to releases' task create_release: :'git:update' do - on roles :all do + on release_roles :all do with fetch(:git_environmental_variables) do within repo_path do execute :mkdir, '-p', release_path diff --git a/lib/capistrano/tasks/hg.rake b/lib/capistrano/tasks/hg.rake index 4018ef1a..138a75c7 100644 --- a/lib/capistrano/tasks/hg.rake +++ b/lib/capistrano/tasks/hg.rake @@ -1,14 +1,14 @@ namespace :hg do desc 'Check that the repo is reachable' task :check do - on roles :all do + on release_roles :all do execute "hg", "id", repo_url end end desc 'Clone the repo to the cache' task :clone do - on roles :all do + on release_roles :all do if test " [ -d #{repo_path}/.hg ] " info t(:mirror_exists, at: repo_path) else @@ -21,7 +21,7 @@ namespace :hg do desc 'Pull changes from the remote repo' task :update => :'hg:clone' do - on roles :all do + on release_roles :all do within repo_path do execute "hg", "pull" end @@ -30,7 +30,7 @@ namespace :hg do desc 'Copy repo to releases' task :create_release => :'hg:update' do - on roles :all do + on release_roles :all do within repo_path do execute "hg", "archive", release_path, "--rev", fetch(:branch) end diff --git a/lib/capistrano/templates/deploy.rb.erb b/lib/capistrano/templates/deploy.rb.erb index a1b6fd1a..c87fb4a3 100644 --- a/lib/capistrano/templates/deploy.rb.erb +++ b/lib/capistrano/templates/deploy.rb.erb @@ -26,6 +26,8 @@ namespace :deploy do end end + after :publishing, :restart + after :restart, :clear_cache do on roles(:web), in: :groups, limit: 3, wait: 10 do # Here we can do anything such as: @@ -35,6 +37,4 @@ namespace :deploy do end end - after :finishing, 'deploy:cleanup' - end diff --git a/lib/capistrano/templates/stage.rb.erb b/lib/capistrano/templates/stage.rb.erb index c5513e45..a53a8bc5 100644 --- a/lib/capistrano/templates/stage.rb.erb +++ b/lib/capistrano/templates/stage.rb.erb @@ -1,10 +1,9 @@ -set :stage, :<%= stage %> - # Simple Role Syntax # ================== # Supports bulk-adding hosts to roles, the primary # server in each group is considered to be the first # unless any hosts have the primary property set. +# Don't declare `role :all`, it's a meta role role :app, %w{deploy@example.com} role :web, %w{deploy@example.com} role :db, %w{deploy@example.com} diff --git a/spec/integration/dsl_spec.rb b/spec/integration/dsl_spec.rb index d99a8657..d811cf86 100644 --- a/spec/integration/dsl_spec.rb +++ b/spec/integration/dsl_spec.rb @@ -11,13 +11,33 @@ describe Capistrano::DSL do dsl.server 'example2.com', roles: %w{web} dsl.server 'example3.com', roles: %w{app web}, active: true dsl.server 'example4.com', roles: %w{app}, primary: true + dsl.server 'example5.com', roles: %w{db}, no_release: true end describe 'fetching all servers' do subject { dsl.roles(:all) } it 'returns all servers' do - expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} + expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com} + end + end + + describe 'fetching all release servers' do + + context 'with no additional options' do + subject { dsl.release_roles(:all) } + + it 'returns all release servers' do + expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} + end + end + + context 'with filter options' do + subject { dsl.release_roles(:all, filter: :active) } + + it 'returns all release servers that match the filter' do + expect(subject.map(&:hostname)).to eq %w{example1.com example3.com} + end end end @@ -70,6 +90,14 @@ describe Capistrano::DSL do end + describe 'when defining role with reserved name' do + it 'fails with ArgumentError' do + expect { + dsl.role :all, %w{example1.com} + }.to raise_error(ArgumentError, "all reserved name for role. Please choose another name") + end + end + describe 'when defining hosts using the `role` syntax' do before do dsl.role :web, %w{example1.com example2.com example3.com} @@ -77,16 +105,37 @@ describe Capistrano::DSL do dsl.role :app, %w{example3.com example4.com} dsl.role :app, %w{example3.com}, active: true dsl.role :app, %w{example4.com}, primary: true + dsl.role :db, %w{example5.com}, no_release: true end describe 'fetching all servers' do subject { dsl.roles(:all) } it 'returns all servers' do - expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} + expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com} end end + describe 'fetching all release servers' do + + context 'with no additional options' do + subject { dsl.release_roles(:all) } + + it 'returns all release servers' do + expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} + end + end + + context 'with filter options' do + subject { dsl.release_roles(:all, filter: :active) } + + it 'returns all release servers that match the filter' do + expect(subject.map(&:hostname)).to eq %w{example1.com example3.com} + end + end + end + + describe 'fetching servers by role' do subject { dsl.roles(:app) } @@ -340,5 +389,53 @@ describe Capistrano::DSL do end end + describe 'setting deploy configuration path' do + subject { dsl.deploy_config_path.to_s } + + context 'where no config path is set' do + before do + dsl.delete(:deploy_config_path) + end + + it 'returns "config/deploy.rb"' do + expect(subject).to eq 'config/deploy.rb' + end + end + + context 'where a custom path is set' do + before do + dsl.set(:deploy_config_path, 'my/custom/path.rb') + end + + it 'returns the custom path' do + expect(subject).to eq 'my/custom/path.rb' + end + end + end + + describe 'setting stage configuration path' do + subject { dsl.stage_config_path.to_s } + + context 'where no config path is set' do + + before do + dsl.delete(:stage_config_path) + end + + it 'returns "config/deploy"' do + expect(subject).to eq 'config/deploy' + end + end + + context 'where a custom path is set' do + before do + dsl.set(:stage_config_path, 'my/custom/path') + end + + it 'returns the custom path' do + expect(subject).to eq 'my/custom/path' + end + end + end end end diff --git a/spec/lib/capistrano/configuration/server_spec.rb b/spec/lib/capistrano/configuration/server_spec.rb index 4d98cd50..b4bb032a 100644 --- a/spec/lib/capistrano/configuration/server_spec.rb +++ b/spec/lib/capistrano/configuration/server_spec.rb @@ -159,6 +159,11 @@ module Capistrano let(:options) { { select: :active }} it { should be_true } end + + context 'with :exclude' do + let(:options) { { exclude: :active }} + it { should be_false } + end end context 'value does not match server properly' do @@ -171,6 +176,11 @@ module Capistrano let(:options) { { select: :inactive }} it { should be_false } end + + context 'with :exclude' do + let(:options) { { exclude: :inactive }} + it { should be_true } + end end end @@ -186,6 +196,12 @@ module Capistrano let(:options) { { select: ->(s) { s.properties.active } } } it { should be_true } end + + context 'with :exclude' do + let(:options) { { exclude: ->(s) { s.properties.active } } } + it { should be_false } + end + end context 'value does not match server properly' do @@ -198,6 +214,12 @@ module Capistrano let(:options) { { select: ->(s) { s.properties.inactive } } } it { should be_false } end + + context 'with :exclude' do + let(:options) { { exclude: ->(s) { s.properties.inactive } } } + it { should be_true } + end + end end diff --git a/spec/lib/capistrano/configuration/servers_spec.rb b/spec/lib/capistrano/configuration/servers_spec.rb index f250593c..03677bf8 100644 --- a/spec/lib/capistrano/configuration/servers_spec.rb +++ b/spec/lib/capistrano/configuration/servers_spec.rb @@ -141,6 +141,35 @@ module Capistrano end + describe 'excluding by property' do + + before do + servers.add_host('1', roles: :app, active: true) + servers.add_host('2', roles: :app, active: true, no_release: true) + end + + it 'is empty if the filter would remove all matching hosts' do + hosts = servers.roles_for([:app, exclude: :active]) + expect(hosts.map(&:hostname)).to be_empty + end + + it 'returns the servers without the attributes specified' do + hosts = servers.roles_for([:app, exclude: :no_release]) + expect(hosts.map(&:hostname)).to eq %w{1} + end + + it 'can exclude hosts by properties on the host using a regular proc' do + hosts = servers.roles_for([:app, exclude: ->(h) { h.properties.no_release }]) + expect(hosts.map(&:hostname)).to eq %w{1} + end + + it 'is empty if the regular proc filter would remove all matching hosts' do + hosts = servers.roles_for([:app, exclude: ->(h) { h.properties.active }]) + expect(hosts.map(&:hostname)).to be_empty + end + + end + describe 'filtering roles' do before do diff --git a/spec/lib/capistrano/dsl/env_spec.rb b/spec/lib/capistrano/dsl/env_spec.rb deleted file mode 100644 index 61414cba..00000000 --- a/spec/lib/capistrano/dsl/env_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' - -module Capistrano - module DSL - - describe Env do - - end - end -end diff --git a/spec/lib/capistrano/dsl_spec.rb b/spec/lib/capistrano/dsl_spec.rb index 4157a996..9d244218 100644 --- a/spec/lib/capistrano/dsl_spec.rb +++ b/spec/lib/capistrano/dsl_spec.rb @@ -20,17 +20,6 @@ module Capistrano end end - describe '#stages' do - before do - Dir.expects(:[]).with('config/deploy/*.rb'). - returns(['config/deploy/staging.rb', 'config/deploy/production.rb']) - end - - it 'returns a list of defined stages' do - expect(dsl.stages).to eq %w{staging production} - end - end - describe '#stage_set?' do subject { dsl.stage_set? } 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 diff --git a/spec/support/test_app.rb b/spec/support/test_app.rb index 5f18d1fd..f6d1677a 100644 --- a/spec/support/test_app.rb +++ b/spec/support/test_app.rb @@ -8,10 +8,9 @@ module TestApp def default_config %{ - set :stage, :#{stage} set :deploy_to, '#{deploy_to}' set :repo_url, 'git://github.com/capistrano/capistrano.git' - set :branch, 'v3' + set :branch, 'master' set :ssh_options, { keys: "\#{ENV['HOME']}/.vagrant.d/insecure_private_key" } server 'vagrant@localhost:2220', roles: %w{web app} set :linked_files, #{linked_files} @@ -58,6 +57,14 @@ module TestApp end end + def prepend_to_capfile(config) + current_capfile = File.read(capfile) + File.open(capfile, 'w') do |file| + file.write config + file.write current_capfile + end + end + def create_shared_directory(path) FileUtils.mkdir_p(shared_path.join(path)) end @@ -67,9 +74,14 @@ module TestApp end def cap(task) + run "bundle exec cap #{stage} #{task}" + end + + def run(command) Dir.chdir(test_app_path) do - %x[bundle exec cap #{stage} #{task}] + %x[#{command}] end + $?.success? end def stage @@ -135,4 +147,22 @@ module TestApp def copy_task_to_test_app(source) FileUtils.cp(source, task_dir) end + + def config_path + test_app_path.join('config') + end + + def move_configuration_to_custom_location(location) + prepend_to_capfile( + %{ + set :stage_config_path, "app/config/deploy" + set :deploy_config_path, "app/config/deploy.rb" + } + ) + + location = test_app_path.join(location) + FileUtils.mkdir_p(location) + FileUtils.mv(config_path, location) + end + end