capistrano 2.4.3 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +39 -0
- data/capistrano.gemspec +5 -5
- data/lib/capistrano/cli/execute.rb +1 -0
- data/lib/capistrano/cli/help.txt +6 -0
- data/lib/capistrano/cli/options.rb +26 -0
- data/lib/capistrano/cli/ui.rb +0 -1
- data/lib/capistrano/command.rb +175 -47
- data/lib/capistrano/configuration.rb +2 -1
- data/lib/capistrano/configuration/actions/invocation.rb +38 -7
- data/lib/capistrano/configuration/connections.rb +12 -6
- data/lib/capistrano/configuration/execution.rb +2 -1
- data/lib/capistrano/configuration/servers.rb +5 -4
- data/lib/capistrano/recipes/deploy.rb +45 -22
- data/lib/capistrano/recipes/deploy/local_dependency.rb +7 -3
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +2 -1
- data/lib/capistrano/recipes/deploy/scm/none.rb +1 -1
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +10 -1
- data/lib/capistrano/role.rb +4 -0
- data/lib/capistrano/version.rb +2 -2
- data/test/cli/execute_test.rb +1 -1
- data/test/cli/options_test.rb +84 -0
- data/test/command_test.rb +38 -41
- data/test/configuration/actions/invocation_test.rb +12 -16
- data/test/configuration/connections_test.rb +30 -9
- data/test/configuration/execution_test.rb +16 -0
- data/test/configuration/servers_test.rb +15 -0
- data/test/configuration_test.rb +8 -1
- data/test/deploy/local_dependency_test.rb +15 -12
- data/test/deploy/scm/none_test.rb +35 -0
- data/test/deploy/strategy/copy_test.rb +13 -0
- metadata +3 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,42 @@
|
|
1
|
+
== 2.5.0 / August 28, 2008
|
2
|
+
|
3
|
+
* Allow :gateway to be set to an array, in which case a chain of tunnels is created [Kerry Buckley]
|
4
|
+
|
5
|
+
* Allow HOSTS spec to override even non-existent roles [Mike Bailey]
|
6
|
+
|
7
|
+
* Sort releases via "ls -xt" instead of "ls -x" to allow for custom release names [Yan Pritzker]
|
8
|
+
|
9
|
+
* Convert arguments to -s and -S into integers, booleans, etc. based on whether the arguments appear to be those types [Jamis Buck]
|
10
|
+
|
11
|
+
* Add descriptions of -n and -d to the verbose help text [Jamis Buck]
|
12
|
+
|
13
|
+
* Make rollbacks work with processes that need the current directory to be valid in order to restart properly (e.g. mongrel_rails) [Jamis Buck]
|
14
|
+
|
15
|
+
* Rename deploy:rollback_code to deploy:rollback:code [Jamis Buck]
|
16
|
+
|
17
|
+
* Added parallel() helper for executing multiple different commands in parallel [Jamis Buck]
|
18
|
+
|
19
|
+
* Make sure a task only uses the last on_rollback block, once, on rollback [Jamis Buck]
|
20
|
+
|
21
|
+
* Add :shared_children variable to customize which subdirectories are created by deploy:setup [Jonathan Share]
|
22
|
+
|
23
|
+
* Allow filename globbing in copy_exclude setting for the copy strategy [Jonathan Share]
|
24
|
+
|
25
|
+
* Allow remote_cache strategy to use copy_exclude settings (requires rsync) [Lewis Mackenzie]
|
26
|
+
|
27
|
+
* Make None SCM module work in Windows [Carlos Kozuszko]
|
28
|
+
|
29
|
+
* Recognize mingw as a Windows platform [Carlos Kozuszko]
|
30
|
+
|
31
|
+
* Fixed failing tests in Windows [Carlos Kozuszko]
|
32
|
+
|
33
|
+
* Made :scm_auth_cache control whether password option is emitted in subversion module [Brendan Schwartz]
|
34
|
+
|
35
|
+
* Fixed timestamp bug in CVS module [Jørgen Fjeld]
|
36
|
+
|
37
|
+
* Added -n/--dry-run switch, to display but not execute remote tasks [Paul Gross]
|
38
|
+
|
39
|
+
|
1
40
|
== 2.4.3 / June 28, 2008
|
2
41
|
|
3
42
|
* Fix gem dependencies so gem actually understands them [Jamis Buck]
|
data/capistrano.gemspec
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Capistrano-2.
|
2
|
+
# Gem::Specification for Capistrano-2.5.0
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{capistrano}
|
7
|
-
s.version = "2.
|
7
|
+
s.version = "2.5.0"
|
8
8
|
|
9
9
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
10
|
s.authors = ["Jamis Buck"]
|
11
|
-
s.date = %q{2008-
|
11
|
+
s.date = %q{2008-08-28}
|
12
12
|
s.description = %q{Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.}
|
13
13
|
s.email = %q{jamis@jamisbuck.org}
|
14
14
|
s.executables = ["cap", "capify"]
|
15
15
|
s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/capistrano/callback.rb", "lib/capistrano/cli/execute.rb", "lib/capistrano/cli/help.rb", "lib/capistrano/cli/help.txt", "lib/capistrano/cli/options.rb", "lib/capistrano/cli/ui.rb", "lib/capistrano/cli.rb", "lib/capistrano/command.rb", "lib/capistrano/configuration/actions/file_transfer.rb", "lib/capistrano/configuration/actions/inspect.rb", "lib/capistrano/configuration/actions/invocation.rb", "lib/capistrano/configuration/callbacks.rb", "lib/capistrano/configuration/connections.rb", "lib/capistrano/configuration/execution.rb", "lib/capistrano/configuration/loading.rb", "lib/capistrano/configuration/namespaces.rb", "lib/capistrano/configuration/roles.rb", "lib/capistrano/configuration/servers.rb", "lib/capistrano/configuration/variables.rb", "lib/capistrano/configuration.rb", "lib/capistrano/errors.rb", "lib/capistrano/extensions.rb", "lib/capistrano/logger.rb", "lib/capistrano/processable.rb", "lib/capistrano/recipes/compat.rb", "lib/capistrano/recipes/deploy/dependencies.rb", "lib/capistrano/recipes/deploy/local_dependency.rb", "lib/capistrano/recipes/deploy/remote_dependency.rb", "lib/capistrano/recipes/deploy/scm/accurev.rb", "lib/capistrano/recipes/deploy/scm/base.rb", "lib/capistrano/recipes/deploy/scm/bzr.rb", "lib/capistrano/recipes/deploy/scm/cvs.rb", "lib/capistrano/recipes/deploy/scm/darcs.rb", "lib/capistrano/recipes/deploy/scm/git.rb", "lib/capistrano/recipes/deploy/scm/mercurial.rb", "lib/capistrano/recipes/deploy/scm/none.rb", "lib/capistrano/recipes/deploy/scm/perforce.rb", "lib/capistrano/recipes/deploy/scm/subversion.rb", "lib/capistrano/recipes/deploy/scm.rb", "lib/capistrano/recipes/deploy/strategy/base.rb", "lib/capistrano/recipes/deploy/strategy/checkout.rb", "lib/capistrano/recipes/deploy/strategy/copy.rb", "lib/capistrano/recipes/deploy/strategy/export.rb", "lib/capistrano/recipes/deploy/strategy/remote.rb", "lib/capistrano/recipes/deploy/strategy/remote_cache.rb", "lib/capistrano/recipes/deploy/strategy.rb", "lib/capistrano/recipes/deploy/templates/maintenance.rhtml", "lib/capistrano/recipes/deploy.rb", "lib/capistrano/recipes/standard.rb", "lib/capistrano/recipes/templates/maintenance.rhtml", "lib/capistrano/recipes/upgrade.rb", "lib/capistrano/role.rb", "lib/capistrano/server_definition.rb", "lib/capistrano/shell.rb", "lib/capistrano/ssh.rb", "lib/capistrano/task_definition.rb", "lib/capistrano/transfer.rb", "lib/capistrano/version.rb", "lib/capistrano.rb", "README.rdoc"]
|
16
|
-
s.files = ["bin/cap", "bin/capify", "CHANGELOG.rdoc", "examples/sample.rb", "lib/capistrano/callback.rb", "lib/capistrano/cli/execute.rb", "lib/capistrano/cli/help.rb", "lib/capistrano/cli/help.txt", "lib/capistrano/cli/options.rb", "lib/capistrano/cli/ui.rb", "lib/capistrano/cli.rb", "lib/capistrano/command.rb", "lib/capistrano/configuration/actions/file_transfer.rb", "lib/capistrano/configuration/actions/inspect.rb", "lib/capistrano/configuration/actions/invocation.rb", "lib/capistrano/configuration/callbacks.rb", "lib/capistrano/configuration/connections.rb", "lib/capistrano/configuration/execution.rb", "lib/capistrano/configuration/loading.rb", "lib/capistrano/configuration/namespaces.rb", "lib/capistrano/configuration/roles.rb", "lib/capistrano/configuration/servers.rb", "lib/capistrano/configuration/variables.rb", "lib/capistrano/configuration.rb", "lib/capistrano/errors.rb", "lib/capistrano/extensions.rb", "lib/capistrano/logger.rb", "lib/capistrano/processable.rb", "lib/capistrano/recipes/compat.rb", "lib/capistrano/recipes/deploy/dependencies.rb", "lib/capistrano/recipes/deploy/local_dependency.rb", "lib/capistrano/recipes/deploy/remote_dependency.rb", "lib/capistrano/recipes/deploy/scm/accurev.rb", "lib/capistrano/recipes/deploy/scm/base.rb", "lib/capistrano/recipes/deploy/scm/bzr.rb", "lib/capistrano/recipes/deploy/scm/cvs.rb", "lib/capistrano/recipes/deploy/scm/darcs.rb", "lib/capistrano/recipes/deploy/scm/git.rb", "lib/capistrano/recipes/deploy/scm/mercurial.rb", "lib/capistrano/recipes/deploy/scm/none.rb", "lib/capistrano/recipes/deploy/scm/perforce.rb", "lib/capistrano/recipes/deploy/scm/subversion.rb", "lib/capistrano/recipes/deploy/scm.rb", "lib/capistrano/recipes/deploy/strategy/base.rb", "lib/capistrano/recipes/deploy/strategy/checkout.rb", "lib/capistrano/recipes/deploy/strategy/copy.rb", "lib/capistrano/recipes/deploy/strategy/export.rb", "lib/capistrano/recipes/deploy/strategy/remote.rb", "lib/capistrano/recipes/deploy/strategy/remote_cache.rb", "lib/capistrano/recipes/deploy/strategy.rb", "lib/capistrano/recipes/deploy/templates/maintenance.rhtml", "lib/capistrano/recipes/deploy.rb", "lib/capistrano/recipes/standard.rb", "lib/capistrano/recipes/templates/maintenance.rhtml", "lib/capistrano/recipes/upgrade.rb", "lib/capistrano/role.rb", "lib/capistrano/server_definition.rb", "lib/capistrano/shell.rb", "lib/capistrano/ssh.rb", "lib/capistrano/task_definition.rb", "lib/capistrano/transfer.rb", "lib/capistrano/version.rb", "lib/capistrano.rb", "Rakefile", "README.rdoc", "setup.rb", "test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/fixtures/cli_integration.rb", "test/fixtures/config.rb", "test/fixtures/custom.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb", "test/utils.rb", "Manifest", "capistrano.gemspec"]
|
16
|
+
s.files = ["bin/cap", "bin/capify", "CHANGELOG.rdoc", "examples/sample.rb", "lib/capistrano/callback.rb", "lib/capistrano/cli/execute.rb", "lib/capistrano/cli/help.rb", "lib/capistrano/cli/help.txt", "lib/capistrano/cli/options.rb", "lib/capistrano/cli/ui.rb", "lib/capistrano/cli.rb", "lib/capistrano/command.rb", "lib/capistrano/configuration/actions/file_transfer.rb", "lib/capistrano/configuration/actions/inspect.rb", "lib/capistrano/configuration/actions/invocation.rb", "lib/capistrano/configuration/callbacks.rb", "lib/capistrano/configuration/connections.rb", "lib/capistrano/configuration/execution.rb", "lib/capistrano/configuration/loading.rb", "lib/capistrano/configuration/namespaces.rb", "lib/capistrano/configuration/roles.rb", "lib/capistrano/configuration/servers.rb", "lib/capistrano/configuration/variables.rb", "lib/capistrano/configuration.rb", "lib/capistrano/errors.rb", "lib/capistrano/extensions.rb", "lib/capistrano/logger.rb", "lib/capistrano/processable.rb", "lib/capistrano/recipes/compat.rb", "lib/capistrano/recipes/deploy/dependencies.rb", "lib/capistrano/recipes/deploy/local_dependency.rb", "lib/capistrano/recipes/deploy/remote_dependency.rb", "lib/capistrano/recipes/deploy/scm/accurev.rb", "lib/capistrano/recipes/deploy/scm/base.rb", "lib/capistrano/recipes/deploy/scm/bzr.rb", "lib/capistrano/recipes/deploy/scm/cvs.rb", "lib/capistrano/recipes/deploy/scm/darcs.rb", "lib/capistrano/recipes/deploy/scm/git.rb", "lib/capistrano/recipes/deploy/scm/mercurial.rb", "lib/capistrano/recipes/deploy/scm/none.rb", "lib/capistrano/recipes/deploy/scm/perforce.rb", "lib/capistrano/recipes/deploy/scm/subversion.rb", "lib/capistrano/recipes/deploy/scm.rb", "lib/capistrano/recipes/deploy/strategy/base.rb", "lib/capistrano/recipes/deploy/strategy/checkout.rb", "lib/capistrano/recipes/deploy/strategy/copy.rb", "lib/capistrano/recipes/deploy/strategy/export.rb", "lib/capistrano/recipes/deploy/strategy/remote.rb", "lib/capistrano/recipes/deploy/strategy/remote_cache.rb", "lib/capistrano/recipes/deploy/strategy.rb", "lib/capistrano/recipes/deploy/templates/maintenance.rhtml", "lib/capistrano/recipes/deploy.rb", "lib/capistrano/recipes/standard.rb", "lib/capistrano/recipes/templates/maintenance.rhtml", "lib/capistrano/recipes/upgrade.rb", "lib/capistrano/role.rb", "lib/capistrano/server_definition.rb", "lib/capistrano/shell.rb", "lib/capistrano/ssh.rb", "lib/capistrano/task_definition.rb", "lib/capistrano/transfer.rb", "lib/capistrano/version.rb", "lib/capistrano.rb", "Rakefile", "README.rdoc", "setup.rb", "test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/fixtures/cli_integration.rb", "test/fixtures/config.rb", "test/fixtures/custom.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb", "test/utils.rb", "Manifest", "capistrano.gemspec", "test/deploy/scm/none_test.rb"]
|
17
17
|
s.has_rdoc = true
|
18
18
|
s.homepage = %q{http://www.capify.org}
|
19
19
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Capistrano", "--main", "README.rdoc"]
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.rubyforge_project = %q{capistrano}
|
22
22
|
s.rubygems_version = %q{1.2.0}
|
23
23
|
s.summary = %q{Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.}
|
24
|
-
s.test_files = ["test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb"]
|
24
|
+
s.test_files = ["test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/scm/none_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb"]
|
25
25
|
|
26
26
|
if s.respond_to? :specification_version then
|
27
27
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
data/lib/capistrano/cli/help.txt
CHANGED
@@ -10,6 +10,9 @@ The command-line interface to Capistrano is via the `cap' command.
|
|
10
10
|
|
11
11
|
The following options are understood:
|
12
12
|
|
13
|
+
<%= color '-d, --debug', :bold %>
|
14
|
+
Causes Capistrano to pause and prompt before executing any remote command, giving the user the option to either execute the command, skip the command, or abort execution entirely. This makes it a great way to troubleshoot tasks, or test custom tasks, by executing commands one at a time and checking the server to make sure they worked as expected before moving on to the next command. (Compare this to the --dry-run command.)
|
15
|
+
|
13
16
|
<%= color '-e, --explain TASK', :bold %>
|
14
17
|
Displays the extended description of the given task. Not all tasks will have an extended description, but for those that do, this can provide a wealth of additional usage information, such as describing environment variables or settings that can affect the execution of the task.
|
15
18
|
|
@@ -25,6 +28,9 @@ The following options are understood:
|
|
25
28
|
<%= color '-h, --help', :bold %>
|
26
29
|
Shows a brief summary of these options and exits.
|
27
30
|
|
31
|
+
<%= color '-n, --dry-run', :bold %>
|
32
|
+
Causes Capistrano to simply display each remote command, without executing it. In this sense it is similar to --debug, but without the prompt. Note that commands executed locally are still run--only remote commands are skipped.
|
33
|
+
|
28
34
|
<%= color '-p, --password', :bold %>
|
29
35
|
Normally, cap will prompt for the password on-demand, the first time it is needed. This can make it hard to walk away from Capistrano, since you might not know if it will prompt for a password down the road. In such cases, you can use the -p option to force cap to prompt for the password immediately.
|
30
36
|
|
@@ -53,6 +53,10 @@ module Capistrano
|
|
53
53
|
exit
|
54
54
|
end
|
55
55
|
|
56
|
+
opts.on("-n", "--dry-run",
|
57
|
+
"Prints out commands without running them."
|
58
|
+
) { |value| options[:dry_run] = true }
|
59
|
+
|
56
60
|
opts.on("-p", "--password",
|
57
61
|
"Immediately prompt for the password."
|
58
62
|
) { options[:password] = nil }
|
@@ -120,6 +124,8 @@ module Capistrano
|
|
120
124
|
|
121
125
|
option_parser.parse!(args)
|
122
126
|
|
127
|
+
coerce_variable_types!
|
128
|
+
|
123
129
|
# if no verbosity has been specified, be verbose
|
124
130
|
options[:verbose] = 3 if !options.has_key?(:verbose)
|
125
131
|
|
@@ -182,6 +188,26 @@ module Capistrano
|
|
182
188
|
"/"
|
183
189
|
end
|
184
190
|
|
191
|
+
def coerce_variable_types!
|
192
|
+
[:pre_vars, :vars].each do |collection|
|
193
|
+
options[collection].keys.each do |key|
|
194
|
+
options[collection][key] = coerce_variable(options[collection][key])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def coerce_variable(value)
|
200
|
+
case value
|
201
|
+
when /^"(.*)"$/ then $1
|
202
|
+
when /^'(.*)'$/ then $1
|
203
|
+
when /^\d+$/ then value.to_i
|
204
|
+
when /^\d+\.\d*$/ then value.to_f
|
205
|
+
when "true" then true
|
206
|
+
when "false" then false
|
207
|
+
when "nil" then nil
|
208
|
+
else value
|
209
|
+
end
|
210
|
+
end
|
185
211
|
end
|
186
212
|
end
|
187
213
|
end
|
data/lib/capistrano/cli/ui.rb
CHANGED
data/lib/capistrano/command.rb
CHANGED
@@ -8,10 +8,129 @@ module Capistrano
|
|
8
8
|
class Command
|
9
9
|
include Processable
|
10
10
|
|
11
|
-
|
11
|
+
class Tree
|
12
|
+
attr_reader :configuration
|
13
|
+
attr_reader :branches
|
14
|
+
attr_reader :fallback
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
include Enumerable
|
17
|
+
|
18
|
+
class Branch
|
19
|
+
attr_accessor :command, :callback
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
def initialize(command, options, callback)
|
23
|
+
@command = command.strip.gsub(/\r?\n/, "\\\n")
|
24
|
+
@callback = callback || Capistrano::Configuration.default_io_proc
|
25
|
+
@options = options
|
26
|
+
@skip = false
|
27
|
+
end
|
28
|
+
|
29
|
+
def last?
|
30
|
+
options[:last]
|
31
|
+
end
|
32
|
+
|
33
|
+
def skip?
|
34
|
+
@skip
|
35
|
+
end
|
36
|
+
|
37
|
+
def skip!
|
38
|
+
@skip = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def match(server)
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
command.inspect
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ConditionBranch < Branch
|
51
|
+
attr_accessor :configuration
|
52
|
+
attr_accessor :condition
|
53
|
+
|
54
|
+
class Evaluator
|
55
|
+
attr_reader :configuration, :condition, :server
|
56
|
+
|
57
|
+
def initialize(config, condition, server)
|
58
|
+
@configuration = config
|
59
|
+
@condition = condition
|
60
|
+
@server = server
|
61
|
+
end
|
62
|
+
|
63
|
+
def in?(role)
|
64
|
+
configuration.roles[role].include?(server)
|
65
|
+
end
|
66
|
+
|
67
|
+
def result
|
68
|
+
eval(condition, binding)
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_missing(sym, *args, &block)
|
72
|
+
if server.respond_to?(sym)
|
73
|
+
server.send(sym, *args, &block)
|
74
|
+
elsif configuration.respond_to?(sym)
|
75
|
+
configuration.send(sym, *args, &block)
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(configuration, condition, command, options, callback)
|
83
|
+
@configuration = configuration
|
84
|
+
@condition = condition
|
85
|
+
super(command, options, callback)
|
86
|
+
end
|
87
|
+
|
88
|
+
def match(server)
|
89
|
+
Evaluator.new(configuration, condition, server).result
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
"#{condition.inspect} :: #{command.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(config)
|
98
|
+
@configuration = config
|
99
|
+
@branches = []
|
100
|
+
yield self if block_given?
|
101
|
+
end
|
102
|
+
|
103
|
+
def when(condition, command, options={}, &block)
|
104
|
+
branches << ConditionBranch.new(configuration, condition, command, options, block)
|
105
|
+
end
|
106
|
+
|
107
|
+
def else(command, &block)
|
108
|
+
@fallback = Branch.new(command, {}, block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def branches_for(server)
|
112
|
+
seen_last = false
|
113
|
+
matches = branches.select do |branch|
|
114
|
+
success = !seen_last && !branch.skip? && branch.match(server)
|
115
|
+
seen_last = success && branch.last?
|
116
|
+
success
|
117
|
+
end
|
118
|
+
|
119
|
+
matches << fallback if matches.empty? && fallback
|
120
|
+
return matches
|
121
|
+
end
|
122
|
+
|
123
|
+
def each
|
124
|
+
branches.each { |branch| yield branch }
|
125
|
+
yield fallback if fallback
|
126
|
+
return self
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
attr_reader :tree, :sessions, :options
|
131
|
+
|
132
|
+
def self.process(tree, sessions, options={})
|
133
|
+
new(tree, sessions, options).process!
|
15
134
|
end
|
16
135
|
|
17
136
|
# Instantiates a new command object. The +command+ must be a string
|
@@ -23,11 +142,16 @@ module Capistrano
|
|
23
142
|
# * +data+: (optional), a string to be sent to the command via it's stdin
|
24
143
|
# * +env+: (optional), a string or hash to be interpreted as environment
|
25
144
|
# variables that should be defined for this command invocation.
|
26
|
-
def initialize(
|
27
|
-
|
145
|
+
def initialize(tree, sessions, options={}, &block)
|
146
|
+
if String === tree
|
147
|
+
tree = Tree.new(nil) { |t| t.else(tree, &block) }
|
148
|
+
elsif block
|
149
|
+
raise ArgumentError, "block given with tree argument"
|
150
|
+
end
|
151
|
+
|
152
|
+
@tree = tree
|
28
153
|
@sessions = sessions
|
29
154
|
@options = options
|
30
|
-
@callback = block
|
31
155
|
@channels = open_channels
|
32
156
|
end
|
33
157
|
|
@@ -42,9 +166,10 @@ module Capistrano
|
|
42
166
|
logger.trace "command finished" if logger
|
43
167
|
|
44
168
|
if (failed = @channels.select { |ch| ch[:status] != 0 }).any?
|
45
|
-
|
46
|
-
|
47
|
-
error
|
169
|
+
commands = failed.inject({}) { |map, ch| (map[ch[:command]] ||= []) << ch[:server]; map }
|
170
|
+
message = commands.map { |command, list| "#{command.inspect} on #{list.join(',')}" }.join("; ")
|
171
|
+
error = CommandError.new("failed: #{message}")
|
172
|
+
error.hosts = commands.values.flatten
|
48
173
|
raise error
|
49
174
|
end
|
50
175
|
|
@@ -67,56 +192,59 @@ module Capistrano
|
|
67
192
|
|
68
193
|
def open_channels
|
69
194
|
sessions.map do |session|
|
70
|
-
session.
|
71
|
-
|
195
|
+
server = session.xserver
|
196
|
+
tree.branches_for(server).map do |branch|
|
197
|
+
session.open_channel do |channel|
|
198
|
+
channel[:server] = server
|
199
|
+
channel[:host] = server.host
|
200
|
+
channel[:options] = options
|
201
|
+
channel[:branch] = branch
|
202
|
+
|
203
|
+
request_pty_if_necessary(channel) do |ch, success|
|
204
|
+
if success
|
205
|
+
logger.trace "executing command", ch[:server] if logger
|
206
|
+
cmd = replace_placeholders(channel[:branch].command, ch)
|
72
207
|
|
73
|
-
|
74
|
-
|
75
|
-
|
208
|
+
if options[:shell] == false
|
209
|
+
shell = nil
|
210
|
+
else
|
211
|
+
shell = "#{options[:shell] || "sh"} -c"
|
212
|
+
cmd = cmd.gsub(/[$\\`"]/) { |m| "\\#{m}" }
|
213
|
+
cmd = "\"#{cmd}\""
|
214
|
+
end
|
76
215
|
|
77
|
-
|
78
|
-
|
79
|
-
logger.trace "executing command", ch[:server] if logger
|
80
|
-
cmd = replace_placeholders(command, ch)
|
216
|
+
command_line = [environment, shell, cmd].compact.join(" ")
|
217
|
+
ch[:command] = command_line
|
81
218
|
|
82
|
-
|
83
|
-
|
219
|
+
ch.exec(command_line)
|
220
|
+
ch.send_data(options[:data]) if options[:data]
|
84
221
|
else
|
85
|
-
|
86
|
-
|
87
|
-
|
222
|
+
# just log it, don't actually raise an exception, since the
|
223
|
+
# process method will see that the status is not zero and will
|
224
|
+
# raise an exception then.
|
225
|
+
logger.important "could not open channel", ch[:server] if logger
|
226
|
+
ch.close
|
88
227
|
end
|
89
|
-
|
90
|
-
command_line = [environment, shell, cmd].compact.join(" ")
|
91
|
-
|
92
|
-
ch.exec(command_line)
|
93
|
-
ch.send_data(options[:data]) if options[:data]
|
94
|
-
else
|
95
|
-
# just log it, don't actually raise an exception, since the
|
96
|
-
# process method will see that the status is not zero and will
|
97
|
-
# raise an exception then.
|
98
|
-
logger.important "could not open channel", ch[:server] if logger
|
99
|
-
ch.close
|
100
228
|
end
|
101
|
-
end
|
102
229
|
|
103
|
-
|
104
|
-
|
105
|
-
|
230
|
+
channel.on_data do |ch, data|
|
231
|
+
ch[:branch].callback[ch, :out, data]
|
232
|
+
end
|
106
233
|
|
107
|
-
|
108
|
-
|
109
|
-
|
234
|
+
channel.on_extended_data do |ch, type, data|
|
235
|
+
ch[:branch].callback[ch, :err, data]
|
236
|
+
end
|
110
237
|
|
111
|
-
|
112
|
-
|
113
|
-
|
238
|
+
channel.on_request("exit-status") do |ch, data|
|
239
|
+
ch[:status] = data.read_long
|
240
|
+
end
|
114
241
|
|
115
|
-
|
116
|
-
|
242
|
+
channel.on_close do |ch|
|
243
|
+
ch[:closed] = true
|
244
|
+
end
|
117
245
|
end
|
118
246
|
end
|
119
|
-
end
|
247
|
+
end.flatten
|
120
248
|
end
|
121
249
|
|
122
250
|
def request_pty_if_necessary(channel)
|
@@ -19,10 +19,11 @@ module Capistrano
|
|
19
19
|
# define roles, and set configuration variables.
|
20
20
|
class Configuration
|
21
21
|
# The logger instance defined for this configuration.
|
22
|
-
attr_accessor :debug, :logger
|
22
|
+
attr_accessor :debug, :logger, :dry_run
|
23
23
|
|
24
24
|
def initialize #:nodoc:
|
25
25
|
@debug = false
|
26
|
+
@dry_run = false
|
26
27
|
@logger = Logger.new
|
27
28
|
end
|
28
29
|
|
@@ -26,6 +26,12 @@ module Capistrano
|
|
26
26
|
set :default_run_options, {}
|
27
27
|
end
|
28
28
|
|
29
|
+
def parallel(options={})
|
30
|
+
raise ArgumentError, "parallel() requires a block" unless block_given?
|
31
|
+
tree = Command::Tree.new(self) { |t| yield t }
|
32
|
+
run_tree(tree)
|
33
|
+
end
|
34
|
+
|
29
35
|
# Invokes the given command. If a +via+ key is given, it will be used
|
30
36
|
# to determine what method to use to invoke the command. It defaults
|
31
37
|
# to :run, but may be :sudo, or any other method that conforms to the
|
@@ -44,19 +50,35 @@ module Capistrano
|
|
44
50
|
# stdout), and the data that was received.
|
45
51
|
def run(cmd, options={}, &block)
|
46
52
|
block ||= self.class.default_io_proc
|
47
|
-
|
53
|
+
tree = Command::Tree.new(self) { |t| t.else(cmd, &block) }
|
54
|
+
run_tree(tree, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_tree(tree, options={})
|
58
|
+
if tree.branches.empty? && tree.fallback
|
59
|
+
logger.debug "executing #{tree.fallback}"
|
60
|
+
elsif tree.branches.any?
|
61
|
+
logger.debug "executing multiple commands in parallel"
|
62
|
+
tree.each do |branch|
|
63
|
+
logger.trace "-> #{branch}"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise ArgumentError, "attempt to execute without specifying a command"
|
67
|
+
end
|
48
68
|
|
49
|
-
return if debug && continue_execution(
|
69
|
+
return if dry_run || (debug && continue_execution(tree) == false)
|
50
70
|
|
51
71
|
options = add_default_command_options(options)
|
52
72
|
|
53
|
-
|
54
|
-
|
73
|
+
tree.each do |branch|
|
74
|
+
if branch.command.include?(sudo)
|
75
|
+
branch.callback = sudo_behavior_callback(branch.callback)
|
76
|
+
end
|
55
77
|
end
|
56
78
|
|
57
79
|
execute_on_servers(options) do |servers|
|
58
80
|
targets = servers.map { |s| sessions[s] }
|
59
|
-
Command.process(
|
81
|
+
Command.process(tree, targets, options.merge(:logger => logger))
|
60
82
|
end
|
61
83
|
end
|
62
84
|
|
@@ -145,8 +167,17 @@ module Capistrano
|
|
145
167
|
fetch(:sudo_prompt, "sudo password: ")
|
146
168
|
end
|
147
169
|
|
148
|
-
def continue_execution(
|
149
|
-
|
170
|
+
def continue_execution(tree)
|
171
|
+
if tree.branches.length == 1
|
172
|
+
continue_execution_for_branch(tree.branches.first)
|
173
|
+
else
|
174
|
+
tree.each { |branch| branch.skip! unless continue_execution_for_branch(branch) }
|
175
|
+
tree.any? { |branch| !branch.skip? }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def continue_execution_for_branch(branch)
|
180
|
+
case Capistrano::CLI.debug_prompt(branch)
|
150
181
|
when "y"
|
151
182
|
true
|
152
183
|
when "n"
|