minmb-capistrano 2.15.4

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.
Files changed (119) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +7 -0
  3. data/CHANGELOG +1170 -0
  4. data/Gemfile +13 -0
  5. data/README.md +94 -0
  6. data/Rakefile +11 -0
  7. data/bin/cap +4 -0
  8. data/bin/capify +92 -0
  9. data/capistrano.gemspec +40 -0
  10. data/lib/capistrano.rb +5 -0
  11. data/lib/capistrano/callback.rb +45 -0
  12. data/lib/capistrano/cli.rb +47 -0
  13. data/lib/capistrano/cli/execute.rb +85 -0
  14. data/lib/capistrano/cli/help.rb +125 -0
  15. data/lib/capistrano/cli/help.txt +81 -0
  16. data/lib/capistrano/cli/options.rb +243 -0
  17. data/lib/capistrano/cli/ui.rb +40 -0
  18. data/lib/capistrano/command.rb +303 -0
  19. data/lib/capistrano/configuration.rb +57 -0
  20. data/lib/capistrano/configuration/actions/file_transfer.rb +50 -0
  21. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  22. data/lib/capistrano/configuration/actions/invocation.rb +329 -0
  23. data/lib/capistrano/configuration/alias_task.rb +26 -0
  24. data/lib/capistrano/configuration/callbacks.rb +147 -0
  25. data/lib/capistrano/configuration/connections.rb +237 -0
  26. data/lib/capistrano/configuration/execution.rb +142 -0
  27. data/lib/capistrano/configuration/loading.rb +205 -0
  28. data/lib/capistrano/configuration/log_formatters.rb +75 -0
  29. data/lib/capistrano/configuration/namespaces.rb +223 -0
  30. data/lib/capistrano/configuration/roles.rb +77 -0
  31. data/lib/capistrano/configuration/servers.rb +116 -0
  32. data/lib/capistrano/configuration/variables.rb +127 -0
  33. data/lib/capistrano/errors.rb +19 -0
  34. data/lib/capistrano/ext/multistage.rb +64 -0
  35. data/lib/capistrano/ext/string.rb +5 -0
  36. data/lib/capistrano/extensions.rb +57 -0
  37. data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
  38. data/lib/capistrano/logger.rb +166 -0
  39. data/lib/capistrano/processable.rb +57 -0
  40. data/lib/capistrano/recipes/compat.rb +32 -0
  41. data/lib/capistrano/recipes/deploy.rb +625 -0
  42. data/lib/capistrano/recipes/deploy/assets.rb +201 -0
  43. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  44. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  45. data/lib/capistrano/recipes/deploy/remote_dependency.rb +117 -0
  46. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  47. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  48. data/lib/capistrano/recipes/deploy/scm/base.rb +200 -0
  49. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  50. data/lib/capistrano/recipes/deploy/scm/cvs.rb +153 -0
  51. data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
  52. data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
  53. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  54. data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
  55. data/lib/capistrano/recipes/deploy/scm/perforce.rb +152 -0
  56. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  57. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  58. data/lib/capistrano/recipes/deploy/strategy/base.rb +92 -0
  59. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  60. data/lib/capistrano/recipes/deploy/strategy/copy.rb +338 -0
  61. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  62. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  63. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +57 -0
  64. data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
  65. data/lib/capistrano/recipes/standard.rb +37 -0
  66. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  67. data/lib/capistrano/role.rb +102 -0
  68. data/lib/capistrano/server_definition.rb +56 -0
  69. data/lib/capistrano/shell.rb +265 -0
  70. data/lib/capistrano/ssh.rb +95 -0
  71. data/lib/capistrano/task_definition.rb +77 -0
  72. data/lib/capistrano/transfer.rb +218 -0
  73. data/lib/capistrano/version.rb +11 -0
  74. data/test/cli/execute_test.rb +132 -0
  75. data/test/cli/help_test.rb +165 -0
  76. data/test/cli/options_test.rb +329 -0
  77. data/test/cli/ui_test.rb +28 -0
  78. data/test/cli_test.rb +17 -0
  79. data/test/command_test.rb +322 -0
  80. data/test/configuration/actions/file_transfer_test.rb +61 -0
  81. data/test/configuration/actions/inspect_test.rb +76 -0
  82. data/test/configuration/actions/invocation_test.rb +288 -0
  83. data/test/configuration/alias_task_test.rb +118 -0
  84. data/test/configuration/callbacks_test.rb +201 -0
  85. data/test/configuration/connections_test.rb +439 -0
  86. data/test/configuration/execution_test.rb +175 -0
  87. data/test/configuration/loading_test.rb +148 -0
  88. data/test/configuration/namespace_dsl_test.rb +332 -0
  89. data/test/configuration/roles_test.rb +157 -0
  90. data/test/configuration/servers_test.rb +183 -0
  91. data/test/configuration/variables_test.rb +190 -0
  92. data/test/configuration_test.rb +77 -0
  93. data/test/deploy/local_dependency_test.rb +76 -0
  94. data/test/deploy/remote_dependency_test.rb +146 -0
  95. data/test/deploy/scm/accurev_test.rb +23 -0
  96. data/test/deploy/scm/base_test.rb +55 -0
  97. data/test/deploy/scm/bzr_test.rb +51 -0
  98. data/test/deploy/scm/darcs_test.rb +37 -0
  99. data/test/deploy/scm/git_test.rb +221 -0
  100. data/test/deploy/scm/mercurial_test.rb +134 -0
  101. data/test/deploy/scm/none_test.rb +35 -0
  102. data/test/deploy/scm/perforce_test.rb +23 -0
  103. data/test/deploy/scm/subversion_test.rb +40 -0
  104. data/test/deploy/strategy/copy_test.rb +360 -0
  105. data/test/extensions_test.rb +69 -0
  106. data/test/fixtures/cli_integration.rb +5 -0
  107. data/test/fixtures/config.rb +5 -0
  108. data/test/fixtures/custom.rb +3 -0
  109. data/test/logger_formatting_test.rb +149 -0
  110. data/test/logger_test.rb +134 -0
  111. data/test/recipes_test.rb +25 -0
  112. data/test/role_test.rb +11 -0
  113. data/test/server_definition_test.rb +121 -0
  114. data/test/shell_test.rb +96 -0
  115. data/test/ssh_test.rb +113 -0
  116. data/test/task_definition_test.rb +117 -0
  117. data/test/transfer_test.rb +168 -0
  118. data/test/utils.rb +37 -0
  119. metadata +316 -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,303 @@
