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
@@ -0,0 +1,26 @@
1
+ require "capistrano/configuration"
2
+ require "capistrano/configuration/empty_filter"
3
+ require "capistrano/configuration/host_filter"
4
+ require "capistrano/configuration/null_filter"
5
+ require "capistrano/configuration/role_filter"
6
+
7
+ module Capistrano
8
+ class Configuration
9
+ class Filter
10
+ def initialize(type, values=nil)
11
+ raise "Invalid filter type #{type}" unless %i(host role).include? type
12
+ av = Array(values)
13
+ @strategy = if av.empty? then EmptyFilter.new
14
+ elsif av.include?(:all) || av.include?("all") then NullFilter.new
15
+ elsif type == :host then HostFilter.new(values)
16
+ elsif type == :role then RoleFilter.new(values)
17
+ else NullFilter.new
18
+ end
19
+ end
20
+
21
+ def filter(servers)
22
+ @strategy.filter servers
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module Capistrano
2
+ class Configuration
3
+ class HostFilter
4
+ def initialize(values)
5
+ av = Array(values).dup
6
+ av = av.flat_map { |v| v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/ ? v.split(",") : v }
7
+ @rex = regex_matcher(av)
8
+ end
9
+
10
+ def filter(servers)
11
+ Array(servers).select { |s| @rex.match s.to_s }
12
+ end
13
+
14
+ private
15
+
16
+ def regex_matcher(values)
17
+ values.map! do |v|
18
+ case v
19
+ when Regexp then v
20
+ else
21
+ vs = v.to_s
22
+ vs =~ /^[-A-Za-z0-9.]+$/ ? /^#{Regexp.quote(vs)}$/ : Regexp.new(vs)
23
+ end
24
+ end
25
+ Regexp.union values
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ module Capistrano
2
+ class Configuration
3
+ class NullFilter
4
+ def filter(servers)
5
+ servers
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ # Encapsulates the logic for installing plugins into Capistrano. Plugins must
2
+ # simply conform to a basic API; the PluginInstaller takes care of invoking the
3
+ # API at appropriate times.
4
+ #
5
+ # This class is not used directly; instead it is typically accessed via the
6
+ # `install_plugin` method of the Capistrano DSL.
7
+ #
8
+ module Capistrano
9
+ class Configuration
10
+ class PluginInstaller
11
+ # "Installs" a Plugin into Capistrano by loading its tasks, hooks, and
12
+ # defaults at the appropriate time. The hooks in particular can be
13
+ # skipped, if you want full control over when and how the plugin's tasks
14
+ # are executed. Simply pass `load_hooks:false` to opt out.
15
+ #
16
+ # The plugin class or instance may be provided. These are equivalent:
17
+ #
18
+ # install(Capistrano::SCM::Git)
19
+ # install(Capistrano::SCM::Git.new)
20
+ #
21
+ # Note that the :load_immediately flag is for internal use only and will
22
+ # be removed in an upcoming release.
23
+ #
24
+ def install(plugin, load_hooks: true, load_immediately: false)
25
+ plugin = plugin.is_a?(Class) ? plugin.new : plugin
26
+
27
+ plugin.define_tasks
28
+ plugin.register_hooks if load_hooks
29
+ @scm_installed ||= provides_scm?(plugin)
30
+
31
+ if load_immediately
32
+ plugin.set_defaults
33
+ else
34
+ Rake::Task.define_task("load:defaults") do
35
+ plugin.set_defaults
36
+ end
37
+ end
38
+ end
39
+
40
+ def scm_installed?
41
+ @scm_installed
42
+ end
43
+
44
+ private
45
+
46
+ def provides_scm?(plugin)
47
+ plugin.respond_to?(:scm?) && plugin.scm?
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ module Capistrano
2
+ class Configuration
3
+ class Question
4
+ def initialize(key, default, options={})
5
+ @key = key
6
+ @default = default
7
+ @options = options
8
+ end
9
+
10
+ def call
11
+ ask_question
12
+ value_or_default
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :key, :default, :options
18
+
19
+ def ask_question
20
+ $stdout.print question
21
+ $stdout.flush
22
+ end
23
+
24
+ def value_or_default
25
+ if response.empty?
26
+ default
27
+ else
28
+ response
29
+ end
30
+ end
31
+
32
+ def response
33
+ return @response if defined? @response
34
+
35
+ @response = (gets || "").chomp
36
+ end
37
+
38
+ def gets
39
+ return unless stdin.tty?
40
+
41
+ if echo?
42
+ stdin.gets
43
+ else
44
+ stdin.noecho(&:gets).tap { $stdout.print "\n" }
45
+ end
46
+ rescue Errno::EIO
47
+ # when stdio gets closed
48
+ return
49
+ end
50
+
51
+ def question
52
+ if prompt && default.nil?
53
+ I18n.t(:question_prompt, key: prompt, scope: :capistrano)
54
+ elsif prompt
55
+ I18n.t(:question_prompt_default, key: prompt, default_value: default, scope: :capistrano)
56
+ elsif default.nil?
57
+ I18n.t(:question, key: key, scope: :capistrano)
58
+ else
59
+ I18n.t(:question_default, key: key, default_value: default, scope: :capistrano)
60
+ end
61
+ end
62
+
63
+ def echo?
64
+ (options || {}).fetch(:echo, true)
65
+ end
66
+
67
+ def stdin
68
+ (options || {}).fetch(:stdin, $stdin)
69
+ end
70
+
71
+ def prompt
72
+ (options || {}).fetch(:prompt, nil)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ module Capistrano
2
+ class Configuration
3
+ class RoleFilter
4
+ def initialize(values)
5
+ av = Array(values).dup
6
+ av = av.flat_map { |v| v.is_a?(String) ? v.split(",") : v }
7
+ @rex = regex_matcher(av)
8
+ end
9
+
10
+ def filter(servers)
11
+ Array(servers).select { |s| s.is_a?(String) ? false : s.roles.any? { |r| @rex.match r } }
12
+ end
13
+
14
+ private
15
+
16
+ def regex_matcher(values)
17
+ values.map! do |v|
18
+ case v
19
+ when Regexp then v
20
+ else
21
+ vs = v.to_s
22
+ vs =~ %r{^/(.+)/$} ? Regexp.new($1) : /^#{Regexp.quote(vs)}$/
23
+ end
24
+ end
25
+ Regexp.union values
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,149 @@
1
+ module Capistrano
2
+ class Configuration
3
+ # In earlier versions of Capistrano, users would specify the desired SCM
4
+ # implementation using `set :scm, :git`, for example. Capistrano would then
5
+ # load the matching .rb file based on this variable.
6
+ #
7
+ # Now we expect users to explicitly `require` and call `new` on the desired
8
+ # SCM implementation in their Capfile. The `set` technique is deprecated.
9
+ #
10
+ # This SCMResolver class takes care of managing the transition from the old
11
+ # to new system. It maintains the legacy behavior, but prints deprecation
12
+ # warnings when it is used.
13
+ #
14
+ # To maintain backwards compatibility, the resolver will load the Git SCM by
15
+ # if default it determines that no SCM has been explicitly specified or
16
+ # loaded. To force no SCM to be used at all, use `set :scm, nil`. This hack
17
+ # won't be necessary once backwards compatibility is removed in a future
18
+ # version.
19
+ #
20
+ # TODO: Remove this class entirely in Capistrano 4.0.
21
+ #
22
+ class SCMResolver
23
+ DEFAULT_GIT = :"default-git"
24
+
25
+ include Capistrano::DSL
26
+
27
+ def resolve
28
+ return if scm_name.nil?
29
+ set(:scm, :git) if using_default_scm?
30
+
31
+ print_deprecation_warnings_if_applicable
32
+
33
+ # Note that `scm_plugin_installed?` comes from Capistrano::DSL
34
+ if scm_plugin_installed?
35
+ delete(:scm)
36
+ return
37
+ end
38
+
39
+ if built_in_scm_name?
40
+ load_built_in_scm
41
+ else
42
+ # Compatibility with existing 3.x third-party SCMs
43
+ register_legacy_scm_hooks
44
+ load_legacy_scm_by_name
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def using_default_scm?
51
+ return @using_default_scm if defined? @using_default_scm
52
+ @using_default_scm = (fetch(:scm) == DEFAULT_GIT)
53
+ end
54
+
55
+ def scm_name
56
+ fetch(:scm)
57
+ end
58
+
59
+ def load_built_in_scm
60
+ require "capistrano/scm/#{scm_name}"
61
+ scm_class = Object.const_get(built_in_scm_plugin_class_name)
62
+ # We use :load_immediately because we are initializing the SCM plugin
63
+ # late in the load process and therefore can't use the standard
64
+ # load:defaults technique.
65
+ install_plugin(scm_class, load_immediately: true)
66
+ end
67
+
68
+ def load_legacy_scm_by_name
69
+ load("capistrano/#{scm_name}.rb")
70
+ end
71
+
72
+ def third_party_scm_name?
73
+ !built_in_scm_name?
74
+ end
75
+
76
+ def built_in_scm_name?
77
+ %w(git hg svn).include?(scm_name.to_s.downcase)
78
+ end
79
+
80
+ def built_in_scm_plugin_class_name
81
+ "Capistrano::SCM::#{scm_name.to_s.capitalize}"
82
+ end
83
+
84
+ # rubocop:disable Style/GuardClause
85
+ def register_legacy_scm_hooks
86
+ if Rake::Task.task_defined?("deploy:new_release_path")
87
+ after "deploy:new_release_path", "#{scm_name}:create_release"
88
+ end
89
+
90
+ if Rake::Task.task_defined?("deploy:check")
91
+ before "deploy:check", "#{scm_name}:check"
92
+ end
93
+
94
+ if Rake::Task.task_defined?("deploy:set_current_revision")
95
+ before "deploy:set_current_revision",
96
+ "#{scm_name}:set_current_revision"
97
+ end
98
+ end
99
+ # rubocop:enable Style/GuardClause
100
+
101
+ def print_deprecation_warnings_if_applicable
102
+ if using_default_scm?
103
+ warn_add_git_to_capfile unless scm_plugin_installed?
104
+ elsif built_in_scm_name?
105
+ warn_set_scm_is_deprecated
106
+ elsif third_party_scm_name?
107
+ warn_third_party_scm_must_be_upgraded
108
+ end
109
+ end
110
+
111
+ def warn_set_scm_is_deprecated
112
+ $stderr.puts(<<-MESSAGE)
113
+ [Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated.
114
+ To ensure your project is compatible with future versions of Capistrano,
115
+ remove the :scm setting and instead add these lines to your Capfile after
116
+ `require "capistrano/deploy"`:
117
+
118
+ require "capistrano/scm/#{scm_name}"
119
+ install_plugin #{built_in_scm_plugin_class_name}
120
+
121
+ MESSAGE
122
+ end
123
+
124
+ def warn_add_git_to_capfile
125
+ $stderr.puts(<<-MESSAGE)
126
+ [Deprecation Notice] Future versions of Capistrano will not load the Git SCM
127
+ plugin by default. To silence this deprecation warning, add the following to
128
+ your Capfile after `require "capistrano/deploy"`:
129
+
130
+ require "capistrano/scm/git"
131
+ install_plugin Capistrano::SCM::Git
132
+
133
+ MESSAGE
134
+ end
135
+
136
+ def warn_third_party_scm_must_be_upgraded
137
+ $stderr.puts(<<-MESSAGE)
138
+ [Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated.
139
+ To ensure this custom SCM will work with future versions of Capistrano,
140
+ please upgrade it to a version that uses the new SCM plugin mechanism
141
+ documented here:
142
+
143
+ http://capistranorb.com/documentation/advanced-features/custom-scm
144
+
145
+ MESSAGE
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,137 @@
1
+ require "set"
2
+ module Capistrano
3
+ class Configuration
4
+ class Server < SSHKit::Host
5
+ extend Forwardable
6
+ def_delegators :properties, :roles, :fetch, :set
7
+
8
+ def self.[](host)
9
+ host.is_a?(Server) ? host : new(host)
10
+ end
11
+
12
+ def add_roles(roles)
13
+ Array(roles).each { |role| add_role(role) }
14
+ self
15
+ end
16
+ alias roles= add_roles
17
+
18
+ def add_role(role)
19
+ roles.add role.to_sym
20
+ self
21
+ end
22
+
23
+ def has_role?(role)
24
+ roles.include? role.to_sym
25
+ end
26
+
27
+ def select?(options)
28
+ options.each do |k, v|
29
+ callable = v.respond_to?(:call) ? v : ->(server) { server.fetch(v) }
30
+ result = \
31
+ case k
32
+ when :filter, :select
33
+ callable.call(self)
34
+ when :exclude
35
+ !callable.call(self)
36
+ else
37
+ fetch(k) == v
38
+ end
39
+ return false unless result
40
+ end
41
+
42
+ true
43
+ end
44
+
45
+ def primary
46
+ self if fetch(:primary)
47
+ end
48
+
49
+ def with(properties)
50
+ properties.each { |key, value| add_property(key, value) }
51
+ self
52
+ end
53
+
54
+ def properties
55
+ @properties ||= Properties.new
56
+ end
57
+
58
+ def netssh_options
59
+ @netssh_options ||= super.merge(fetch(:ssh_options) || {})
60
+ end
61
+
62
+ def roles_array
63
+ roles.to_a
64
+ end
65
+
66
+ def matches?(other)
67
+ # This matching logic must stay in sync with `Servers#add_host`.
68
+ hostname == other.hostname && port == other.port
69
+ end
70
+
71
+ private
72
+
73
+ def add_property(key, value)
74
+ if respond_to?("#{key}=")
75
+ send("#{key}=", value)
76
+ else
77
+ set(key, value)
78
+ end
79
+ end
80
+
81
+ class Properties
82
+ def initialize
83
+ @properties = {}
84
+ end
85
+
86
+ def set(key, value)
87
+ pval = @properties[key]
88
+ if pval.is_a?(Hash) && value.is_a?(Hash)
89
+ pval.merge!(value)
90
+ elsif pval.is_a?(Set) && value.is_a?(Set)
91
+ pval.merge(value)
92
+ elsif pval.is_a?(Array) && value.is_a?(Array)
93
+ pval.concat value
94
+ else
95
+ @properties[key] = value
96
+ end
97
+ end
98
+
99
+ def fetch(key)
100
+ @properties[key]
101
+ end
102
+
103
+ def respond_to_missing?(method, _include_all=false)
104
+ @properties.key?(method) || super
105
+ end
106
+
107
+ def roles
108
+ @roles ||= Set.new
109
+ end
110
+
111
+ def keys
112
+ @properties.keys
113
+ end
114
+
115
+ # rubocop:disable Style/MethodMissing
116
+ def method_missing(key, value=nil)
117
+ if value
118
+ set(lvalue(key), value)
119
+ else
120
+ fetch(key)
121
+ end
122
+ end
123
+ # rubocop:enable Style/MethodMissing
124
+
125
+ def to_h
126
+ @properties
127
+ end
128
+
129
+ private
130
+
131
+ def lvalue(key)
132
+ key.to_s.chomp("=").to_sym
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -1,115 +1,75 @@
1
+ require "set"
2
+ require "capistrano/configuration"
3
+ require "capistrano/configuration/filter"
4
+
1
5
  module Capistrano
2
6
  class Configuration
3
- module Servers
4
- # Identifies all servers that the given task should be executed on.
5
- # The options hash accepts the same arguments as #find_servers, and any
6
- # preexisting options there will take precedence over the options in
7
- # the task.
8
- def find_servers_for_task(task, options={})
9
- find_servers(task.options.merge(options))
10
- end
7
+ class Servers
8
+ include Enumerable
11
9
 
12
- # Attempts to find all defined servers that match the given criteria.
13
- # The options hash may include a :hosts option (which should specify
14
- # an array of host names or ServerDefinition instances), a :roles
15
- # option (specifying an array of roles), an :only option (specifying
16
- # a hash of key/value pairs that any matching server must match),
17
- # an :exception option (like :only, but the inverse), and a
18
- # :skip_hostfilter option to ignore the HOSTFILTER environment variable
19
- # described below.
20
- #
21
- # Additionally, if the HOSTS environment variable is set, it will take
22
- # precedence over any other options. Similarly, the ROLES environment
23
- # variable will take precedence over other options. If both HOSTS and
24
- # ROLES are given, HOSTS wins.
25
- #
26
- # Yet additionally, if the HOSTFILTER environment variable is set, it
27
- # will limit the result to hosts found in that (comma-separated) list.
28
- #
29
- # If the HOSTROLEFILTER environment variable is set, it will limit the
30
- # result to hosts found in that (comma-separated) list of roles
31
- #
32
- # Usage:
33
- #
34
- # # return all known servers
35
- # servers = find_servers
36
- #
37
- # # find all servers in the app role that are not exempted from
38
- # # deployment
39
- # servers = find_servers :roles => :app,
40
- # :except => { :no_release => true }
41
- #
42
- # # returns the given hosts, translated to ServerDefinition objects
43
- # servers = find_servers :hosts => "jamis@example.host.com"
44
- def find_servers(options={})
45
- return [] if options.key?(:hosts) && (options[:hosts].nil? || [] == options[:hosts])
46
- return [] if options.key?(:roles) && (options[:roles].nil? || [] == options[:roles])
47
-
48
- hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
49
-
50
- if hosts.any?
51
- if options[:skip_hostfilter]
52
- hosts.uniq
53
- else
54
- filter_server_list(hosts.uniq)
55
- end
10
+ def add_host(host, properties={})
11
+ new_host = Server[host]
12
+ new_host.port = properties[:port] if properties.key?(:port)
13
+ # This matching logic must stay in sync with `Server#matches?`.
14
+ key = ServerKey.new(new_host.hostname, new_host.port)
15
+ existing = servers_by_key[key]
16
+ if existing
17
+ existing.user = new_host.user if new_host.user
18
+ existing.with(properties)
56
19
  else
57
- roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
58
- roles = roles & Array(options[:roles]) if preserve_roles && !options[:roles].nil?
59
-
60
- only = options[:only] || {}
61
- except = options[:except] || {}
62
-
63
- servers = roles.inject([]) { |list, role| list.concat(self.roles[role]) }
64
- servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
65
- servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } }
66
-
67
- if options[:skip_hostfilter]
68
- servers.uniq
69
- else
70
- filter_server_list(servers.uniq)
71
- end
20
+ servers_by_key[key] = new_host.with(properties)
72
21
  end
