capistrano-edge 2.5.6
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.
- data/CHANGELOG.rdoc +770 -0
- data/Manifest +104 -0
- data/README.rdoc +66 -0
- data/Rakefile +35 -0
- data/bin/cap +4 -0
- data/bin/capify +95 -0
- data/capistrano.gemspec +51 -0
- data/examples/sample.rb +14 -0
- data/lib/capistrano.rb +2 -0
- data/lib/capistrano/callback.rb +45 -0
- data/lib/capistrano/cli.rb +47 -0
- data/lib/capistrano/cli/execute.rb +84 -0
- data/lib/capistrano/cli/help.rb +125 -0
- data/lib/capistrano/cli/help.txt +75 -0
- data/lib/capistrano/cli/options.rb +224 -0
- data/lib/capistrano/cli/ui.rb +40 -0
- data/lib/capistrano/command.rb +283 -0
- data/lib/capistrano/configuration.rb +43 -0
- data/lib/capistrano/configuration/actions/file_transfer.rb +47 -0
- data/lib/capistrano/configuration/actions/inspect.rb +46 -0
- data/lib/capistrano/configuration/actions/invocation.rb +293 -0
- data/lib/capistrano/configuration/callbacks.rb +148 -0
- data/lib/capistrano/configuration/connections.rb +204 -0
- data/lib/capistrano/configuration/execution.rb +143 -0
- data/lib/capistrano/configuration/loading.rb +197 -0
- data/lib/capistrano/configuration/namespaces.rb +197 -0
- data/lib/capistrano/configuration/roles.rb +73 -0
- data/lib/capistrano/configuration/servers.rb +85 -0
- data/lib/capistrano/configuration/variables.rb +127 -0
- data/lib/capistrano/errors.rb +15 -0
- data/lib/capistrano/extensions.rb +57 -0
- data/lib/capistrano/logger.rb +59 -0
- data/lib/capistrano/processable.rb +53 -0
- data/lib/capistrano/recipes/compat.rb +32 -0
- data/lib/capistrano/recipes/deploy.rb +438 -0
- data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
- data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +105 -0
- data/lib/capistrano/recipes/deploy/scm.rb +19 -0
- data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
- data/lib/capistrano/recipes/deploy/scm/base.rb +196 -0
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +83 -0
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +152 -0
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +85 -0
- data/lib/capistrano/recipes/deploy/scm/git.rb +274 -0
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
- data/lib/capistrano/recipes/deploy/scm/none.rb +44 -0
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +138 -0
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
- data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
- data/lib/capistrano/recipes/deploy/strategy/base.rb +79 -0
- data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +210 -0
- data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +56 -0
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
- data/lib/capistrano/recipes/ext/rails-database-migrations.rb +50 -0
- data/lib/capistrano/recipes/ext/web-disable-enable.rb +40 -0
- data/lib/capistrano/recipes/standard.rb +37 -0
- data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
- data/lib/capistrano/recipes/upgrade.rb +33 -0
- data/lib/capistrano/role.rb +102 -0
- data/lib/capistrano/server_definition.rb +56 -0
- data/lib/capistrano/shell.rb +260 -0
- data/lib/capistrano/ssh.rb +99 -0
- data/lib/capistrano/task_definition.rb +70 -0
- data/lib/capistrano/transfer.rb +216 -0
- data/lib/capistrano/version.rb +18 -0
- data/setup.rb +1346 -0
- data/test/cli/execute_test.rb +132 -0
- data/test/cli/help_test.rb +165 -0
- data/test/cli/options_test.rb +317 -0
- data/test/cli/ui_test.rb +28 -0
- data/test/cli_test.rb +17 -0
- data/test/command_test.rb +286 -0
- data/test/configuration/actions/file_transfer_test.rb +61 -0
- data/test/configuration/actions/inspect_test.rb +65 -0
- data/test/configuration/actions/invocation_test.rb +224 -0
- data/test/configuration/callbacks_test.rb +220 -0
- data/test/configuration/connections_test.rb +349 -0
- data/test/configuration/execution_test.rb +175 -0
- data/test/configuration/loading_test.rb +132 -0
- data/test/configuration/namespace_dsl_test.rb +311 -0
- data/test/configuration/roles_test.rb +144 -0
- data/test/configuration/servers_test.rb +121 -0
- data/test/configuration/variables_test.rb +184 -0
- data/test/configuration_test.rb +88 -0
- data/test/deploy/local_dependency_test.rb +76 -0
- data/test/deploy/remote_dependency_test.rb +114 -0
- data/test/deploy/scm/accurev_test.rb +23 -0
- data/test/deploy/scm/base_test.rb +55 -0
- data/test/deploy/scm/git_test.rb +184 -0
- data/test/deploy/scm/mercurial_test.rb +129 -0
- data/test/deploy/scm/none_test.rb +35 -0
- data/test/deploy/strategy/copy_test.rb +258 -0
- data/test/extensions_test.rb +69 -0
- data/test/fixtures/cli_integration.rb +5 -0
- data/test/fixtures/config.rb +5 -0
- data/test/fixtures/custom.rb +3 -0
- data/test/logger_test.rb +123 -0
- data/test/role_test.rb +11 -0
- data/test/server_definition_test.rb +121 -0
- data/test/shell_test.rb +90 -0
- data/test/ssh_test.rb +104 -0
- data/test/task_definition_test.rb +101 -0
- data/test/transfer_test.rb +160 -0
- data/test/utils.rb +38 -0
- metadata +321 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'highline'
|
|
2
|
+
|
|
3
|
+
# work around problem where HighLine detects an eof on $stdin and raises an
|
|
4
|
+
# error.
|
|
5
|
+
HighLine.track_eof = false
|
|
6
|
+
|
|
7
|
+
module Capistrano
|
|
8
|
+
class CLI
|
|
9
|
+
module UI
|
|
10
|
+
def self.included(base) #:nodoc:
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# Return the object that provides UI-specific methods, such as prompts
|
|
16
|
+
# and more.
|
|
17
|
+
def ui
|
|
18
|
+
@ui ||= HighLine.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Prompt for a password using echo suppression.
|
|
22
|
+
def password_prompt(prompt="Password: ")
|
|
23
|
+
ui.ask(prompt) { |q| q.echo = false }
|
|
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
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
require 'capistrano/errors'
|
|
2
|
+
require 'capistrano/processable'
|
|
3
|
+
|
|
4
|
+
module Capistrano
|
|
5
|
+
|
|
6
|
+
# This class encapsulates a single command to be executed on a set of remote
|
|
7
|
+
# machines, in parallel.
|
|
8
|
+
class Command
|
|
9
|
+
include Processable
|
|
10
|
+
|
|
11
|
+
class Tree
|
|
12
|
+
attr_reader :configuration
|
|
13
|
+
attr_reader :branches
|
|
14
|
+
attr_reader :fallback
|
|
15
|
+
|
|
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!
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Instantiates a new command object. The +command+ must be a string
|
|
137
|
+
# containing the command to execute. +sessions+ is an array of Net::SSH
|
|
138
|
+
# session instances, and +options+ must be a hash containing any of the
|
|
139
|
+
# following keys:
|
|
140
|
+
#
|
|
141
|
+
# * +logger+: (optional), a Capistrano::Logger instance
|
|
142
|
+
# * +data+: (optional), a string to be sent to the command via it's stdin
|
|
143
|
+
# * +env+: (optional), a string or hash to be interpreted as environment
|
|
144
|
+
# variables that should be defined for this command invocation.
|
|
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
|
|
153
|
+
@sessions = sessions
|
|
154
|
+
@options = options
|
|
155
|
+
@channels = open_channels
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Processes the command in parallel on all specified hosts. If the command
|
|
159
|
+
# fails (non-zero return code) on any of the hosts, this will raise a
|
|
160
|
+
# Capistrano::CommandError.
|
|
161
|
+
def process!
|
|
162
|
+
loop do
|
|
163
|
+
break unless process_iteration { @channels.any? { |ch| !ch[:closed] } }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
logger.trace "command finished" if logger
|
|
167
|
+
|
|
168
|
+
if (failed = @channels.select { |ch| ch[:status] != 0 }).any?
|
|
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
|
|
173
|
+
raise error
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Force the command to stop processing, by closing all open channels
|
|
180
|
+
# associated with this command.
|
|
181
|
+
def stop!
|
|
182
|
+
@channels.each do |ch|
|
|
183
|
+
ch.close unless ch[:closed]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
def logger
|
|
190
|
+
options[:logger]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def open_channels
|
|
194
|
+
sessions.map do |session|
|
|
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)
|
|
207
|
+
|
|
208
|
+
if options[:shell] == false
|
|
209
|
+
shell = nil
|
|
210
|
+
else
|
|
211
|
+
shell = "#{options[:shell] || "sh"} -c"
|
|
212
|
+
cmd = cmd.gsub(/'/) { |m| "'\\''" }
|
|
213
|
+
cmd = "'#{cmd}'"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
command_line = [environment, shell, cmd].compact.join(" ")
|
|
217
|
+
ch[:command] = command_line
|
|
218
|
+
|
|
219
|
+
ch.exec(command_line)
|
|
220
|
+
ch.send_data(options[:data]) if options[:data]
|
|
221
|
+
else
|
|
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
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
channel.on_data do |ch, data|
|
|
231
|
+
ch[:branch].callback[ch, :out, data]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
channel.on_extended_data do |ch, type, data|
|
|
235
|
+
ch[:branch].callback[ch, :err, data]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
channel.on_request("exit-status") do |ch, data|
|
|
239
|
+
ch[:status] = data.read_long
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
channel.on_close do |ch|
|
|
243
|
+
ch[:closed] = true
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end.flatten
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def request_pty_if_necessary(channel)
|
|
251
|
+
if options[:pty]
|
|
252
|
+
channel.request_pty do |ch, success|
|
|
253
|
+
yield ch, success
|
|
254
|
+
end
|
|
255
|
+
else
|
|
256
|
+
yield channel, true
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def replace_placeholders(command, channel)
|
|
261
|
+
command.gsub(/\$CAPISTRANO:HOST\$/, channel[:host])
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# prepare a space-separated sequence of variables assignments
|
|
265
|
+
# intended to be prepended to a command, so the shell sets
|
|
266
|
+
# the environment before running the command.
|
|
267
|
+
# i.e.: options[:env] = {'PATH' => '/opt/ruby/bin:$PATH',
|
|
268
|
+
# 'TEST' => '( "quoted" )'}
|
|
269
|
+
# environment returns:
|
|
270
|
+
# "env TEST=(\ \"quoted\"\ ) PATH=/opt/ruby/bin:$PATH"
|
|
271
|
+
def environment
|
|
272
|
+
return if options[:env].nil? || options[:env].empty?
|
|
273
|
+
@environment ||= if String === options[:env]
|
|
274
|
+
"env #{options[:env]}"
|
|
275
|
+
else
|
|
276
|
+
options[:env].inject("env") do |string, (name, value)|
|
|
277
|
+
value = value.to_s.gsub(/[ "]/) { |m| "\\#{m}" }
|
|
278
|
+
string << " #{name}=#{value}"
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'capistrano/logger'
|
|
2
|
+
|
|
3
|
+
require 'capistrano/configuration/callbacks'
|
|
4
|
+
require 'capistrano/configuration/connections'
|
|
5
|
+
require 'capistrano/configuration/execution'
|
|
6
|
+
require 'capistrano/configuration/loading'
|
|
7
|
+
require 'capistrano/configuration/namespaces'
|
|
8
|
+
require 'capistrano/configuration/roles'
|
|
9
|
+
require 'capistrano/configuration/servers'
|
|
10
|
+
require 'capistrano/configuration/variables'
|
|
11
|
+
|
|
12
|
+
require 'capistrano/configuration/actions/file_transfer'
|
|
13
|
+
require 'capistrano/configuration/actions/inspect'
|
|
14
|
+
require 'capistrano/configuration/actions/invocation'
|
|
15
|
+
|
|
16
|
+
module Capistrano
|
|
17
|
+
# Represents a specific Capistrano configuration. A Configuration instance
|
|
18
|
+
# may be used to load multiple recipe files, define and describe tasks,
|
|
19
|
+
# define roles, and set configuration variables.
|
|
20
|
+
class Configuration
|
|
21
|
+
# The logger instance defined for this configuration.
|
|
22
|
+
attr_accessor :debug, :logger, :dry_run
|
|
23
|
+
|
|
24
|
+
def initialize #:nodoc:
|
|
25
|
+
@debug = false
|
|
26
|
+
@dry_run = false
|
|
27
|
+
@logger = Logger.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# make the DSL easier to read when using lazy evaluation via lambdas
|
|
31
|
+
alias defer lambda
|
|
32
|
+
|
|
33
|
+
# The includes must come at the bottom, since they may redefine methods
|
|
34
|
+
# defined in the base class.
|
|
35
|
+
include Connections, Execution, Loading, Namespaces, Roles, Servers, Variables
|
|
36
|
+
|
|
37
|
+
# Mix in the actions
|
|
38
|
+
include Actions::FileTransfer, Actions::Inspect, Actions::Invocation
|
|
39
|
+
|
|
40
|
+
# Must mix last, because it hooks into previously defined methods
|
|
41
|
+
include Callbacks
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'capistrano/transfer'
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
class Configuration
|
|
5
|
+
module Actions
|
|
6
|
+
module FileTransfer
|
|
7
|
+
|
|
8
|
+
# Store the given data at the given location on all servers targetted
|
|
9
|
+
# by the current task. If <tt>:mode</tt> is specified it is used to
|
|
10
|
+
# set the mode on the file.
|
|
11
|
+
def put(data, path, options={})
|
|
12
|
+
opts = options.dup
|
|
13
|
+
upload(StringIO.new(data), path, opts)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get file remote_path from FIRST server targeted by
|
|
17
|
+
# the current task and transfer it to local machine as path.
|
|
18
|
+
#
|
|
19
|
+
# get "#{deploy_to}/current/log/production.log", "log/production.log.web"
|
|
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}"
|
|
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
|
+
execute_on_servers(options) do |servers|
|
|
39
|
+
targets = servers.map { |s| sessions[s] }
|
|
40
|
+
Transfer.process(direction, from, to, targets, options.merge(:logger => logger), &block)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'capistrano/errors'
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
class Configuration
|
|
5
|
+
module Actions
|
|
6
|
+
module Inspect
|
|
7
|
+
|
|
8
|
+
# Streams the result of the command from all servers that are the
|
|
9
|
+
# target of the current task. All these streams will be joined into a
|
|
10
|
+
# single one, so you can, say, watch 10 log files as though they were
|
|
11
|
+
# one. Do note that this is quite expensive from a bandwidth
|
|
12
|
+
# perspective, so use it with care.
|
|
13
|
+
#
|
|
14
|
+
# The command is invoked via #invoke_command.
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
#
|
|
18
|
+
# desc "Run a tail on multiple log files at the same time"
|
|
19
|
+
# task :tail_fcgi, :roles => :app do
|
|
20
|
+
# stream "tail -f #{shared_path}/log/fastcgi.crash.log"
|
|
21
|
+
# end
|
|
22
|
+
def stream(command, options={})
|
|
23
|
+
invoke_command(command, options) do |ch, stream, out|
|
|
24
|
+
puts out if stream == :out
|
|
25
|
+
warn "[err :: #{ch[:server]}] #{out}" if stream == :err
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Executes the given command on the first server targetted by the
|
|
30
|
+
# current task, collects it's stdout into a string, and returns the
|
|
31
|
+
# string. The command is invoked via #invoke_command.
|
|
32
|
+
def capture(command, options={})
|
|
33
|
+
output = ""
|
|
34
|
+
invoke_command(command, options.merge(:once => true)) do |ch, stream, data|
|
|
35
|
+
case stream
|
|
36
|
+
when :out then output << data
|
|
37
|
+
when :err then warn "[err :: #{ch[:server]}] #{data}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
output
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|