1
+ require 'benchmark'
2
+ require 'capistrano/errors'
3
+ require 'capistrano/processable'
4
+
5
+ module Capistrano
6
+
7
+ # This class encapsulates a single command to be executed on a set of remote
8
+ # machines, in parallel.
9
+ class Command
10
+ include Processable
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!
141
+ end
142
+
143
+ # Instantiates a new command object. The +command+ must be a string
144
+ # containing the command to execute. +sessions+ is an array of Net::SSH
145
+ # session instances, and +options+ must be a hash containing any of the
146
+ # following keys:
147
+ #
148
+ # * +logger+: (optional), a Capistrano::Logger instance
149
+ # * +data+: (optional), a string to be sent to the command via it's stdin
150
+ # * +env+: (optional), a string or hash to be interpreted as environment
151
+ # variables that should be defined for this command invocation.
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
160
+ @sessions = sessions
161
+ @options = options
162
+ @channels = open_channels
163
+ end
164
+
165
+ # Processes the command in parallel on all specified hosts. If the command
166
+ # fails (non-zero return code) on any of the hosts, this will raise a
167
+ # Capistrano::CommandError.
168
+ def process!
169
+ elapsed = Benchmark.realtime do
170
+ loop do
171
+ break unless process_iteration { @channels.any? { |ch| !ch[:closed] } }
172
+ end
173
+ end
174
+
175
+ logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
176
+
177
+ if (failed = @channels.select { |ch| ch[:status] != 0 }).any?
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
182
+ raise error
183
+ end
184
+
185
+ self
186
+ end
187
+
188
+ # Force the command to stop processing, by closing all open channels
189
+ # associated with this command.
190
+ def stop!
191
+ @channels.each do |ch|
192
+ ch.close unless ch[:closed]
193
+ end
194
+ end
195
+
196
+ private
197
+
198
+ def logger
199
+ options[:logger]
200
+ end
201
+
202
+ def open_channels
203
+ sessions.map do |session|
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
211
+
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)
216
+
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
224
+
225
+ command_line = [environment, shell, cmd].compact.join(" ")
226
+ ch[:command] = command_line
227
+
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
251
+
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
262
+ end
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
274
+ end
275
+ end
276
+
277
+ def replace_placeholders(command, channel)
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
282
+ end
283
+
284
+ # prepare a space-separated sequence of variables assignments
285
+ # intended to be prepended to a command, so the shell sets
286
+ # the environment before running the command.
287
+ # i.e.: options[:env] = {'PATH' => '/opt/ruby/bin:$PATH',
288
+ # 'TEST' => '( "quoted" )'}
289
+ # environment returns:
290
+ # "env TEST=(\ \"quoted\"\ ) PATH=/opt/ruby/bin:$PATH"
291
+ def environment
292
+ return if options[:env].nil? || options[:env].empty?
293
+ @environment ||= if String === options[:env]
294
+ "env #{options[:env]}"
295
+ else
296
+ options[:env].inject("env") do |string, (name, value)|
297
+ value = value.to_s.gsub(/[ "]/) { |m| "\\#{m}" }
298
+ string << " #{name}=#{value}"
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,57 @@
1
+ require 'capistrano/logger'
2
+
3
+ require 'capistrano/configuration/alias_task'
4
+ require 'capistrano/configuration/callbacks'
5
+ require 'capistrano/configuration/connections'
6
+ require 'capistrano/configuration/execution'
7
+ require 'capistrano/configuration/loading'
8
+ require 'capistrano/configuration/log_formatters'
9
+ require 'capistrano/configuration/namespaces'
10
+ require 'capistrano/configuration/roles'
11
+ require 'capistrano/configuration/servers'
12
+ require 'capistrano/configuration/variables'
13
+
14
+ require 'capistrano/configuration/actions/file_transfer'
15
+ require 'capistrano/configuration/actions/inspect'
16
+ require 'capistrano/configuration/actions/invocation'
17
+
18
+ module Capistrano
19
+ # Represents a specific Capistrano configuration. A Configuration instance
20
+ # may be used to load multiple recipe files, define and describe tasks,
21
+ # define roles, and set configuration variables.
22
+ class Configuration
23
+ # The logger instance defined for this configuration.
24
+ attr_accessor :debug, :logger, :dry_run, :preserve_roles
25
+
26
+ def initialize(options={}) #:nodoc:
27
+ @debug = false
28
+ @dry_run = false
29
+ @preserve_roles = false
30
+ @logger = Logger.new(options)
31
+ end
32
+
33
+ # make the DSL easier to read when using lazy evaluation via lambdas
34
+ alias defer lambda
35
+
36
+ # The includes must come at the bottom, since they may redefine methods
37
+ # defined in the base class.
38
+ include AliasTask, Connections, Execution, Loading, LogFormatters, Namespaces, Roles, Servers, Variables
39
+
40
+ # Mix in the actions
41
+ include Actions::FileTransfer, Actions::Inspect, Actions::Invocation
42
+
43
+ # Must mix last, because it hooks into previously defined methods
44
+ include Callbacks
45
+
46
+ (self.instance_methods & Kernel.methods).select do |name|
47
+ # Select the instance methods owned by the Configuration class.
48
+ self.instance_method(name).owner.to_s.start_with?("Capistrano::Configuration")
49
+ end.select do |name|
50
+ # Of those, select methods that are being shadowed by the Kernel module in the Namespace class.
51
+ Namespaces::Namespace.method_defined?(name) && Namespaces::Namespace.instance_method(name).owner == Kernel
52
+ end.each do |name|
53
+ # Undefine the shadowed methods, since we want Namespace objects to defer handling to the Configuration object.
54
+ Namespaces::Namespace.send(:undef_method, name)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,50 @@
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}", 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)
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end