73
22
  end
74
23
 
75
- protected
24
+ # rubocop:disable Security/MarshalLoad
25
+ def add_role(role, hosts, options={})
26
+ options_deepcopy = Marshal.dump(options.merge(roles: role))
27
+ Array(hosts).each { |host| add_host(host, Marshal.load(options_deepcopy)) }
28
+ end
29
+ # rubocop:enable Security/MarshalLoad
76
30
 
77
- def filter_server_list(servers)
78
- return servers unless ENV['HOSTFILTER'] or ENV['HOSTROLEFILTER']
79
- if ENV['HOSTFILTER']
80
- filters = ENV['HOSTFILTER'].split(/,/)
81
- servers.select { |server| filters.include?(server.host) }
82
- elsif ENV['HOSTROLEFILTER']
83
- filters = ENV['HOSTROLEFILTER'].split(/,/).map do |role|
84
- local_roles = roles[role.to_sym]
85
- if local_roles.is_a? Array
86
- roles[role.to_sym]
87
- else
88
- roles[role.to_sym].servers
31
+ def roles_for(names)
32
+ options = extract_options(names)
33
+ s = Filter.new(:role, names).filter(servers_by_key.values)
34
+ s.select { |server| server.select?(options) }
35
+ end
36
+
37
+ def role_properties_for(rolenames)
38
+ roles = rolenames.to_set
39
+ rps = Set.new unless block_given?
40
+ roles_for(rolenames).each do |host|
41
+ host.roles.intersection(roles).each do |role|
42
+ [host.properties.fetch(role)].flatten(1).each do |props|
43
+ if block_given?
44
+ yield host, role, props
45
+ else
46
+ rps << (props || {}).merge(role: role, hostname: host.hostname)
47
+ end
89
48
  end
