capistrano 2.8.0 → 3.19.0

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 (239) hide show
  1. checksums.yaml +7 -0
  2. data/.docker/Dockerfile +7 -0
  3. data/.docker/ssh_key_rsa +49 -0
  4. data/.docker/ssh_key_rsa.pub +1 -0
  5. data/.docker/ubuntu_setup.sh +23 -0
  6. data/.github/issue_template.md +19 -0
  7. data/.github/pull_request_template.md +22 -0
  8. data/.github/release-drafter.yml +25 -0
  9. data/.github/workflows/ci.yml +80 -0
  10. data/.github/workflows/release-drafter.yml +18 -0
  11. data/.gitignore +23 -8
  12. data/.rubocop.yml +62 -0
  13. data/CHANGELOG.md +1 -0
  14. data/CONTRIBUTING.md +63 -0
  15. data/DEVELOPMENT.md +112 -0
  16. data/Gemfile +42 -9
  17. data/LICENSE.txt +21 -0
  18. data/README.md +221 -0
  19. data/RELEASING.md +17 -0
  20. data/Rakefile +17 -8
  21. data/UPGRADING-3.7.md +86 -0
  22. data/bin/cap +2 -3
  23. data/bin/capify +7 -89
  24. data/capistrano.gemspec +29 -43
  25. data/docker-compose.yml +8 -0
  26. data/features/configuration.feature +28 -0
  27. data/features/deploy.feature +92 -0
  28. data/features/deploy_failure.feature +17 -0
  29. data/features/doctor.feature +11 -0
  30. data/features/installation.feature +21 -0
  31. data/features/sshconnect.feature +11 -0
  32. data/features/stage_failure.feature +9 -0
  33. data/features/step_definitions/assertions.rb +162 -0
  34. data/features/step_definitions/cap_commands.rb +21 -0
  35. data/features/step_definitions/setup.rb +91 -0
  36. data/features/subdirectory.feature +9 -0
  37. data/features/support/docker_gateway.rb +53 -0
  38. data/features/support/env.rb +1 -0
  39. data/features/support/remote_command_helpers.rb +29 -0
  40. data/features/support/remote_ssh_helpers.rb +33 -0
  41. data/lib/Capfile +3 -0
  42. data/lib/capistrano/all.rb +17 -0
  43. data/lib/capistrano/application.rb +153 -0
  44. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  45. data/lib/capistrano/configuration/filter.rb +26 -0
  46. data/lib/capistrano/configuration/host_filter.rb +29 -0
  47. data/lib/capistrano/configuration/null_filter.rb +9 -0
  48. data/lib/capistrano/configuration/plugin_installer.rb +51 -0
  49. data/lib/capistrano/configuration/question.rb +76 -0
  50. data/lib/capistrano/configuration/role_filter.rb +29 -0
  51. data/lib/capistrano/configuration/scm_resolver.rb +149 -0
  52. data/lib/capistrano/configuration/server.rb +137 -0
  53. data/lib/capistrano/configuration/servers.rb +56 -96
  54. data/lib/capistrano/configuration/validated_variables.rb +110 -0
  55. data/lib/capistrano/configuration/variables.rb +79 -94
  56. data/lib/capistrano/configuration.rb +178 -33
  57. data/lib/capistrano/console.rb +1 -0
  58. data/lib/capistrano/defaults.rb +36 -0
  59. data/lib/capistrano/deploy.rb +3 -0
  60. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  61. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  62. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  63. data/lib/capistrano/doctor/servers_doctor.rb +105 -0
  64. data/lib/capistrano/doctor/variables_doctor.rb +74 -0
  65. data/lib/capistrano/doctor.rb +6 -0
  66. data/lib/capistrano/dotfile.rb +2 -0
  67. data/lib/capistrano/dsl/env.rb +43 -0
  68. data/lib/capistrano/dsl/paths.rb +89 -0
  69. data/lib/capistrano/dsl/stages.rb +31 -0
  70. data/lib/capistrano/dsl/task_enhancements.rb +61 -0
  71. data/lib/capistrano/dsl.rb +95 -0
  72. data/lib/capistrano/framework.rb +2 -0
  73. data/lib/capistrano/i18n.rb +46 -0
  74. data/lib/capistrano/immutable_task.rb +30 -0
  75. data/lib/capistrano/install.rb +1 -0
  76. data/lib/capistrano/plugin.rb +95 -0
  77. data/lib/capistrano/proc_helpers.rb +13 -0
  78. data/lib/capistrano/scm/git.rb +105 -0
  79. data/lib/capistrano/scm/hg.rb +55 -0
  80. data/lib/capistrano/scm/plugin.rb +13 -0
  81. data/lib/capistrano/scm/svn.rb +56 -0
  82. data/lib/capistrano/scm/tasks/git.rake +84 -0
  83. data/lib/capistrano/scm/tasks/hg.rake +53 -0
  84. data/lib/capistrano/scm/tasks/svn.rake +53 -0
  85. data/lib/capistrano/scm.rb +115 -0
  86. data/lib/capistrano/setup.rb +36 -0
  87. data/lib/capistrano/tasks/console.rake +25 -0
  88. data/lib/capistrano/tasks/deploy.rake +280 -0
  89. data/lib/capistrano/tasks/doctor.rake +24 -0
  90. data/lib/capistrano/tasks/framework.rake +67 -0
  91. data/lib/capistrano/tasks/install.rake +41 -0
  92. data/lib/capistrano/templates/Capfile +38 -0
  93. data/lib/capistrano/templates/deploy.rb.erb +39 -0
  94. data/lib/capistrano/templates/stage.rb.erb +61 -0
  95. data/lib/capistrano/upload_task.rb +9 -0
  96. data/lib/capistrano/version.rb +1 -14
  97. data/lib/capistrano/version_validator.rb +32 -0
  98. data/lib/capistrano.rb +0 -3
  99. data/spec/integration/dsl_spec.rb +632 -0
  100. data/spec/integration_spec_helper.rb +5 -0
  101. data/spec/lib/capistrano/application_spec.rb +60 -0
  102. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  103. data/spec/lib/capistrano/configuration/filter_spec.rb +109 -0
  104. data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
  105. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  106. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
  107. data/spec/lib/capistrano/configuration/question_spec.rb +92 -0
  108. data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
  109. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +56 -0
  110. data/spec/lib/capistrano/configuration/server_spec.rb +309 -0
  111. data/spec/lib/capistrano/configuration/servers_spec.rb +331 -0
  112. data/spec/lib/capistrano/configuration_spec.rb +357 -0
  113. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  114. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
  115. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  116. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
  117. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
  118. data/spec/lib/capistrano/dsl/paths_spec.rb +228 -0
  119. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +108 -0
  120. data/spec/lib/capistrano/dsl_spec.rb +125 -0
  121. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  122. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  123. data/spec/lib/capistrano/scm/git_spec.rb +194 -0
  124. data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
  125. data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
  126. data/spec/lib/capistrano/scm_spec.rb +103 -0
  127. data/spec/lib/capistrano/upload_task_spec.rb +19 -0
  128. data/spec/lib/capistrano/version_validator_spec.rb +118 -0
  129. data/spec/lib/capistrano_spec.rb +7 -0
  130. data/spec/spec_helper.rb +29 -0
  131. data/spec/support/matchers.rb +5 -0
  132. data/spec/support/tasks/database.rake +11 -0
  133. data/spec/support/tasks/fail.rake +8 -0
  134. data/spec/support/tasks/failed.rake +5 -0
  135. data/spec/support/tasks/plugin.rake +6 -0
  136. data/spec/support/tasks/root.rake +11 -0
  137. data/spec/support/test_app.rb +205 -0
  138. metadata +234 -208
  139. data/.rvmrc +0 -1
  140. data/CHANGELOG +0 -954
  141. data/README.mdown +0 -76
  142. data/lib/capistrano/callback.rb +0 -45
  143. data/lib/capistrano/cli/execute.rb +0 -85
  144. data/lib/capistrano/cli/help.rb +0 -125
  145. data/lib/capistrano/cli/help.txt +0 -81
  146. data/lib/capistrano/cli/options.rb +0 -243
  147. data/lib/capistrano/cli/ui.rb +0 -40
  148. data/lib/capistrano/cli.rb +0 -47
  149. data/lib/capistrano/command.rb +0 -286
  150. data/lib/capistrano/configuration/actions/file_transfer.rb +0 -51
  151. data/lib/capistrano/configuration/actions/inspect.rb +0 -46
  152. data/lib/capistrano/configuration/actions/invocation.rb +0 -298
  153. data/lib/capistrano/configuration/callbacks.rb +0 -148
  154. data/lib/capistrano/configuration/connections.rb +0 -230
  155. data/lib/capistrano/configuration/execution.rb +0 -143
  156. data/lib/capistrano/configuration/loading.rb +0 -197
  157. data/lib/capistrano/configuration/namespaces.rb +0 -197
  158. data/lib/capistrano/configuration/roles.rb +0 -73
  159. data/lib/capistrano/errors.rb +0 -19
  160. data/lib/capistrano/ext/string.rb +0 -5
  161. data/lib/capistrano/extensions.rb +0 -57
  162. data/lib/capistrano/logger.rb +0 -59
  163. data/lib/capistrano/processable.rb +0 -53
  164. data/lib/capistrano/recipes/compat.rb +0 -32
  165. data/lib/capistrano/recipes/deploy/assets.rb +0 -57
  166. data/lib/capistrano/recipes/deploy/dependencies.rb +0 -44
  167. data/lib/capistrano/recipes/deploy/local_dependency.rb +0 -54
  168. data/lib/capistrano/recipes/deploy/remote_dependency.rb +0 -111
  169. data/lib/capistrano/recipes/deploy/scm/accurev.rb +0 -169
  170. data/lib/capistrano/recipes/deploy/scm/base.rb +0 -196
  171. data/lib/capistrano/recipes/deploy/scm/bzr.rb +0 -86
  172. data/lib/capistrano/recipes/deploy/scm/cvs.rb +0 -153
  173. data/lib/capistrano/recipes/deploy/scm/darcs.rb +0 -96
  174. data/lib/capistrano/recipes/deploy/scm/git.rb +0 -282
  175. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +0 -137
  176. data/lib/capistrano/recipes/deploy/scm/none.rb +0 -44
  177. data/lib/capistrano/recipes/deploy/scm/perforce.rb +0 -138
  178. data/lib/capistrano/recipes/deploy/scm/subversion.rb +0 -121
  179. data/lib/capistrano/recipes/deploy/scm.rb +0 -19
  180. data/lib/capistrano/recipes/deploy/strategy/base.rb +0 -88
  181. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +0 -20
  182. data/lib/capistrano/recipes/deploy/strategy/copy.rb +0 -224
  183. data/lib/capistrano/recipes/deploy/strategy/export.rb +0 -20
  184. data/lib/capistrano/recipes/deploy/strategy/remote.rb +0 -52
  185. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +0 -57
  186. data/lib/capistrano/recipes/deploy/strategy.rb +0 -19
  187. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
  188. data/lib/capistrano/recipes/deploy.rb +0 -568
  189. data/lib/capistrano/recipes/standard.rb +0 -37
  190. data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
  191. data/lib/capistrano/role.rb +0 -102
  192. data/lib/capistrano/server_definition.rb +0 -56
  193. data/lib/capistrano/shell.rb +0 -260
  194. data/lib/capistrano/ssh.rb +0 -101
  195. data/lib/capistrano/task_definition.rb +0 -75
  196. data/lib/capistrano/transfer.rb +0 -216
  197. data/rvmrc.sample +0 -1
  198. data/test/cli/execute_test.rb +0 -132
  199. data/test/cli/help_test.rb +0 -165
  200. data/test/cli/options_test.rb +0 -329
  201. data/test/cli/ui_test.rb +0 -28
  202. data/test/cli_test.rb +0 -17
  203. data/test/command_test.rb +0 -289
  204. data/test/configuration/actions/file_transfer_test.rb +0 -61
  205. data/test/configuration/actions/inspect_test.rb +0 -65
  206. data/test/configuration/actions/invocation_test.rb +0 -247
  207. data/test/configuration/callbacks_test.rb +0 -220
  208. data/test/configuration/connections_test.rb +0 -420
  209. data/test/configuration/execution_test.rb +0 -175
  210. data/test/configuration/loading_test.rb +0 -132
  211. data/test/configuration/namespace_dsl_test.rb +0 -311
  212. data/test/configuration/roles_test.rb +0 -144
  213. data/test/configuration/servers_test.rb +0 -183
  214. data/test/configuration/variables_test.rb +0 -190
  215. data/test/configuration_test.rb +0 -88
  216. data/test/deploy/local_dependency_test.rb +0 -76
  217. data/test/deploy/remote_dependency_test.rb +0 -135
  218. data/test/deploy/scm/accurev_test.rb +0 -23
  219. data/test/deploy/scm/base_test.rb +0 -55
  220. data/test/deploy/scm/bzr_test.rb +0 -51
  221. data/test/deploy/scm/darcs_test.rb +0 -37
  222. data/test/deploy/scm/git_test.rb +0 -184
  223. data/test/deploy/scm/mercurial_test.rb +0 -134
  224. data/test/deploy/scm/none_test.rb +0 -35
  225. data/test/deploy/scm/subversion_test.rb +0 -32
  226. data/test/deploy/strategy/copy_test.rb +0 -321
  227. data/test/extensions_test.rb +0 -69
  228. data/test/fixtures/cli_integration.rb +0 -5
  229. data/test/fixtures/config.rb +0 -5
  230. data/test/fixtures/custom.rb +0 -3
  231. data/test/logger_test.rb +0 -123
  232. data/test/recipes_test.rb +0 -25
  233. data/test/role_test.rb +0 -11
  234. data/test/server_definition_test.rb +0 -121
  235. data/test/shell_test.rb +0 -90
  236. data/test/ssh_test.rb +0 -113
  237. data/test/task_definition_test.rb +0 -116
  238. data/test/transfer_test.rb +0 -160
  239. data/test/utils.rb +0 -37
