capistrano 2.0.0 → 2.15.2
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 +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/CHANGELOG +715 -18
- data/Gemfile +12 -0
- data/README.md +94 -0
- data/Rakefile +11 -0
- data/bin/cap +0 -0
- data/bin/capify +37 -22
- data/capistrano.gemspec +40 -0
- data/lib/capistrano/callback.rb +5 -1
- data/lib/capistrano/cli/execute.rb +10 -7
- data/lib/capistrano/cli/help.rb +39 -16
- data/lib/capistrano/cli/help.txt +44 -16
- data/lib/capistrano/cli/options.rb +71 -11
- data/lib/capistrano/cli/ui.rb +13 -1
- data/lib/capistrano/cli.rb +5 -5
- data/lib/capistrano/command.rb +215 -58
- data/lib/capistrano/configuration/actions/file_transfer.rb +29 -14
- data/lib/capistrano/configuration/actions/inspect.rb +3 -3
- data/lib/capistrano/configuration/actions/invocation.rb +212 -22
- data/lib/capistrano/configuration/alias_task.rb +26 -0
- data/lib/capistrano/configuration/callbacks.rb +26 -27
- data/lib/capistrano/configuration/connections.rb +130 -52
- data/lib/capistrano/configuration/execution.rb +34 -18
- data/lib/capistrano/configuration/loading.rb +91 -6
- data/lib/capistrano/configuration/log_formatters.rb +75 -0
- data/lib/capistrano/configuration/namespaces.rb +45 -12
- data/lib/capistrano/configuration/roles.rb +28 -2
- data/lib/capistrano/configuration/servers.rb +51 -10
- data/lib/capistrano/configuration/variables.rb +3 -3
- data/lib/capistrano/configuration.rb +20 -4
- data/lib/capistrano/errors.rb +12 -8
- data/lib/capistrano/ext/multistage.rb +62 -0
- data/lib/capistrano/ext/string.rb +5 -0
- data/lib/capistrano/extensions.rb +1 -1
- data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
- data/lib/capistrano/logger.rb +112 -5
- data/lib/capistrano/processable.rb +55 -0
- data/lib/capistrano/recipes/compat.rb +2 -2
- data/lib/capistrano/recipes/deploy/assets.rb +185 -0
- data/lib/capistrano/recipes/deploy/dependencies.rb +2 -2
- data/lib/capistrano/recipes/deploy/local_dependency.rb +10 -2
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +54 -2
- data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
- data/lib/capistrano/recipes/deploy/scm/base.rb +31 -11
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +14 -14
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +10 -8
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +12 -1
- data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +23 -15
- data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +54 -28
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +35 -17
- data/lib/capistrano/recipes/deploy/scm.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/base.rb +32 -4
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +238 -43
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +11 -1
- data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
- data/lib/capistrano/recipes/deploy/strategy.rb +1 -1
- data/lib/capistrano/recipes/deploy.rb +265 -123
- data/lib/capistrano/recipes/standard.rb +1 -1
- data/lib/capistrano/role.rb +102 -0
- data/lib/capistrano/server_definition.rb +6 -1
- data/lib/capistrano/shell.rb +30 -33
- data/lib/capistrano/ssh.rb +46 -60
- data/lib/capistrano/task_definition.rb +16 -8
- data/lib/capistrano/transfer.rb +218 -0
- data/lib/capistrano/version.rb +6 -17
- data/lib/capistrano.rb +4 -1
- data/test/cli/execute_test.rb +3 -3
- data/test/cli/help_test.rb +33 -7
- data/test/cli/options_test.rb +109 -6
- data/test/cli/ui_test.rb +2 -2
- data/test/cli_test.rb +3 -3
- data/test/command_test.rb +144 -124
- data/test/configuration/actions/file_transfer_test.rb +41 -20
- data/test/configuration/actions/inspect_test.rb +21 -7
- data/test/configuration/actions/invocation_test.rb +91 -30
- data/test/configuration/alias_task_test.rb +118 -0
- data/test/configuration/callbacks_test.rb +41 -46
- data/test/configuration/connections_test.rb +187 -36
- data/test/configuration/execution_test.rb +18 -2
- data/test/configuration/loading_test.rb +17 -4
- data/test/configuration/namespace_dsl_test.rb +54 -5
- data/test/configuration/roles_test.rb +114 -4
- data/test/configuration/servers_test.rb +97 -4
- data/test/configuration/variables_test.rb +12 -2
- data/test/configuration_test.rb +9 -13
- data/test/deploy/local_dependency_test.rb +76 -0
- data/test/deploy/remote_dependency_test.rb +146 -0
- data/test/deploy/scm/accurev_test.rb +23 -0
- data/test/deploy/scm/base_test.rb +1 -1
- data/test/deploy/scm/bzr_test.rb +51 -0
- data/test/deploy/scm/darcs_test.rb +37 -0
- data/test/deploy/scm/git_test.rb +221 -0
- data/test/deploy/scm/mercurial_test.rb +134 -0
- data/test/deploy/scm/none_test.rb +35 -0
- data/test/deploy/scm/perforce_test.rb +23 -0
- data/test/deploy/scm/subversion_test.rb +40 -0
- data/test/deploy/strategy/copy_test.rb +240 -26
- data/test/extensions_test.rb +2 -2
- data/test/logger_formatting_test.rb +149 -0
- data/test/logger_test.rb +13 -2
- data/test/recipes_test.rb +25 -0
- data/test/role_test.rb +11 -0
- data/test/server_definition_test.rb +15 -2
- data/test/shell_test.rb +33 -1
- data/test/ssh_test.rb +40 -24
- data/test/task_definition_test.rb +18 -2
- data/test/transfer_test.rb +168 -0
- data/test/utils.rb +27 -33
- metadata +215 -102
- data/MIT-LICENSE +0 -20
- data/README +0 -43
- data/examples/sample.rb +0 -14
- data/lib/capistrano/gateway.rb +0 -131
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/recipes/upgrade.rb +0 -33
- data/lib/capistrano/upload.rb +0 -146
- data/test/gateway_test.rb +0 -167
- data/test/upload_test.rb +0 -131
- data/test/version_test.rb +0 -24
data/lib/capistrano/cli/ui.rb
CHANGED
|
@@ -22,7 +22,19 @@ module Capistrano
|
|
|
22
22
|
def password_prompt(prompt="Password: ")
|
|
23
23
|
ui.ask(prompt) { |q| q.echo = false }
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
# Debug mode prompt
|
|
27
|
+
def debug_prompt(cmd)
|
|
28
|
+
ui.say("Preparing to execute command: #{cmd}")
|
|
29
|
+
prompt = "Execute ([Yes], No, Abort) "
|
|
30
|
+
ui.ask("#{prompt}? ") do |q|
|
|
31
|
+
q.overwrite = false
|
|
32
|
+
q.default = 'y'
|
|
33
|
+
q.validate = /(y(es)?)|(no?)|(a(bort)?|\n)/i
|
|
34
|
+
q.responses[:not_valid] = prompt
|
|
35
|
+
end
|
|
36
|
+
end
|
|
25
37
|
end
|
|
26
38
|
end
|
|
27
39
|
end
|
|
28
|
-
end
|
|
40
|
+
end
|
data/lib/capistrano/cli.rb
CHANGED
|
@@ -17,17 +17,16 @@ module Capistrano
|
|
|
17
17
|
# different set of parameters (such as when embedded cap in a program):
|
|
18
18
|
#
|
|
19
19
|
# require 'capistrano/cli'
|
|
20
|
-
# Capistrano::CLI.parse(%
|
|
20
|
+
# Capistrano::CLI.parse(%W(-vvvv -f config/deploy update_code)).execute!
|
|
21
21
|
#
|
|
22
22
|
# Note that you can also embed cap directly by creating a new Configuration
|
|
23
|
-
# instance and setting it up,
|
|
24
|
-
#
|
|
25
|
-
# class directly, would look like:
|
|
23
|
+
# instance and setting it up, The above snippet, redone using the
|
|
24
|
+
# Configuration class directly, would look like:
|
|
26
25
|
#
|
|
27
26
|
# require 'capistrano'
|
|
28
27
|
# require 'capistrano/cli'
|
|
29
28
|
# config = Capistrano::Configuration.new
|
|
30
|
-
# config.
|
|
29
|
+
# config.logger.level = Capistrano::Logger::TRACE
|
|
31
30
|
# config.set(:password) { Capistrano::CLI.password_prompt }
|
|
32
31
|
# config.load "config/deploy"
|
|
33
32
|
# config.update_code
|
|
@@ -43,5 +42,6 @@ module Capistrano
|
|
|
43
42
|
# Mix-in the actual behavior
|
|
44
43
|
include Execute, Options, UI
|
|
45
44
|
include Help # needs to be included last, because it overrides some methods
|
|
45
|
+
|
|
46
46
|
end
|
|
47
47
|
end
|
data/lib/capistrano/command.rb
CHANGED
|
@@ -1,14 +1,143 @@
|
|
|
1
|
+
require 'benchmark'
|
|
1
2
|
require 'capistrano/errors'
|
|
3
|
+
require 'capistrano/processable'
|
|
2
4
|
|
|
3
5
|
module Capistrano
|
|
4
6
|
|
|
5
7
|
# This class encapsulates a single command to be executed on a set of remote
|
|
6
8
|
# machines, in parallel.
|
|
7
9
|
class Command
|
|
8
|
-
|
|
10
|
+
include Processable
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
class Tree
|
|
13
|
+
attr_reader :configuration
|
|
14
|
+
attr_reader :branches
|
|
15
|
+
attr_reader :fallback
|
|
16
|
+
|
|
17
|
+
include Enumerable
|
|
18
|
+
|
|
19
|
+
class Branch
|
|
20
|
+
attr_accessor :command, :callback, :condition
|
|
21
|
+
attr_reader :options
|
|
22
|
+
|
|
23
|
+
def initialize(command, options, callback)
|
|
24
|
+
@command = command.strip.gsub(/\r?\n/, "\\\n")
|
|
25
|
+
@callback = callback || Capistrano::Configuration.default_io_proc
|
|
26
|
+
@options = options
|
|
27
|
+
@skip = false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def last?
|
|
31
|
+
options[:last]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def skip?
|
|
35
|
+
@skip
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def skip!
|
|
39
|
+
@skip = true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def match(server)
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_s(parallel=false)
|
|
47
|
+
if parallel && @condition
|
|
48
|
+
"#{condition.inspect} :: #{command.inspect}"
|
|
49
|
+
else
|
|
50
|
+
command.inspect
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class ConditionBranch < Branch
|
|
56
|
+
attr_accessor :configuration
|
|
57
|
+
|
|
58
|
+
class Evaluator
|
|
59
|
+
attr_reader :configuration, :condition, :server
|
|
60
|
+
|
|
61
|
+
def initialize(config, condition, server)
|
|
62
|
+
@configuration = config
|
|
63
|
+
@condition = condition
|
|
64
|
+
@server = server
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def in?(role)
|
|
68
|
+
configuration.roles[role].include?(server)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def result
|
|
72
|
+
eval(condition, binding)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def method_missing(sym, *args, &block)
|
|
76
|
+
if server.respond_to?(sym)
|
|
77
|
+
server.send(sym, *args, &block)
|
|
78
|
+
elsif configuration.respond_to?(sym)
|
|
79
|
+
configuration.send(sym, *args, &block)
|
|
80
|
+
else
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def initialize(configuration, condition, command, options, callback)
|
|
87
|
+
@configuration = configuration
|
|
88
|
+
@condition = condition
|
|
89
|
+
super(command, options, callback)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def match(server)
|
|
93
|
+
Evaluator.new(configuration, condition, server).result
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class ElseBranch < Branch
|
|
98
|
+
def initialize(command, options, callback)
|
|
99
|
+
@condition = "else"
|
|
100
|
+
super(command, options, callback)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def initialize(config)
|
|
105
|
+
@configuration = config
|
|
106
|
+
@branches = []
|
|
107
|
+
yield self if block_given?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def when(condition, command, options={}, &block)
|
|
111
|
+
branches << ConditionBranch.new(configuration, condition, command, options, block)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def else(command, &block)
|
|
115
|
+
@fallback = ElseBranch.new(command, {}, block)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def branches_for(server)
|
|
119
|
+
seen_last = false
|
|
120
|
+
matches = branches.select do |branch|
|
|
121
|
+
success = !seen_last && !branch.skip? && branch.match(server)
|
|
122
|
+
seen_last = success && branch.last?
|
|
123
|
+
success
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
matches << fallback if matches.empty? && fallback
|
|
127
|
+
return matches
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def each
|
|
131
|
+
branches.each { |branch| yield branch }
|
|
132
|
+
yield fallback if fallback
|
|
133
|
+
return self
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
attr_reader :tree, :sessions, :options
|
|
138
|
+
|
|
139
|
+
def self.process(tree, sessions, options={})
|
|
140
|
+
new(tree, sessions, options).process!
|
|
12
141
|
end
|
|
13
142
|
|
|
14
143
|
# Instantiates a new command object. The +command+ must be a string
|
|
@@ -20,11 +149,16 @@ module Capistrano
|
|
|
20
149
|
# * +data+: (optional), a string to be sent to the command via it's stdin
|
|
21
150
|
# * +env+: (optional), a string or hash to be interpreted as environment
|
|
22
151
|
# variables that should be defined for this command invocation.
|
|
23
|
-
def initialize(
|
|
24
|
-
|
|
152
|
+
def initialize(tree, sessions, options={}, &block)
|
|
153
|
+
if String === tree
|
|
154
|
+
tree = Tree.new(nil) { |t| t.else(tree, &block) }
|
|
155
|
+
elsif block
|
|
156
|
+
raise ArgumentError, "block given with tree argument"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
@tree = tree
|
|
25
160
|
@sessions = sessions
|
|
26
161
|
@options = options
|
|
27
|
-
@callback = block
|
|
28
162
|
@channels = open_channels
|
|
29
163
|
end
|
|
30
164
|
|
|
@@ -32,29 +166,19 @@ module Capistrano
|
|
|
32
166
|
# fails (non-zero return code) on any of the hosts, this will raise a
|
|
33
167
|
# Capistrano::CommandError.
|
|
34
168
|
def process!
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@channels.each do |ch|
|
|
39
|
-
next if ch[:closed]
|
|
40
|
-
active += 1
|
|
41
|
-
ch.connection.process(true)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
break if active == 0
|
|
45
|
-
if Time.now - since >= 1
|
|
46
|
-
since = Time.now
|
|
47
|
-
@channels.each { |ch| ch.connection.ping! }
|
|
169
|
+
elapsed = Benchmark.realtime do
|
|
170
|
+
loop do
|
|
171
|
+
break unless process_iteration { @channels.any? { |ch| !ch[:closed] } }
|
|
48
172
|
end
|
|
49
|
-
sleep 0.01 # a brief respite, to keep the CPU from going crazy
|
|
50
173
|
end
|
|
51
174
|
|
|
52
|
-
logger.trace "command finished" if logger
|
|
175
|
+
logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
|
|
53
176
|
|
|
54
177
|
if (failed = @channels.select { |ch| ch[:status] != 0 }).any?
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
error
|
|
178
|
+
commands = failed.inject({}) { |map, ch| (map[ch[:command]] ||= []) << ch[:server]; map }
|
|
179
|
+
message = commands.map { |command, list| "#{command.inspect} on #{list.join(',')}" }.join("; ")
|
|
180
|
+
error = CommandError.new("failed: #{message}")
|
|
181
|
+
error.hosts = commands.values.flatten
|
|
58
182
|
raise error
|
|
59
183
|
end
|
|
60
184
|
|
|
@@ -77,51 +201,84 @@ module Capistrano
|
|
|
77
201
|
|
|
78
202
|
def open_channels
|
|
79
203
|
sessions.map do |session|
|
|
80
|
-
session.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
channel.on_success do |ch|
|
|
89
|
-
logger.trace "executing command", ch[:server] if logger
|
|
90
|
-
escaped = replace_placeholders(command, ch).gsub(/[$\\`"]/) { |m| "\\#{m}" }
|
|
91
|
-
command_line = [environment, options[:shell] || "sh", "-c", "\"#{escaped}\""].compact.join(" ")
|
|
92
|
-
ch.exec(command_line)
|
|
93
|
-
ch.send_data(options[:data]) if options[:data]
|
|
94
|
-
end
|
|
204
|
+
server = session.xserver
|
|
205
|
+
tree.branches_for(server).map do |branch|
|
|
206
|
+
session.open_channel do |channel|
|
|
207
|
+
channel[:server] = server
|
|
208
|
+
channel[:host] = server.host
|
|
209
|
+
channel[:options] = options
|
|
210
|
+
channel[:branch] = branch
|
|
95
211
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
logger.important "could not open channel", ch[:server] if logger
|
|
101
|
-
ch.close
|
|
102
|
-
end
|
|
212
|
+
request_pty_if_necessary(channel) do |ch, success|
|
|
213
|
+
if success
|
|
214
|
+
logger.trace "executing command", ch[:server] if logger
|
|
215
|
+
cmd = replace_placeholders(channel[:branch].command, ch)
|
|
103
216
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
217
|
+
if options[:shell] == false
|
|
218
|
+
shell = nil
|
|
219
|
+
else
|
|
220
|
+
shell = "#{options[:shell] || "sh"} -c"
|
|
221
|
+
cmd = cmd.gsub(/'/) { |m| "'\\''" }
|
|
222
|
+
cmd = "'#{cmd}'"
|
|
223
|
+
end
|
|
107
224
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
end
|
|
225
|
+
command_line = [environment, shell, cmd].compact.join(" ")
|
|
226
|
+
ch[:command] = command_line
|
|
111
227
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
228
|
+
ch.exec(command_line)
|
|
229
|
+
ch.send_data(options[:data]) if options[:data]
|
|
230
|
+
ch.eof! if options[:eof]
|
|
231
|
+
else
|
|
232
|
+
# just log it, don't actually raise an exception, since the
|
|
233
|
+
# process method will see that the status is not zero and will
|
|
234
|
+
# raise an exception then.
|
|
235
|
+
logger.important "could not open channel", ch[:server] if logger
|
|
236
|
+
ch.close
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
channel.on_data do |ch, data|
|
|
241
|
+
ch[:branch].callback[ch, :out, data]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
channel.on_extended_data do |ch, type, data|
|
|
245
|
+
ch[:branch].callback[ch, :err, data]
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
channel.on_request("exit-status") do |ch, data|
|
|
249
|
+
ch[:status] = data.read_long
|
|
250
|
+
end
|
|
115
251
|
|
|
116
|
-
|
|
117
|
-
|
|
252
|
+
channel.on_request("exit-signal") do |ch, data|
|
|
253
|
+
if logger
|
|
254
|
+
exit_signal = data.read_string
|
|
255
|
+
logger.important "command received signal #{exit_signal}", ch[:server]
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
channel.on_close do |ch|
|
|
260
|
+
ch[:closed] = true
|
|
261
|
+
end
|
|
118
262
|
end
|
|
119
263
|
end
|
|
264
|
+
end.flatten
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def request_pty_if_necessary(channel)
|
|
268
|
+
if options[:pty]
|
|
269
|
+
channel.request_pty do |ch, success|
|
|
270
|
+
yield ch, success
|
|
271
|
+
end
|
|
272
|
+
else
|
|
273
|
+
yield channel, true
|
|
120
274
|
end
|
|
121
275
|
end
|
|
122
276
|
|
|
123
277
|
def replace_placeholders(command, channel)
|
|
124
|
-
|
|
278
|
+
roles = @tree.configuration && @tree.configuration.role_names_for_host(channel[:server])
|
|
279
|
+
command = command.gsub(/\$CAPISTRANO:HOST\$/, channel[:host])
|
|
280
|
+
command.gsub!(/\$CAPISTRANO:HOSTROLES\$/, roles.join(',')) if roles
|
|
281
|
+
command
|
|
125
282
|
end
|
|
126
283
|
|
|
127
284
|
# prepare a space-separated sequence of variables assignments
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'capistrano/
|
|
1
|
+
require 'capistrano/transfer'
|
|
2
2
|
|
|
3
3
|
module Capistrano
|
|
4
4
|
class Configuration
|
|
@@ -9,23 +9,38 @@ module Capistrano
|
|
|
9
9
|
# by the current task. If <tt>:mode</tt> is specified it is used to
|
|
10
10
|
# set the mode on the file.
|
|
11
11
|
def put(data, path, options={})
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Upload.process(targets, path, :data => data, :mode => options[:mode], :logger => logger)
|
|
15
|
-
end
|
|
12
|
+
opts = options.dup
|
|
13
|
+
upload(StringIO.new(data), path, opts)
|
|
16
14
|
end
|
|
17
|
-
|
|
18
|
-
# Get file remote_path from FIRST server
|
|
15
|
+
|
|
16
|
+
# Get file remote_path from FIRST server targeted by
|
|
19
17
|
# the current task and transfer it to local machine as path.
|
|
20
18
|
#
|
|
21
19
|
# get "#{deploy_to}/current/log/production.log", "log/production.log.web"
|
|
22
|
-
def get(remote_path, path, options
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
def get(remote_path, path, options={}, &block)
|
|
21
|
+
download(remote_path, path, options.merge(:once => true), &block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def upload(from, to, options={}, &block)
|
|
25
|
+
mode = options.delete(:mode)
|
|
26
|
+
transfer(:up, from, to, options, &block)
|
|
27
|
+
if mode
|
|
28
|
+
mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s
|
|
29
|
+
run "chmod #{mode} #{to}", options
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def download(from, to, options={}, &block)
|
|
34
|
+
transfer(:down, from, to, options, &block)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def transfer(direction, from, to, options={}, &block)
|
|
38
|
+
if dry_run
|
|
39
|
+
return logger.debug "transfering: #{[direction, from, to] * ', '}"
|
|
40
|
+
end
|
|
41
|
+
execute_on_servers(options) do |servers|
|
|
42
|
+
targets = servers.map { |s| sessions[s] }
|
|
43
|
+
Transfer.process(direction, from, to, targets, options.merge(:logger => logger), &block)
|
|
29
44
|
end
|
|
30
45
|
end
|
|
31
46
|
|
|
@@ -20,7 +20,7 @@ module Capistrano
|
|
|
20
20
|
# stream "tail -f #{shared_path}/log/fastcgi.crash.log"
|
|
21
21
|
# end
|
|
22
22
|
def stream(command, options={})
|
|
23
|
-
invoke_command(command, options) do |ch, stream, out|
|
|
23
|
+
invoke_command(command, options.merge(:eof => !command.include?(sudo))) do |ch, stream, out|
|
|
24
24
|
puts out if stream == :out
|
|
25
25
|
warn "[err :: #{ch[:server]}] #{out}" if stream == :err
|
|
26
26
|
end
|
|
@@ -31,10 +31,10 @@ module Capistrano
|
|
|
31
31
|
# string. The command is invoked via #invoke_command.
|
|
32
32
|
def capture(command, options={})
|
|
33
33
|
output = ""
|
|
34
|
-
invoke_command(command, options.merge(:once => true)) do |ch, stream, data|
|
|
34
|
+
invoke_command(command, options.merge(:once => true, :eof => !command.include?(sudo))) do |ch, stream, data|
|
|
35
35
|
case stream
|
|
36
36
|
when :out then output << data
|
|
37
|
-
when :err then
|
|
37
|
+
when :err then warn "[err :: #{ch[:server]}] #{data}"
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
output
|