90
- end.flatten
91
- servers.select { |server| filters.include?(server) }
49
+ end
92
50
  end
51
+ block_given? ? nil : rps
93
52
  end
94
53
 
95
- def server_list_from(hosts)
96
- hosts = hosts.split(/,/) if String === hosts
97
- hosts = build_list(hosts)
98
- hosts.map { |s| String === s ? ServerDefinition.new(s.strip) : s }
54
+ def fetch_primary(role)
55
+ hosts = roles_for([role])
56
+ hosts.find(&:primary) || hosts.first
99
57
  end
100
58
 
101
- def role_list_from(roles)
102
- roles = roles.split(/,/) if String === roles
103
- roles = build_list(roles)
104
- roles.map do |role|
105
- role = String === role ? role.strip.to_sym : role
106
- raise ArgumentError, "unknown role `#{role}'" unless self.roles.key?(role)
107
- role
108
- end
59
+ def each
60
+ servers_by_key.values.each { |server| yield server }
61
+ end
62
+
63
+ private
64
+
65
+ ServerKey = Struct.new(:hostname, :port)
66
+
67
+ def servers_by_key
68
+ @servers_by_key ||= {}
109
69
  end
110
70
 
111
- def build_list(list)
112
- Array(list).map { |item| item.respond_to?(:call) ? item.call : item }.flatten
71
+ def extract_options(array)
72
+ array.last.is_a?(::Hash) ? array.pop : {}
113
73
  end
114
74
  end
115
75
  end