@@ -1,298 +0,0 @@
1
- require 'capistrano/command'
2
-
3
- module Capistrano
4
- class Configuration
5
- module Actions
6
- module Invocation
7
- def self.included(base) #:nodoc:
8
- base.extend(ClassMethods)
9
-
10
- base.send :alias_method, :initialize_without_invocation, :initialize
11
- base.send :alias_method, :initialize, :initialize_with_invocation
12
-
13
- base.default_io_proc = Proc.new do |ch, stream, out|
14
- level = stream == :err ? :important : :info
15
- ch[:options][:logger].send(level, out, "#{stream} :: #{ch[:server]}")
16
- end
17
- end
18
-
19
- module ClassMethods
20
- attr_accessor :default_io_proc
21
- end
22
-
23
- def initialize_with_invocation(*args) #:nodoc:
24
- initialize_without_invocation(*args)
25
- set :default_environment, {}
26
- set :default_run_options, {}
27
- end
28
-
29
- # Executes different commands in parallel. This is useful for commands
30
- # that need to be different on different hosts, but which could be
31
- # otherwise run in parallel.
32
- #
33
- # The +options+ parameter is currently unused.
34
- #
35
- # Example:
36
- #
37
- # task :restart_everything do
38
- # parallel do |session|
39
- # session.when "in?(:app)", "/path/to/restart/mongrel"
40
- # session.when "in?(:web)", "/path/to/restart/apache"
41
- # session.when "in?(:db)", "/path/to/restart/mysql"
42
- # end
43
- # end
44
- #
45
- # Each command may have its own callback block, for capturing and
46
- # responding to output, with semantics identical to #run:
47
- #
48
- # session.when "in?(:app)", "/path/to/restart/mongrel" do |ch, stream, data|
49
- # # ch is the SSH channel for this command, used to send data
50
- # # back to the command (e.g. ch.send_data("password\n"))
51
- # # stream is either :out or :err, for which stream the data arrived on
52
- # # data is a string containing data sent from the remote command
53
- # end
54
- #
55
- # Also, you can specify a fallback command, to use when none of the
56
- # conditions match a server:
57
- #
58
- # session.else "/execute/something/else"
59
- #
60
- # The string specified as the first argument to +when+ may be any valid
61
- # Ruby code. It has access to the following variables and methods:
62
- #
63
- # * +in?(role)+ returns true if the server participates in the given role
64
- # * +server+ is the ServerDefinition object for the server. This can be
65
- # used to get the host-name, etc.
66
- # * +configuration+ is the current Capistrano::Configuration object, which
67
- # you can use to get the value of variables, etc.
68
- #
69
- # For example:
70
- #
71
- # session.when "server.host =~ /app/", "/some/command"
72
- # session.when "server.host == configuration[:some_var]", "/another/command"
73
- # session.when "in?(:web) || in?(:app)", "/more/commands"
74
- #
75
- # See #run for a description of the valid +options+.
76
- def parallel(options={})
77
- raise ArgumentError, "parallel() requires a block" unless block_given?
78
- tree = Command::Tree.new(self) { |t| yield t }
79
- run_tree(tree, options)
80
- end
81
-
82
- # Invokes the given command. If a +via+ key is given, it will be used
83
- # to determine what method to use to invoke the command. It defaults
84
- # to :run, but may be :sudo, or any other method that conforms to the
85
- # same interface as run and sudo.
86
- def invoke_command(cmd, options={}, &block)
87
- options = options.dup
88
- via = options.delete(:via) || :run
89
- send(via, cmd, options, &block)
90
- end
91
-
92
- # Execute the given command on all servers that are the target of the
93
- # current task. If a block is given, it is invoked for all output
94
- # generated by the command, and should accept three parameters: the SSH
95
- # channel (which may be used to send data back to the remote process),
96
- # the stream identifier (<tt>:err</tt> for stderr, and <tt>:out</tt> for
97
- # stdout), and the data that was received.
98
- #
99
- # The +options+ hash may include any of the following keys:
100
- #
101
- # * :hosts - this is either a string (for a single target host) or an array
102
- # of strings, indicating which hosts the command should run on. By default,
103
- # the hosts are determined from the task definition.
104
- # * :roles - this is either a string or symbol (for a single target role) or
105
- # an array of strings or symbols, indicating which roles the command should
106
- # run on. If :hosts is specified, :roles will be ignored.
107
- # * :only - specifies a condition limiting which hosts will be selected to
108
- # run the command. This should refer to values set in the role definition.
109
- # For example, if a role is defined with :primary => true, then you could
110
- # select only hosts with :primary true by setting :only => { :primary => true }.
111
- # * :except - specifies a condition limiting which hosts will be selected to
112
- # run the command. This is the inverse of :only (hosts that do _not_ match
113
- # the condition will be selected).
114
- # * :on_no_matching_servers - if :continue, will continue to execute tasks if
115
- # no matching servers are found for the host criteria. The default is to raise
116
- # a NoMatchingServersError exception.
117
- # * :once - if true, only the first matching server will be selected. The default
118
- # is false (all matching servers will be selected).
119
- # * :max_hosts - specifies the maximum number of hosts that should be selected
120
- # at a time. If this value is less than the number of hosts that are selected
121
- # to run, then the hosts will be run in groups of max_hosts. The default is nil,
122
- # which indicates that there is no maximum host limit. Please note this does not
123
- # limit the number of SSH channels that can be open, only the number of hosts upon
124
- # which this will be called.
125
- # * :shell - says which shell should be used to invoke commands. This
126
- # defaults to "sh". Setting this to false causes Capistrano to invoke
127
- # the commands directly, without wrapping them in a shell invocation.
128
- # * :data - if not nil (the default), this should be a string that will
129
- # be passed to the command's stdin stream.
130
- # * :pty - if true, a pseudo-tty will be allocated for each command. The
131
- # default is false. Note that there are benefits and drawbacks both ways.
132
- # Empirically, it appears that if a pty is allocated, the SSH server daemon
133
- # will _not_ read user shell start-up scripts (e.g. bashrc, etc.). However,
134
- # if a pty is _not_ allocated, some commands will refuse to run in
135
- # interactive mode and will not prompt for (e.g.) passwords.
136
- # * :env - a hash of environment variable mappings that should be made
137
- # available to the command. The keys should be environment variable names,
138
- # and the values should be their corresponding values. The default is
139
- # empty, but may be modified by changing the +default_environment+
140
- # Capistrano variable.
141
- #
142
- # Note that if you set these keys in the +default_run_options+ Capistrano
143
- # variable, they will apply for all invocations of #run, #invoke_command,
144
- # and #parallel.
145
- def run(cmd, options={}, &block)
146
- block ||= self.class.default_io_proc
147
- tree = Command::Tree.new(self) { |t| t.else(cmd, &block) }
148
- run_tree(tree, options)
149
- end
150
-
151
- # Executes a Capistrano::Command::Tree object. This is not for direct
152
- # use, but should instead be called indirectly, via #run or #parallel,
153
- # or #invoke_command.
154
- def run_tree(tree, options={}) #:nodoc:
155
- if tree.branches.empty? && tree.fallback
156
- logger.debug "executing #{tree.fallback}"
157
- elsif tree.branches.any?
158
- logger.debug "executing multiple commands in parallel"
159
- tree.each do |branch|
160
- logger.trace "-> #{branch}"
161
- end
162
- else
163
- raise ArgumentError, "attempt to execute without specifying a command"
164
- end
165
-
166
- return if dry_run || (debug && continue_execution(tree) == false)
167
-
168
- options = add_default_command_options(options)
169
-
170
- tree.each do |branch|
171
- if branch.command.include?(sudo)
172
- branch.callback = sudo_behavior_callback(branch.callback)
173
- end
174
- end
175
-
176
- execute_on_servers(options) do |servers|
177
- targets = servers.map { |s| sessions[s] }
178
- Command.process(tree, targets, options.merge(:logger => logger))
179
- end
180
- end
181
-
182
- # Returns the command string used by capistrano to invoke a comamnd via
183
- # sudo.
184
- #
185
- # run "#{sudo :as => 'bob'} mkdir /path/to/dir"
186
- #
187
- # It can also be invoked like #run, but executing the command via sudo.
188
- # This assumes that the sudo password (if required) is the same as the
189
- # password for logging in to the server.
190
- #
191
- # sudo "mkdir /path/to/dir"
192
- #
193
- # Also, this method understands a <tt>:sudo</tt> configuration variable,
194
- # which (if specified) will be used as the full path to the sudo
195
- # executable on the remote machine:
196
- #
197
- # set :sudo, "/opt/local/bin/sudo"
198
- #
199
- # If you know what you're doing, you can also set <tt>:sudo_prompt</tt>,
200
- # which tells capistrano which prompt sudo should use when asking for
201
- # a password. (This is so that capistrano knows what prompt to look for
202
- # in the output.) If you set :sudo_prompt to an empty string, Capistrano
203
- # will not send a preferred prompt.
204
- def sudo(*parameters, &block)
205
- options = parameters.last.is_a?(Hash) ? parameters.pop.dup : {}
206
- command = parameters.first
207
- user = options[:as] && "-u #{options.delete(:as)}"
208
-
209
- sudo_prompt_option = "-p '#{sudo_prompt}'" unless sudo_prompt.empty?
210
- sudo_command = [fetch(:sudo, "sudo"), sudo_prompt_option, user].compact.join(" ")
211
-
212
- if command
213
- command = sudo_command + " " + command
214
- run(command, options, &block)
215
- else
216
- return sudo_command
217
- end
218
- end
219
-
220
- # Returns a Proc object that defines the behavior of the sudo
221
- # callback. The returned Proc will defer to the +fallback+ argument
222
- # (which should also be a Proc) for any output it does not
223
- # explicitly handle.
224
- def sudo_behavior_callback(fallback) #:nodoc:
225
- # in order to prevent _each host_ from prompting when the password
226
- # was wrong, let's track which host prompted first and only allow
227
- # subsequent prompts from that host.
228
- prompt_host = nil
229
-
230
- Proc.new do |ch, stream, out|
231
- if out =~ /^Sorry, try again/
232
- if prompt_host.nil? || prompt_host == ch[:server]
233
- prompt_host = ch[:server]
234
- logger.important out, "#{stream} :: #{ch[:server]}"
235
- reset! :password
236
- end
237
- end
238
-
239
- if out =~ /^#{Regexp.escape(sudo_prompt)}/
240
- ch.send_data "#{self[:password]}\n"
241
- elsif fallback
242
- fallback.call(ch, stream, out)
243
- end
244
- end
245
- end
246
-
247
- # Merges the various default command options into the options hash and
248
- # returns the result. The default command options that are understand
249
- # are:
250
- #
251
- # * :default_environment: If the :env key already exists, the :env
252
- # key is merged into default_environment and then added back into
253
- # options.
254
- # * :default_shell: if the :shell key already exists, it will be used.
255
- # Otherwise, if the :default_shell key exists in the configuration,
256
- # it will be used. Otherwise, no :shell key is added.
257
- def add_default_command_options(options)
258
- defaults = self[:default_run_options]
259
- options = defaults.merge(options)
260
-
261
- env = self[:default_environment]
262
- env = env.merge(options[:env]) if options[:env]
263
- options[:env] = env unless env.empty?
264
-
265
- shell = options[:shell] || self[:default_shell]
266
- options[:shell] = shell unless shell.nil?
267
-
268
- options
269
- end
270
-
271
- # Returns the prompt text to use with sudo
272
- def sudo_prompt
273
- fetch(:sudo_prompt, "sudo password: ")
274
- end
275
-
276
- def continue_execution(tree)
277
- if tree.branches.length == 1
278
- continue_execution_for_branch(tree.branches.first)
279
- else
280
- tree.each { |branch| branch.skip! unless continue_execution_for_branch(branch) }
281
- tree.any? { |branch| !branch.skip? }
282
- end
283
- end
284
-
285
- def continue_execution_for_branch(branch)
286
- case Capistrano::CLI.debug_prompt(branch)
287
- when "y"
288
- true
289
- when "n"
290
- false
291
- when "a"
292
- exit(-1)
293
- end
294
- end
295
- end
296
- end
297
- end
298
- end
@@ -1,148 +0,0 @@
1
- require 'capistrano/callback'
2
-
3
- module Capistrano
4
- class Configuration
5
- module Callbacks
6
- def self.included(base) #:nodoc:
7
- %w(initialize invoke_task_directly).each do |method|
8
- base.send :alias_method, "#{method}_without_callbacks", method
9
- base.send :alias_method, method, "#{method}_with_callbacks"
10
- end
11
- end
12
-
13
- # The hash of callbacks that have been registered for this configuration
14
- attr_reader :callbacks
15
-
16
- def initialize_with_callbacks(*args) #:nodoc:
17
- initialize_without_callbacks(*args)
18
- @callbacks = {}
19
- end
20
-
21
- def invoke_task_directly_with_callbacks(task) #:nodoc:
22
- before = find_hook(task, :before)
23
- execute_task(before) if before
24
-
25
- trigger :before, task
26
-
27
- result = invoke_task_directly_without_callbacks(task)
28
-
29
- trigger :after, task
30
-
31
- after = find_hook(task, :after)
32
- execute_task(after) if after
33
-
34
- return result
35
- end
36
-
37
- # Defines a callback to be invoked before the given task. You must
38
- # specify the fully-qualified task name, both for the primary task, and
39
- # for the task(s) to be executed before. Alternatively, you can pass a
40
- # block to be executed before the given task.
41
- #
42
- # before "deploy:update_code", :record_difference
43
- # before :deploy, "custom:log_deploy"
44
- # before :deploy, :this, "then:this", "and:then:this"
45
- # before :some_task do
46
- # puts "an anonymous hook!"
47
- # end
48
- #
49
- # This just provides a convenient interface to the more general #on method.
50
- def before(task_name, *args, &block)
51
- options = args.last.is_a?(Hash) ? args.pop : {}
52
- args << options.merge(:only => task_name)
53
- on :before, *args, &block
54
- end
55
-
56
- # Defines a callback to be invoked after the given task. You must
57
- # specify the fully-qualified task name, both for the primary task, and
58
- # for the task(s) to be executed after. Alternatively, you can pass a
59
- # block to be executed after the given task.
60
- #
61
- # after "deploy:update_code", :log_difference
62
- # after :deploy, "custom:announce"
63
- # after :deploy, :this, "then:this", "and:then:this"
64
- # after :some_task do
65
- # puts "an anonymous hook!"
66
- # end
67
- #
68
- # This just provides a convenient interface to the more general #on method.
69
- def after(task_name, *args, &block)
70
- options = args.last.is_a?(Hash) ? args.pop : {}
71
- args << options.merge(:only => task_name)
72
- on :after, *args, &block
73
- end
74
-
75
- # Defines one or more callbacks to be invoked in response to some event.
76
- # Capistrano currently understands the following events:
77
- #
78
- # * :before, triggered before a task is invoked
79
- # * :after, triggered after a task is invoked
80
- # * :start, triggered before a top-level task is invoked via the command-line
81
- # * :finish, triggered when a top-level task completes
82
- # * :load, triggered after all recipes have loaded
83
- # * :exit, triggered after all tasks have completed
84
- #
85
- # Specify the (fully-qualified) task names that you want invoked in
86
- # response to the event. Alternatively, you can specify a block to invoke
87
- # when the event is triggered. You can also pass a hash of options as the
88
- # last parameter, which may include either of two keys:
89
- #
90
- # * :only, should specify an array of task names. Restricts this callback
91
- # so that it will only fire when the event applies to those tasks.
92
- # * :except, should specify an array of task names. Restricts this callback
93
- # so that it will never fire when the event applies to those tasks.
94
- #
95
- # Usage:
96
- #
97
- # on :before, "some:hook", "another:hook", :only => "deploy:update"
98
- # on :after, "some:hook", :except => "deploy:symlink"
99
- # on :before, "global:hook"
100
- # on :after, :only => :deploy do
101
- # puts "after deploy here"
102
- # end
103
- def on(event, *args, &block)
104
- options = args.last.is_a?(Hash) ? args.pop : {}
105
- callbacks[event] ||= []
106
-
107
- if args.empty? && block.nil?
108
- raise ArgumentError, "please specify either a task name or a block to invoke"
109
- elsif args.any? && block
110
- raise ArgumentError, "please specify only a task name or a block, but not both"
111
- elsif block
112
- callbacks[event] << ProcCallback.new(block, options)
113
- else
114
- args.each do |name|
115
- callbacks[event] << TaskCallback.new(self, name, options)
116
- end
117
- end
118
- end
119
-
120
- # Trigger the named event for the named task. All associated callbacks
121
- # will be fired, in the order they were defined.
122
- def trigger(event, task=nil)
123
- pending = Array(callbacks[event]).select { |c| c.applies_to?(task) }
124
- if pending.any?
125
- msg = "triggering #{event} callbacks"
126
- msg << " for `#{task.fully_qualified_name}'" if task
127
- logger.trace(msg)
128
- pending.each { |callback| callback.call }
129
- end
130
- end
131
-
132
- private
133
-
134
- # Looks for before_foo or after_foo tasks. This method of extending tasks
135
- # is now discouraged (though not formally deprecated). You should use the
136
- # before and after methods to declare hooks for such callbacks.
137
- def find_hook(task, hook)
138
- if task == task.namespace.default_task
139
- result = task.namespace.search_task("#{hook}_#{task.namespace.name}")
140
- return result if result
141
- end
142
-
143
- task.namespace.search_task("#{hook}_#{task.name}")
144
- end
145
-
146
- end
147
- end
148
- end
@@ -1,230 +0,0 @@
1
- require 'enumerator'
2
- require 'net/ssh/gateway'
3
- require 'capistrano/ssh'
4
- require 'capistrano/errors'
5
-
6
- module Capistrano
7
- class Configuration
8
- module Connections
9
- def self.included(base) #:nodoc:
10
- base.send :alias_method, :initialize_without_connections, :initialize
11
- base.send :alias_method, :initialize, :initialize_with_connections
12
- end
13
-
14
- class DefaultConnectionFactory #:nodoc:
15
- def initialize(options)
16
- @options = options
17
- end
18
-
19
- def connect_to(server)
20
- SSH.connect(server, @options)
21
- end
22
- end
23
-
24
- class GatewayConnectionFactory #:nodoc:
25
- def initialize(gateway, options)
26
- @options = options
27
- Thread.abort_on_exception = true
28
- @gateways = {}
29
- if gateway.is_a?(Hash)
30
- @options[:logger].debug "Creating multiple gateways using #{gateway.inspect}" if @options[:logger]
31
- gateway.each do |gw, hosts|
32
- gateway_connection = add_gateway(gw)
33
- [*hosts].each do |host|
34
- @gateways[:default] ||= gateway_connection
35
- @gateways[host] = gateway_connection
36
- end
37
- end
38
- else
39
- @options[:logger].debug "Creating gateway using #{[*gateway].join(', ')}" if @options[:logger]
40
- @gateways[:default] = add_gateway(gateway)
41
- end
42
- end
43
-
44
- def add_gateway(gateway)
45
- gateways = [*gateway].collect { |g| ServerDefinition.new(g) }
46
- tunnel = SSH.connection_strategy(gateways[0], @options) do |host, user, connect_options|
47
- Net::SSH::Gateway.new(host, user, connect_options)
48
- end
49
- (gateways[1..-1]).inject(tunnel) do |tunnel, destination|
50
- @options[:logger].debug "Creating tunnel to #{destination}" if @options[:logger]
51
- local_host = ServerDefinition.new("127.0.0.1", :user => destination.user, :port => tunnel.open(destination.host, (destination.port || 22)))
52
- SSH.connection_strategy(local_host, @options) do |host, user, connect_options|
53
- Net::SSH::Gateway.new(host, user, connect_options)
54
- end
55
- end
56
- end
57
-
58
- def connect_to(server)
59
- @options[:logger].debug "establishing connection to `#{server}' via gateway" if @options[:logger]
60
- local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => gateway_for(server).open(server.host, server.port || 22))
61
- session = SSH.connect(local_host, @options)
62
- session.xserver = server
63
- session
64
- end
65
-
66
- def gateway_for(server)
67
- @gateways[server.host] || @gateways[:default]
68
- end
69
- end
70
-
71
- # A hash of the SSH sessions that are currently open and available.
72
- # Because sessions are constructed lazily, this will only contain
73
- # connections to those servers that have been the targets of one or more
74
- # executed tasks. Stored on a per-thread basis to improve thread-safety.
75
- def sessions
76
- Thread.current[:sessions] ||= {}
77
- end
78
-
79
- def initialize_with_connections(*args) #:nodoc:
80
- initialize_without_connections(*args)
81
- Thread.current[:sessions] = {}
82
- Thread.current[:failed_sessions] = []
83
- end
84
-
85
- # Indicate that the given server could not be connected to.
86
- def failed!(server)
87
- Thread.current[:failed_sessions] << server
88
- end
89
-
90
- # Query whether previous connection attempts to the given server have
91
- # failed.
92
- def has_failed?(server)
93
- Thread.current[:failed_sessions].include?(server)
94
- end
95
-
96
- # Used to force connections to be made to the current task's servers.
97
- # Connections are normally made lazily in Capistrano--you can use this
98
- # to force them open before performing some operation that might be
99
- # time-sensitive.
100
- def connect!(options={})
101
- execute_on_servers(options) { }
102
- end
103
-
104
- # Returns the object responsible for establishing new SSH connections.
105
- # The factory will respond to #connect_to, which can be used to
106
- # establish connections to servers defined via ServerDefinition objects.
107
- def connection_factory
108
- @connection_factory ||= begin
109
- if exists?(:gateway)
110
- logger.debug "establishing connection to gateway `#{fetch(:gateway).inspect}'"
111
- GatewayConnectionFactory.new(fetch(:gateway), self)
112
- else
113
- DefaultConnectionFactory.new(self)
114
- end
115
- end
116
- end
117
-
118
- # Ensures that there are active sessions for each server in the list.
119
- def establish_connections_to(servers)
120
- failed_servers = []
121
-
122
- # force the connection factory to be instantiated synchronously,
123
- # otherwise we wind up with multiple gateway instances, because
124
- # each connection is done in parallel.
125
- connection_factory
126
-
127
- threads = Array(servers).map { |server| establish_connection_to(server, failed_servers) }
128
- threads.each { |t| t.join }
129
-
130
- if failed_servers.any?
131
- errors = failed_servers.map { |h| "#{h[:server]} (#{h[:error].class}: #{h[:error].message})" }
132
- error = ConnectionError.new("connection failed for: #{errors.join(', ')}")
133
- error.hosts = failed_servers.map { |h| h[:server] }
134
- raise error
135
- end
136
- end
137
-
138
- # Destroys sessions for each server in the list.
139
- def teardown_connections_to(servers)
140
- servers.each do |server|
141
- begin
142
- sessions.delete(server).close
143
- rescue IOError
144
- # the TCP connection is already dead
145
- end
146
- end
147
- end
148
-
149
- # Determines the set of servers within the current task's scope and
150
- # establishes connections to them, and then yields that list of
151
- # servers.
152
- def execute_on_servers(options={})
153
- raise ArgumentError, "expected a block" unless block_given?
154
-
155
- if task = current_task
156
- servers = find_servers_for_task(task, options)
157
-
158
- if servers.empty?
159
- if ENV['HOSTFILTER'] || task.options.merge(options)[:on_no_matching_servers] == :continue
160
- logger.info "skipping `#{task.fully_qualified_name}' because no servers matched"
161
- return
162
- else
163
- raise Capistrano::NoMatchingServersError, "`#{task.fully_qualified_name}' is only run for servers matching #{task.options.inspect}, but no servers matched"
164
- end
165
- end
166
-
167
- if task.continue_on_error?
168
- servers.delete_if { |s| has_failed?(s) }
169
- return if servers.empty?
170
- end
171
- else
172
- servers = find_servers(options)
173
- if servers.empty?
174
- raise Capistrano::NoMatchingServersError, "no servers found to match #{options.inspect}" if options[:on_no_matching_servers] != :continue
175
- return
176
- end
177
- end
178
-
179
- servers = [servers.first] if options[:once]
180
- logger.trace "servers: #{servers.map { |s| s.host }.inspect}"
181
-
182
- max_hosts = (options[:max_hosts] || (task && task.max_hosts) || servers.size).to_i
183
- is_subset = max_hosts < servers.size
184
-
185
- # establish connections to those servers in groups of max_hosts, as necessary
186
- servers.each_slice(max_hosts) do |servers_slice|
187
- begin
188
- establish_connections_to(servers_slice)
189
- rescue ConnectionError => error
190
- raise error unless task && task.continue_on_error?
191
- error.hosts.each do |h|
192
- servers_slice.delete(h)
193
- failed!(h)
194
- end
195
- end
196
-
197
- begin
198
- yield servers_slice
199
- rescue RemoteError => error
200
- raise error unless task && task.continue_on_error?
201
- error.hosts.each { |h| failed!(h) }
202
- end
203
-
204
- # if dealing with a subset (e.g., :max_hosts is less than the
205
- # number of servers available) teardown the subset of connections
206
- # that were just made, so that we can make room for the next subset.
207
- teardown_connections_to(servers_slice) if is_subset
208
- end
209
- end
210
-
211
- private
212
-
213
- # We establish the connection by creating a thread in a new method--this
214
- # prevents problems with the thread's scope seeing the wrong 'server'
215
- # variable if the thread just happens to take too long to start up.
216
- def establish_connection_to(server, failures=nil)
217
- current_thread = Thread.current
218
- Thread.new { safely_establish_connection_to(server, current_thread, failures) }
219
- end
220
-
221
- def safely_establish_connection_to(server, thread, failures=nil)
222
- thread[:sessions] ||= {}
223
- thread[:sessions][server] ||= connection_factory.connect_to(server)
224
- rescue Exception => err
225
- raise unless failures
226
- failures << { :server => server, :error => err }
227
- end
228
- end
229
- end
230
- end