capistrano 3.2.1 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/CHANGELOG.md +92 -2
- data/Gemfile +1 -5
- data/README.md +84 -3
- data/Rakefile +5 -1
- data/capistrano.gemspec +5 -1
- data/features/configuration.feature +9 -7
- data/features/deploy.feature +9 -8
- data/features/step_definitions/assertions.rb +15 -17
- data/features/step_definitions/cap_commands.rb +1 -1
- data/features/step_definitions/setup.rb +11 -7
- data/features/support/env.rb +8 -9
- data/features/support/remote_command_helpers.rb +2 -3
- data/features/support/vagrant_helpers.rb +35 -0
- data/lib/capistrano/all.rb +2 -1
- data/lib/capistrano/application.rb +52 -7
- data/lib/capistrano/configuration.rb +39 -10
- data/lib/capistrano/configuration/filter.rb +56 -0
- data/lib/capistrano/configuration/question.rb +23 -11
- data/lib/capistrano/configuration/server.rb +14 -5
- data/lib/capistrano/configuration/servers.rb +12 -29
- data/lib/capistrano/defaults.rb +11 -9
- data/lib/capistrano/deploy.rb +1 -0
- data/lib/capistrano/dsl.rb +13 -2
- data/lib/capistrano/dsl/env.rb +6 -2
- data/lib/capistrano/dsl/task_enhancements.rb +5 -3
- data/lib/capistrano/git.rb +8 -2
- data/lib/capistrano/hg.rb +7 -1
- data/lib/capistrano/svn.rb +2 -2
- data/lib/capistrano/tasks/deploy.rake +12 -10
- data/lib/capistrano/tasks/git.rake +1 -1
- data/lib/capistrano/tasks/install.rake +17 -14
- data/lib/capistrano/templates/Capfile +6 -4
- data/lib/capistrano/templates/deploy.rb.erb +5 -15
- data/lib/capistrano/upload_task.rb +9 -0
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/dsl_spec.rb +129 -10
- data/spec/lib/capistrano/application_spec.rb +24 -6
- data/spec/lib/capistrano/configuration/filter_spec.rb +105 -0
- data/spec/lib/capistrano/configuration/question_spec.rb +18 -12
- data/spec/lib/capistrano/configuration/server_spec.rb +19 -19
- data/spec/lib/capistrano/configuration/servers_spec.rb +101 -20
- data/spec/lib/capistrano/configuration_spec.rb +24 -3
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +88 -0
- data/spec/lib/capistrano/dsl_spec.rb +2 -13
- data/spec/lib/capistrano/git_spec.rb +15 -4
- data/spec/lib/capistrano/hg_spec.rb +13 -2
- data/spec/lib/capistrano/scm_spec.rb +3 -3
- data/spec/lib/capistrano/svn_spec.rb +11 -1
- data/spec/lib/capistrano/upload_task_spec.rb +19 -0
- data/spec/lib/capistrano/version_validator_spec.rb +4 -4
- data/spec/spec_helper.rb +2 -1
- data/spec/support/Vagrantfile +1 -1
- data/spec/support/test_app.rb +2 -0
- metadata +45 -26
- data/lib/capistrano/configuration/servers/host_filter.rb +0 -82
- data/lib/capistrano/configuration/servers/role_filter.rb +0 -86
- data/spec/lib/capistrano/configuration/servers/host_filter_spec.rb +0 -84
- data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +0 -140
@@ -6,17 +6,21 @@ Given(/^servers with the roles app and web$/) do
|
|
6
6
|
vagrant_cli_command('up') rescue nil
|
7
7
|
end
|
8
8
|
|
9
|
-
Given(/^a
|
9
|
+
Given(/^a linked file "(.*?)"$/) do |file|
|
10
|
+
# ignoring other linked files
|
11
|
+
TestApp.append_to_deploy_file("set :linked_files, ['#{file}']")
|
10
12
|
end
|
11
13
|
|
12
|
-
Given(/^
|
13
|
-
|
14
|
+
Given(/^file "(.*?)" exists in shared path$/) do |file|
|
15
|
+
file_shared_path = TestApp.shared_path.join(file)
|
16
|
+
run_vagrant_command("mkdir -p #{TestApp.shared_path}")
|
17
|
+
run_vagrant_command("touch #{file_shared_path}")
|
14
18
|
end
|
15
19
|
|
16
|
-
Given(/^
|
17
|
-
|
18
|
-
|
19
|
-
run_vagrant_command("
|
20
|
+
Given(/^file "(.*?)" does not exist in shared path$/) do |file|
|
21
|
+
file_shared_path = TestApp.shared_path.join(file)
|
22
|
+
run_vagrant_command("mkdir -p #{TestApp.shared_path}")
|
23
|
+
run_vagrant_command("touch #{file_shared_path} && rm #{file_shared_path}")
|
20
24
|
end
|
21
25
|
|
22
26
|
Given(/^a custom task to generate a file$/) do
|
data/features/support/env.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
PROJECT_ROOT = File.expand_path('../../../', __FILE__)
|
2
|
+
VAGRANT_ROOT = File.join(PROJECT_ROOT, 'spec/support')
|
3
|
+
VAGRANT_BIN = ENV['VAGRANT_BIN'] || "vagrant"
|
4
|
+
|
5
|
+
at_exit do
|
6
|
+
if ENV['KEEP_RUNNING']
|
7
|
+
VagrantHelpers.run_vagrant_command("rm -rf /home/vagrant/var")
|
8
|
+
end
|
8
9
|
end
|
9
10
|
|
10
|
-
puts vagrant_root.inspect
|
11
|
-
|
12
11
|
require_relative '../../spec/support/test_app'
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module RemoteCommandHelpers
|
2
|
-
|
3
2
|
def test_dir_exists(path)
|
4
3
|
exists?('d', path)
|
5
4
|
end
|
@@ -13,11 +12,11 @@ module RemoteCommandHelpers
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def exists?(type, path)
|
16
|
-
%{[ -#{type} "#{path}" ]
|
15
|
+
%{[ -#{type} "#{path}" ]}
|
17
16
|
end
|
18
17
|
|
19
18
|
def safely_remove_file(path)
|
20
|
-
run_vagrant_command("rm #{test_file}") rescue
|
19
|
+
run_vagrant_command("rm #{test_file}") rescue VagrantHelpers::VagrantSSHCommandError
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module VagrantHelpers
|
2
|
+
extend self
|
3
|
+
|
4
|
+
class VagrantSSHCommandError < RuntimeError; end
|
5
|
+
|
6
|
+
at_exit do
|
7
|
+
if ENV['KEEP_RUNNING']
|
8
|
+
puts "Vagrant vm will be left up because KEEP_RUNNING is set."
|
9
|
+
puts "Rerun without KEEP_RUNNING set to cleanup the vm."
|
10
|
+
else
|
11
|
+
vagrant_cli_command("destroy -f")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def vagrant_cli_command(command)
|
16
|
+
puts "[vagrant] #{command}"
|
17
|
+
Dir.chdir(VAGRANT_ROOT) do
|
18
|
+
`#{VAGRANT_BIN} #{command} 2>&1`.split("\n").each do |line|
|
19
|
+
puts "[vagrant] #{line}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
$?
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_vagrant_command(command)
|
26
|
+
if (status = vagrant_cli_command("ssh -c #{command.inspect}")).success?
|
27
|
+
true
|
28
|
+
else
|
29
|
+
fail VagrantSSHCommandError, status
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
World(VagrantHelpers)
|
data/lib/capistrano/all.rb
CHANGED
@@ -16,10 +16,43 @@ module Capistrano
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def sort_options(options)
|
19
|
-
|
20
|
-
|
19
|
+
not_applicable_to_capistrano = %w(quiet silent verbose)
|
20
|
+
options.reject! do |(switch, *)|
|
21
|
+
switch =~ /--#{Regexp.union(not_applicable_to_capistrano)}/
|
22
|
+
end
|
23
|
+
|
24
|
+
super.push(version, dry_run, roles, hostfilter)
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_options
|
28
|
+
options.rakelib = ['rakelib']
|
29
|
+
options.trace_output = $stderr
|
30
|
+
|
31
|
+
OptionParser.new do |opts|
|
32
|
+
opts.banner = "See full documentation at http://capistranorb.com/."
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "Install capistrano in a project:"
|
35
|
+
opts.separator " bundle exec cap install [STAGES=qa,staging,production,...]"
|
36
|
+
opts.separator ""
|
37
|
+
opts.separator "Show available tasks:"
|
38
|
+
opts.separator " bundle exec cap -T"
|
39
|
+
opts.separator ""
|
40
|
+
opts.separator "Invoke (or simulate invoking) a task:"
|
41
|
+
opts.separator " bundle exec cap [--dry-run] STAGE TASK"
|
42
|
+
opts.separator ""
|
43
|
+
opts.separator "Advanced options:"
|
44
|
+
|
45
|
+
opts.on_tail("-h", "--help", "-H", "Display this help message.") do
|
46
|
+
puts opts
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
standard_rake_options.each { |args| opts.on(*args) }
|
51
|
+
opts.environment('RAKEOPT')
|
52
|
+
end.parse!
|
21
53
|
end
|
22
54
|
|
55
|
+
|
23
56
|
def top_level_tasks
|
24
57
|
if tasks_without_stage_dependency.include?(@top_level_tasks.first)
|
25
58
|
@top_level_tasks
|
@@ -28,6 +61,18 @@ module Capistrano
|
|
28
61
|
end
|
29
62
|
end
|
30
63
|
|
64
|
+
def display_error_message(ex)
|
65
|
+
unless options.backtrace
|
66
|
+
if loc = Rake.application.find_rakefile_location
|
67
|
+
whitelist = (@imported.dup << loc[0]).map{|f| File.absolute_path(f, loc[1])}
|
68
|
+
pattern = %r@^(?!#{whitelist.map{|p| Regexp.quote(p)}.join('|')})@
|
69
|
+
Rake.application.options.suppress_backtrace_pattern = pattern
|
70
|
+
end
|
71
|
+
trace "(Backtrace restricted to imported tasks)"
|
72
|
+
end
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
31
76
|
def exit_because_of_exception(ex)
|
32
77
|
if respond_to?(:deploying?) && deploying?
|
33
78
|
exit_deploy_because_of_exception(ex)
|
@@ -42,7 +87,7 @@ module Capistrano
|
|
42
87
|
if options.show_tasks
|
43
88
|
invoke 'load:defaults'
|
44
89
|
set(:stage, '')
|
45
|
-
Dir[deploy_config_path
|
90
|
+
Dir[deploy_config_path].each { |f| add_import f }
|
46
91
|
end
|
47
92
|
|
48
93
|
super
|
@@ -74,18 +119,18 @@ module Capistrano
|
|
74
119
|
|
75
120
|
def roles
|
76
121
|
['--roles ROLES', '-r',
|
77
|
-
"
|
122
|
+
"Run SSH commands only on hosts matching these roles",
|
78
123
|
lambda { |value|
|
79
|
-
Configuration.env.
|
124
|
+
Configuration.env.add_cmdline_filter(:role, value)
|
80
125
|
}
|
81
126
|
]
|
82
127
|
end
|
83
128
|
|
84
129
|
def hostfilter
|
85
130
|
['--hosts HOSTS', '-z',
|
86
|
-
"
|
131
|
+
"Run SSH commands only on matching hosts",
|
87
132
|
lambda { |value|
|
88
|
-
Configuration.env.
|
133
|
+
Configuration.env.add_cmdline_filter(:host, value)
|
89
134
|
}
|
90
135
|
]
|
91
136
|
end
|
@@ -1,22 +1,25 @@
|
|
1
|
+
require_relative 'configuration/filter'
|
1
2
|
require_relative 'configuration/question'
|
2
|
-
require_relative 'configuration/servers'
|
3
3
|
require_relative 'configuration/server'
|
4
|
+
require_relative 'configuration/servers'
|
4
5
|
|
5
6
|
module Capistrano
|
6
7
|
class Configuration
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
9
|
+
def initialize(config = nil)
|
10
|
+
@config ||= config
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
13
|
+
def self.env
|
14
|
+
@env ||= new
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
17
|
+
def self.reset!
|
18
|
+
@env = new
|
19
|
+
end
|
20
|
+
|
21
|
+
def ask(key, default=nil, options={})
|
22
|
+
question = Question.new(key, default, options)
|
20
23
|
set(key, question)
|
21
24
|
end
|
22
25
|
|
@@ -24,6 +27,10 @@ module Capistrano
|
|
24
27
|
config[key] = value
|
25
28
|
end
|
26
29
|
|
30
|
+
def set_if_empty(key, value)
|
31
|
+
config[key] = value unless config.has_key? key
|
32
|
+
end
|
33
|
+
|
27
34
|
def delete(key)
|
28
35
|
config.delete(key)
|
29
36
|
end
|
@@ -84,8 +91,30 @@ module Capistrano
|
|
84
91
|
@timestamp ||= Time.now.utc
|
85
92
|
end
|
86
93
|
|
94
|
+
def setup_filters
|
95
|
+
@filters = cmdline_filters.clone
|
96
|
+
@filters << Filter.new(:role, ENV['ROLES']) if ENV['ROLES']
|
97
|
+
@filters << Filter.new(:host, ENV['HOSTS']) if ENV['HOSTS']
|
98
|
+
fh = fetch_for(:filter,{})
|
99
|
+
@filters << Filter.new(:host, fh[:host]) if fh[:host]
|
100
|
+
@filters << Filter.new(:role, fh[:role]) if fh[:role]
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_cmdline_filter(type, values)
|
104
|
+
cmdline_filters << Filter.new(type, values)
|
105
|
+
end
|
106
|
+
|
107
|
+
def filter list
|
108
|
+
setup_filters if @filters.nil?
|
109
|
+
@filters.reduce(list) { |l,f| f.filter l }
|
110
|
+
end
|
111
|
+
|
87
112
|
private
|
88
113
|
|
114
|
+
def cmdline_filters
|
115
|
+
@cmdline_filters ||= []
|
116
|
+
end
|
117
|
+
|
89
118
|
def servers
|
90
119
|
@servers ||= Servers.new
|
91
120
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'capistrano/configuration'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
class Configuration
|
5
|
+
class Filter
|
6
|
+
def initialize type, values = nil
|
7
|
+
raise "Invalid filter type #{type}" unless [:host,:role].include? type
|
8
|
+
av = Array(values)
|
9
|
+
@mode = case
|
10
|
+
when av.size == 0 then :none
|
11
|
+
when av.include?(:all) then :all
|
12
|
+
else type
|
13
|
+
end
|
14
|
+
@rex = case @mode
|
15
|
+
when :host
|
16
|
+
av.map!{|v| (v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/) ? v.split(',') : v }
|
17
|
+
av.flatten!
|
18
|
+
av.map! do |v|
|
19
|
+
case v
|
20
|
+
when Regexp then v
|
21
|
+
else
|
22
|
+
vs = v.to_s
|
23
|
+
vs =~ /^[-A-Za-z0-9.]+$/ ? vs : Regexp.new(vs)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
Regexp.union av
|
27
|
+
when :role
|
28
|
+
av.map!{|v| v.is_a?(String) ? v.split(',') : v }
|
29
|
+
av.flatten!
|
30
|
+
av.map! do |v|
|
31
|
+
case v
|
32
|
+
when Regexp then v
|
33
|
+
else
|
34
|
+
vs = v.to_s
|
35
|
+
vs =~ %r{^/(.+)/$} ? Regexp.new($1) : vs
|
36
|
+
end
|
37
|
+
end
|
38
|
+
Regexp.union av
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
def filter servers
|
44
|
+
as = Array(servers)
|
45
|
+
case @mode
|
46
|
+
when :none then return []
|
47
|
+
when :all then return servers
|
48
|
+
when :host
|
49
|
+
as.select {|s| @rex.match s.hostname}
|
50
|
+
when :role
|
51
|
+
as.select {|s| s.roles.any? {|r| @rex.match r} }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -2,27 +2,23 @@ module Capistrano
|
|
2
2
|
class Configuration
|
3
3
|
class Question
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(key, default, options = {})
|
6
|
+
@key, @default, @options = key, default, options
|
7
7
|
end
|
8
8
|
|
9
9
|
def call
|
10
10
|
ask_question
|
11
|
-
|
11
|
+
value_or_default
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
|
-
attr_reader :
|
15
|
+
attr_reader :key, :default, :options
|
16
16
|
|
17
17
|
def ask_question
|
18
18
|
$stdout.print question
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
env.set(key, value)
|
23
|
-
end
|
24
|
-
|
25
|
-
def value
|
21
|
+
def value_or_default
|
26
22
|
if response.empty?
|
27
23
|
default
|
28
24
|
else
|
@@ -31,12 +27,28 @@ module Capistrano
|
|
31
27
|
end
|
32
28
|
|
33
29
|
def response
|
34
|
-
@response
|
30
|
+
return @response if defined? @response
|
31
|
+
|
32
|
+
@response = (gets || "").chomp
|
35
33
|
end
|
36
|
-
|
34
|
+
|
35
|
+
def gets
|
36
|
+
if echo?
|
37
|
+
$stdin.gets
|
38
|
+
else
|
39
|
+
$stdin.noecho(&:gets).tap{ $stdout.print "\n" }
|
40
|
+
end
|
41
|
+
rescue Errno::EIO
|
42
|
+
# when stdio gets closed
|
43
|
+
end
|
44
|
+
|
37
45
|
def question
|
38
46
|
I18n.t(:question, key: key, default_value: default, scope: :capistrano)
|
39
47
|
end
|
48
|
+
|
49
|
+
def echo?
|
50
|
+
(options || {}).fetch(:echo, true)
|
51
|
+
end
|
40
52
|
end
|
41
53
|
end
|
42
54
|
end
|
@@ -11,11 +11,13 @@ module Capistrano
|
|
11
11
|
|
12
12
|
def add_roles(roles)
|
13
13
|
Array(roles).each { |role| add_role(role) }
|
14
|
+
self
|
14
15
|
end
|
15
16
|
alias roles= add_roles
|
16
17
|
|
17
18
|
def add_role(role)
|
18
19
|
roles.add role.to_sym
|
20
|
+
self
|
19
21
|
end
|
20
22
|
|
21
23
|
def has_role?(role)
|
@@ -40,11 +42,9 @@ module Capistrano
|
|
40
42
|
@properties ||= Properties.new
|
41
43
|
end
|
42
44
|
|
43
|
-
def
|
44
|
-
@netssh_options ||=
|
45
|
+
def netssh_options
|
46
|
+
@netssh_options ||= super.merge( fetch(:ssh_options) || {} )
|
45
47
|
end
|
46
|
-
alias_method :netssh_options_without_options, :netssh_options
|
47
|
-
alias_method :netssh_options, :netssh_options_with_options
|
48
48
|
|
49
49
|
def roles_array
|
50
50
|
roles.to_a
|
@@ -71,7 +71,16 @@ module Capistrano
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def set(key, value)
|
74
|
-
@properties[key]
|
74
|
+
pval = @properties[key]
|
75
|
+
if pval.is_a? Hash and value.is_a? Hash
|
76
|
+
pval.merge!(value)
|
77
|
+
elsif pval.is_a? Set and value.is_a? Set
|
78
|
+
pval.merge(value)
|
79
|
+
elsif pval.is_a? Array and value.is_a? Array
|
80
|
+
pval.concat value
|
81
|
+
else
|
82
|
+
@properties[key] = value
|
83
|
+
end
|
75
84
|
end
|
76
85
|
|
77
86
|
def fetch(key)
|