mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
Merge branch '3.1.x'
Conflicts: CHANGELOG.md README.md lib/capistrano/tasks/git.rake lib/capistrano/templates/deploy.rb.erb
This commit is contained in:
commit
22a98f30a6
32 changed files with 378 additions and 55 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -2,9 +2,20 @@
|
||||||
|
|
||||||
Reverse Chronological Order:
|
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`
|
## `3.0.1`
|
||||||
|
|
||||||
* capify' not listed as executable (@leehambley)
|
* `capify` not listed as executable (@leehambley)
|
||||||
* Confirm license as MIT (@leehambley)
|
* Confirm license as MIT (@leehambley)
|
||||||
* Move the git ssh helper to application path (@mpapis)
|
* Move the git ssh helper to application path (@mpapis)
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ This method is widely used.
|
||||||
``` ruby
|
``` ruby
|
||||||
desc "Ask about breakfast"
|
desc "Ask about breakfast"
|
||||||
task :breakfast do
|
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|
|
on roles(:all) do |h|
|
||||||
execute "echo \"$(whoami) wants #{breakfast} for breakfast!\" | wall"
|
execute "echo \"$(whoami) wants #{breakfast} for breakfast!\" | wall"
|
||||||
end
|
end
|
||||||
|
|
15
features/configuration.feature
Normal file
15
features/configuration.feature
Normal file
|
@ -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
|
|
@ -34,6 +34,7 @@ Feature: Deploy
|
||||||
Then the task will be successful
|
Then the task will be successful
|
||||||
|
|
||||||
Scenario: Creating a release
|
Scenario: Creating a release
|
||||||
|
Given I run cap "deploy:check:directories"
|
||||||
When I run cap "git:create_release" as part of a release
|
When I run cap "git:create_release" as part of a release
|
||||||
Then the repo is cloned
|
Then the repo is cloned
|
||||||
And the release is created
|
And the release is created
|
||||||
|
|
17
features/deploy_failure.feature
Normal file
17
features/deploy_failure.feature
Normal 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
|
|
@ -88,3 +88,22 @@ end
|
||||||
Then(/^it will not recreate the file$/) do
|
Then(/^it will not recreate the file$/) do
|
||||||
#
|
#
|
||||||
end
|
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
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
When(/^I run cap "(.*?)"$/) do |task|
|
When(/^I run cap "(.*?)"$/) do |task|
|
||||||
TestApp.cap(task)
|
@success = TestApp.cap(task)
|
||||||
end
|
end
|
||||||
|
|
||||||
When(/^I run cap "(.*?)" as part of a release$/) do |task|
|
When(/^I run cap "(.*?)" as part of a release$/) do |task|
|
||||||
TestApp.cap("deploy:new_release_path #{task}")
|
TestApp.cap("deploy:new_release_path #{task}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
When(/^I run "(.*?)"$/) do |command|
|
||||||
|
@success = TestApp.run(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,16 @@ Given(/^a custom task to generate a file$/) do
|
||||||
TestApp.copy_task_to_test_app('spec/support/tasks/database.cap')
|
TestApp.copy_task_to_test_app('spec/support/tasks/database.cap')
|
||||||
end
|
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
|
||||||
|
|
|
@ -15,6 +15,10 @@ module RemoteCommandHelpers
|
||||||
def exists?(type, path)
|
def exists?(type, path)
|
||||||
%{[ -#{type} "#{path}" ] && echo "#{path} exists." || echo "Error: #{path} does not exist."}
|
%{[ -#{type} "#{path}" ] && echo "#{path} exists." || echo "Error: #{path} does not exist."}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def safely_remove_file(path)
|
||||||
|
run_vagrant_command("rm #{test_file}") rescue Vagrant::Errors::VagrantError
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
World(RemoteCommandHelpers)
|
World(RemoteCommandHelpers)
|
||||||
|
|
|
@ -30,6 +30,14 @@ module Capistrano
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exit_because_of_exception(ex)
|
||||||
|
if deploying?
|
||||||
|
exit_deploy_because_of_exception(ex)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# allows the `cap install` task to load without a capfile
|
# allows the `cap install` task to load without a capfile
|
||||||
|
|
|
@ -34,6 +34,10 @@ module Capistrano
|
||||||
end
|
end
|
||||||
|
|
||||||
def role(name, hosts, options={})
|
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)
|
servers.add_role(name, hosts, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ module Capistrano
|
||||||
end
|
end
|
||||||
|
|
||||||
def select?(options)
|
def select?(options)
|
||||||
selector = Selector.new(options)
|
selector = Selector.for(options)
|
||||||
selector.call(self)
|
selector.call(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,6 +103,14 @@ module Capistrano
|
||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.for(options)
|
||||||
|
if options.has_key?(:exclude)
|
||||||
|
Exclusive
|
||||||
|
else
|
||||||
|
self
|
||||||
|
end.new(options)
|
||||||
|
end
|
||||||
|
|
||||||
def callable
|
def callable
|
||||||
if key.respond_to?(:call)
|
if key.respond_to?(:call)
|
||||||
key
|
key
|
||||||
|
@ -126,6 +134,17 @@ module Capistrano
|
||||||
->(server) { :all }
|
->(server) { :all }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Exclusive < Selector
|
||||||
|
|
||||||
|
def key
|
||||||
|
options[:exclude]
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(server)
|
||||||
|
!callable.call(server)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,6 +43,16 @@ module Capistrano
|
||||||
env.roles_for(names)
|
env.roles_for(names)
|
||||||
end
|
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)
|
def primary(role)
|
||||||
env.primary(role)
|
env.primary(role)
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,14 @@ module Capistrano
|
||||||
set(:release_path, releases_path.join(timestamp))
|
set(:release_path, releases_path.join(timestamp))
|
||||||
end
|
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
|
def repo_url
|
||||||
require 'cgi'
|
require 'cgi'
|
||||||
require 'uri'
|
require 'uri'
|
||||||
|
|
|
@ -3,7 +3,14 @@ module Capistrano
|
||||||
module Stages
|
module Stages
|
||||||
|
|
||||||
def 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
|
end
|
||||||
|
|
||||||
def stage_set?
|
def stage_set?
|
||||||
|
|
|
@ -6,9 +6,10 @@ module Capistrano
|
||||||
end
|
end
|
||||||
|
|
||||||
def after(task, post_task, *args, &block)
|
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
|
Rake::Task[task].enhance do
|
||||||
invoke(post_task)
|
post_task.invoke
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -49,5 +50,15 @@ module Capistrano
|
||||||
%w{install}
|
%w{install}
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,6 @@ en = {
|
||||||
start: 'Start',
|
start: 'Start',
|
||||||
update: 'Update',
|
update: 'Update',
|
||||||
finalize: 'Finalise',
|
finalize: 'Finalise',
|
||||||
restart: 'Restart',
|
|
||||||
finishing: 'Finishing',
|
finishing: 'Finishing',
|
||||||
finished: 'Finished',
|
finished: 'Finished',
|
||||||
stage_not_set: 'Stage not set, please call something such as `cap production deploy`, where production is a stage you have defined.',
|
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}",
|
mirror_exists: "The repository mirror is at %{at}",
|
||||||
revision_log_message: 'Branch %{branch} deployed as release %{release} by %{user}',
|
revision_log_message: 'Branch %{branch} deployed as release %{release} by %{user}',
|
||||||
rollback_log_message: '%{user} rolled back to release %{release}',
|
rollback_log_message: '%{user} rolled back to release %{release}',
|
||||||
|
deploy_failed: 'The deploy has failed with an error: %{ex}',
|
||||||
console: {
|
console: {
|
||||||
welcome: 'capistrano console - enter command to execute on %{stage}',
|
welcome: 'capistrano console - enter command to execute on %{stage}',
|
||||||
bye: 'bye'
|
bye: 'bye'
|
||||||
|
|
|
@ -8,11 +8,12 @@ end
|
||||||
|
|
||||||
stages.each do |stage|
|
stages.each do |stage|
|
||||||
Rake::Task.define_task(stage) do
|
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)
|
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)
|
I18n.locale = fetch(:locale, :en)
|
||||||
configure_backend
|
configure_backend
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace :deploy do
|
||||||
|
|
||||||
task :publishing do
|
task :publishing do
|
||||||
invoke 'deploy:symlink:release'
|
invoke 'deploy:symlink:release'
|
||||||
invoke 'deploy:restart'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
task :finishing do
|
task :finishing do
|
||||||
|
@ -42,7 +41,7 @@ namespace :deploy do
|
||||||
namespace :check do
|
namespace :check do
|
||||||
desc 'Check shared and release directories exist'
|
desc 'Check shared and release directories exist'
|
||||||
task :directories do
|
task :directories do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
execute :mkdir, '-pv', shared_path, releases_path
|
execute :mkdir, '-pv', shared_path, releases_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -80,7 +79,7 @@ namespace :deploy do
|
||||||
namespace :symlink do
|
namespace :symlink do
|
||||||
desc 'Symlink release to current'
|
desc 'Symlink release to current'
|
||||||
task :release do
|
task :release do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
execute :rm, '-rf', current_path
|
execute :rm, '-rf', current_path
|
||||||
execute :ln, '-s', release_path, current_path
|
execute :ln, '-s', release_path, current_path
|
||||||
end
|
end
|
||||||
|
@ -133,7 +132,7 @@ namespace :deploy do
|
||||||
|
|
||||||
desc 'Clean up old releases'
|
desc 'Clean up old releases'
|
||||||
task :cleanup do
|
task :cleanup do
|
||||||
on roles :all do |host|
|
on release_roles :all do |host|
|
||||||
releases = capture(:ls, '-x', releases_path).split
|
releases = capture(:ls, '-x', releases_path).split
|
||||||
if releases.count >= fetch(:keep_releases)
|
if releases.count >= fetch(:keep_releases)
|
||||||
info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: releases.count)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :restart
|
||||||
|
task :failed
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,6 +57,7 @@ end
|
||||||
|
|
||||||
desc 'Deploy a new release.'
|
desc 'Deploy a new release.'
|
||||||
task :deploy do
|
task :deploy do
|
||||||
|
set(:deploying, true)
|
||||||
%w{ starting started
|
%w{ starting started
|
||||||
updating updated
|
updating updated
|
||||||
publishing published
|
publishing published
|
||||||
|
|
|
@ -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'
|
desc 'Upload the git wrapper script, this script guarantees that we can script git without getting an interactive prompt'
|
||||||
task :wrapper do
|
task :wrapper do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
execute :mkdir, "-p", "#{fetch(:tmp_dir)}/#{fetch(:application)}/"
|
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"
|
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"
|
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'
|
desc 'Check that the repository is reachable'
|
||||||
task check: :'git:wrapper' do
|
task check: :'git:wrapper' do
|
||||||
fetch(:branch)
|
fetch(:branch)
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
with fetch(:git_environmental_variables) do
|
with fetch(:git_environmental_variables) do
|
||||||
exit 1 unless test :git, :'ls-remote', repo_url
|
exit 1 unless test :git, :'ls-remote', repo_url
|
||||||
end
|
end
|
||||||
|
@ -28,7 +28,7 @@ namespace :git do
|
||||||
|
|
||||||
desc 'Clone the repo to the cache'
|
desc 'Clone the repo to the cache'
|
||||||
task clone: :'git:wrapper' do
|
task clone: :'git:wrapper' do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
if test " [ -f #{repo_path}/HEAD ] "
|
if test " [ -f #{repo_path}/HEAD ] "
|
||||||
info t(:mirror_exists, at: repo_path)
|
info t(:mirror_exists, at: repo_path)
|
||||||
else
|
else
|
||||||
|
@ -43,7 +43,7 @@ namespace :git do
|
||||||
|
|
||||||
desc 'Update the repo mirror to reflect the origin state'
|
desc 'Update the repo mirror to reflect the origin state'
|
||||||
task update: :'git:clone' do
|
task update: :'git:clone' do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute :git, :remote, :update
|
execute :git, :remote, :update
|
||||||
end
|
end
|
||||||
|
@ -52,7 +52,7 @@ namespace :git do
|
||||||
|
|
||||||
desc 'Copy repo to releases'
|
desc 'Copy repo to releases'
|
||||||
task create_release: :'git:update' do
|
task create_release: :'git:update' do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
with fetch(:git_environmental_variables) do
|
with fetch(:git_environmental_variables) do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute :mkdir, '-p', release_path
|
execute :mkdir, '-p', release_path
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
namespace :hg do
|
namespace :hg do
|
||||||
desc 'Check that the repo is reachable'
|
desc 'Check that the repo is reachable'
|
||||||
task :check do
|
task :check do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
execute "hg", "id", repo_url
|
execute "hg", "id", repo_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Clone the repo to the cache'
|
desc 'Clone the repo to the cache'
|
||||||
task :clone do
|
task :clone do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
if test " [ -d #{repo_path}/.hg ] "
|
if test " [ -d #{repo_path}/.hg ] "
|
||||||
info t(:mirror_exists, at: repo_path)
|
info t(:mirror_exists, at: repo_path)
|
||||||
else
|
else
|
||||||
|
@ -21,7 +21,7 @@ namespace :hg do
|
||||||
|
|
||||||
desc 'Pull changes from the remote repo'
|
desc 'Pull changes from the remote repo'
|
||||||
task :update => :'hg:clone' do
|
task :update => :'hg:clone' do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute "hg", "pull"
|
execute "hg", "pull"
|
||||||
end
|
end
|
||||||
|
@ -30,7 +30,7 @@ namespace :hg do
|
||||||
|
|
||||||
desc 'Copy repo to releases'
|
desc 'Copy repo to releases'
|
||||||
task :create_release => :'hg:update' do
|
task :create_release => :'hg:update' do
|
||||||
on roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute "hg", "archive", release_path, "--rev", fetch(:branch)
|
execute "hg", "archive", release_path, "--rev", fetch(:branch)
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,6 +26,8 @@ namespace :deploy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
after :publishing, :restart
|
||||||
|
|
||||||
after :restart, :clear_cache do
|
after :restart, :clear_cache do
|
||||||
on roles(:web), in: :groups, limit: 3, wait: 10 do
|
on roles(:web), in: :groups, limit: 3, wait: 10 do
|
||||||
# Here we can do anything such as:
|
# Here we can do anything such as:
|
||||||
|
@ -35,6 +37,4 @@ namespace :deploy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after :finishing, 'deploy:cleanup'
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
set :stage, :<%= stage %>
|
|
||||||
|
|
||||||
# Simple Role Syntax
|
# Simple Role Syntax
|
||||||
# ==================
|
# ==================
|
||||||
# Supports bulk-adding hosts to roles, the primary
|
# Supports bulk-adding hosts to roles, the primary
|
||||||
# server in each group is considered to be the first
|
# server in each group is considered to be the first
|
||||||
# unless any hosts have the primary property set.
|
# 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 :app, %w{deploy@example.com}
|
||||||
role :web, %w{deploy@example.com}
|
role :web, %w{deploy@example.com}
|
||||||
role :db, %w{deploy@example.com}
|
role :db, %w{deploy@example.com}
|
||||||
|
|
|
@ -11,13 +11,33 @@ describe Capistrano::DSL do
|
||||||
dsl.server 'example2.com', roles: %w{web}
|
dsl.server 'example2.com', roles: %w{web}
|
||||||
dsl.server 'example3.com', roles: %w{app web}, active: true
|
dsl.server 'example3.com', roles: %w{app web}, active: true
|
||||||
dsl.server 'example4.com', roles: %w{app}, primary: true
|
dsl.server 'example4.com', roles: %w{app}, primary: true
|
||||||
|
dsl.server 'example5.com', roles: %w{db}, no_release: true
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'fetching all servers' do
|
describe 'fetching all servers' do
|
||||||
subject { dsl.roles(:all) }
|
subject { dsl.roles(:all) }
|
||||||
|
|
||||||
it 'returns all servers' do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,6 +90,14 @@ describe Capistrano::DSL do
|
||||||
|
|
||||||
end
|
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
|
describe 'when defining hosts using the `role` syntax' do
|
||||||
before do
|
before do
|
||||||
dsl.role :web, %w{example1.com example2.com example3.com}
|
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 example4.com}
|
||||||
dsl.role :app, %w{example3.com}, active: true
|
dsl.role :app, %w{example3.com}, active: true
|
||||||
dsl.role :app, %w{example4.com}, primary: true
|
dsl.role :app, %w{example4.com}, primary: true
|
||||||
|
dsl.role :db, %w{example5.com}, no_release: true
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'fetching all servers' do
|
describe 'fetching all servers' do
|
||||||
subject { dsl.roles(:all) }
|
subject { dsl.roles(:all) }
|
||||||
|
|
||||||
it 'returns all servers' do
|
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
|
||||||
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
|
describe 'fetching servers by role' do
|
||||||
subject { dsl.roles(:app) }
|
subject { dsl.roles(:app) }
|
||||||
|
|
||||||
|
@ -340,5 +389,53 @@ describe Capistrano::DSL do
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -159,6 +159,11 @@ module Capistrano
|
||||||
let(:options) { { select: :active }}
|
let(:options) { { select: :active }}
|
||||||
it { should be_true }
|
it { should be_true }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with :exclude' do
|
||||||
|
let(:options) { { exclude: :active }}
|
||||||
|
it { should be_false }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'value does not match server properly' do
|
context 'value does not match server properly' do
|
||||||
|
@ -171,6 +176,11 @@ module Capistrano
|
||||||
let(:options) { { select: :inactive }}
|
let(:options) { { select: :inactive }}
|
||||||
it { should be_false }
|
it { should be_false }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with :exclude' do
|
||||||
|
let(:options) { { exclude: :inactive }}
|
||||||
|
it { should be_true }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -186,6 +196,12 @@ module Capistrano
|
||||||
let(:options) { { select: ->(s) { s.properties.active } } }
|
let(:options) { { select: ->(s) { s.properties.active } } }
|
||||||
it { should be_true }
|
it { should be_true }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with :exclude' do
|
||||||
|
let(:options) { { exclude: ->(s) { s.properties.active } } }
|
||||||
|
it { should be_false }
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'value does not match server properly' do
|
context 'value does not match server properly' do
|
||||||
|
@ -198,6 +214,12 @@ module Capistrano
|
||||||
let(:options) { { select: ->(s) { s.properties.inactive } } }
|
let(:options) { { select: ->(s) { s.properties.inactive } } }
|
||||||
it { should be_false }
|
it { should be_false }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with :exclude' do
|
||||||
|
let(:options) { { exclude: ->(s) { s.properties.inactive } } }
|
||||||
|
it { should be_true }
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,35 @@ module Capistrano
|
||||||
|
|
||||||
end
|
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
|
describe 'filtering roles' do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
module Capistrano
|
|
||||||
module DSL
|
|
||||||
|
|
||||||
describe Env do
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -20,17 +20,6 @@ module Capistrano
|
||||||
end
|
end
|
||||||
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
|
describe '#stage_set?' do
|
||||||
subject { dsl.stage_set? }
|
subject { dsl.stage_set? }
|
||||||
|
|
||||||
|
|
7
spec/support/tasks/fail.cap
Normal file
7
spec/support/tasks/fail.cap
Normal 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
|
5
spec/support/tasks/failed.cap
Normal file
5
spec/support/tasks/failed.cap
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
after 'deploy:failed', :failed do
|
||||||
|
on roles :all do
|
||||||
|
execute :touch, shared_path.join('failed')
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,10 +8,9 @@ module TestApp
|
||||||
|
|
||||||
def default_config
|
def default_config
|
||||||
%{
|
%{
|
||||||
set :stage, :#{stage}
|
|
||||||
set :deploy_to, '#{deploy_to}'
|
set :deploy_to, '#{deploy_to}'
|
||||||
set :repo_url, 'git://github.com/capistrano/capistrano.git'
|
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" }
|
set :ssh_options, { keys: "\#{ENV['HOME']}/.vagrant.d/insecure_private_key" }
|
||||||
server 'vagrant@localhost:2220', roles: %w{web app}
|
server 'vagrant@localhost:2220', roles: %w{web app}
|
||||||
set :linked_files, #{linked_files}
|
set :linked_files, #{linked_files}
|
||||||
|
@ -58,6 +57,14 @@ module TestApp
|
||||||
end
|
end
|
||||||
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)
|
def create_shared_directory(path)
|
||||||
FileUtils.mkdir_p(shared_path.join(path))
|
FileUtils.mkdir_p(shared_path.join(path))
|
||||||
end
|
end
|
||||||
|
@ -67,9 +74,14 @@ module TestApp
|
||||||
end
|
end
|
||||||
|
|
||||||
def cap(task)
|
def cap(task)
|
||||||
|
run "bundle exec cap #{stage} #{task}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(command)
|
||||||
Dir.chdir(test_app_path) do
|
Dir.chdir(test_app_path) do
|
||||||
%x[bundle exec cap #{stage} #{task}]
|
%x[#{command}]
|
||||||
end
|
end
|
||||||
|
$?.success?
|
||||||
end
|
end
|
||||||
|
|
||||||
def stage
|
def stage
|
||||||
|
@ -135,4 +147,22 @@ module TestApp
|
||||||
def copy_task_to_test_app(source)
|
def copy_task_to_test_app(source)
|
||||||
FileUtils.cp(source, task_dir)
|
FileUtils.cp(source, task_dir)
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue