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,110 @@
1
+ require "capistrano/proc_helpers"
2
+ require "delegate"
3
+
4
+ module Capistrano
5
+ class Configuration
6
+ # Decorates a Variables object to additionally perform an optional set of
7
+ # user-supplied validation rules. Each rule for a given key is invoked
8
+ # immediately whenever `set` is called with a value for that key.
9
+ #
10
+ # If `set` is called with a callable value or a block, validation is not
11
+ # performed immediately. Instead, the validation rules are invoked the first
12
+ # time `fetch` is used to access the value.
13
+ #
14
+ # A rule is simply a block that accepts two arguments: key and value. It is
15
+ # up to the rule to raise an exception when it deems the value is invalid
16
+ # (or just print a warning).
17
+ #
18
+ # Rules can be registered using the DSL like this:
19
+ #
20
+ # validate(:my_key) do |key, value|
21
+ # # rule goes here
22
+ # end
23
+ #
24
+ class ValidatedVariables < SimpleDelegator
25
+ include Capistrano::ProcHelpers
26
+
27
+ def initialize(variables)
28
+ super(variables)
29
+ @validators = {}
30
+ end
31
+
32
+ # Decorate Variables#set to add validation behavior.
33
+ def set(key, value=nil, &block)
34
+ assert_value_or_block_not_both(value, block)
35
+
36
+ # Skip validation behavior if no validators are registered for this key
37
+ return super unless validators.key?(key)
38
+
39
+ value_to_evaluate = block || value
40
+
41
+ if callable_without_parameters?(value_to_evaluate)
42
+ super(key, assert_valid_later(key, value_to_evaluate), &nil)
43
+ else
44
+ assert_valid_now(key, value_to_evaluate)
45
+ super
46
+ end
47
+ end
48
+
49
+ # Register a validation rule for the given key.
50
+ def validate(key, &validator)
51
+ vs = (validators[key] || [])
52
+ vs << validator
53
+ validators[key] = vs
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :validators
59
+
60
+ # Given a callable that provides a value, wrap the callable with another
61
+ # object that responds to `call`. This new object will perform validation
62
+ # and then return the original callable's value.
63
+ #
64
+ # If the callable is a `Question`, the object returned by this method will
65
+ # also be a `Question` (a `ValidatedQuestion`, to be precise). This
66
+ # ensures that `is_a?(Question)` remains true even after the validation
67
+ # wrapper is applied. This is needed so that `Configuration#is_question?`
68
+ # works as expected.
69
+ #
70
+ def assert_valid_later(key, callable)
71
+ validation_callback = proc do
72
+ value = callable.call
73
+ assert_valid_now(key, value)
74
+ value
75
+ end
76
+
77
+ if callable.is_a?(Question)
78
+ ValidatedQuestion.new(validation_callback)
79
+ else
80
+ validation_callback
81
+ end
82
+ end
83
+
84
+ # Runs all validation rules registered for the given key against the
85
+ # user-supplied value for that variable. If no validator raises an
86
+ # exception, the value is assumed to be valid.
87
+ def assert_valid_now(key, value)
88
+ validators[key].each do |validator|
89
+ validator.call(key, value)
90
+ end
91
+ end
92
+
93
+ def assert_value_or_block_not_both(value, block)
94
+ return if value.nil? || block.nil?
95
+ raise Capistrano::ValidationError,
96
+ "Value and block both passed to Configuration#set"
97
+ end
98
+
99
+ class ValidatedQuestion < Question
100
+ def initialize(validator)
101
+ @validator = validator
102
+ end
103
+
104
+ def call
105
+ @validator.call
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,126 +1,111 @@
1
- require 'thread'
1
+ require "capistrano/proc_helpers"
2
2
 
3
3
  module Capistrano
4
4
  class Configuration
5
- module Variables
6
- def self.included(base) #:nodoc:
7
- %w(initialize respond_to? method_missing).each do |m|
8
- base_name = m[/^\w+/]
9
- punct = m[/\W+$/]
10
- base.send :alias_method, "#{base_name}_without_variables#{punct}", m
11
- base.send :alias_method, m, "#{base_name}_with_variables#{punct}"
12
- end
5
+ # Holds the variables assigned at Capistrano runtime via `set` and retrieved
6
+ # with `fetch`. Does internal bookkeeping to help identify user mistakes
7
+ # like spelling errors or unused variables that may lead to unexpected
8
+ # behavior.
9
+ class Variables
10
+ CAPISTRANO_LOCATION = File.expand_path("../..", __FILE__).freeze
11
+ IGNORED_LOCATIONS = [
12
+ "#{CAPISTRANO_LOCATION}/configuration/variables.rb:",
13
+ "#{CAPISTRANO_LOCATION}/configuration.rb:",
14
+ "#{CAPISTRANO_LOCATION}/dsl/env.rb:",
15
+ "/dsl.rb:",
16
+ "/forwardable.rb:"
17
+ ].freeze
18
+ private_constant :CAPISTRANO_LOCATION, :IGNORED_LOCATIONS
19
+
20
+ include Capistrano::ProcHelpers
21
+
22
+ def initialize(values={})
23
+ @trusted_keys = []
24
+ @fetched_keys = []
25
+ @locations = {}
26
+ @values = values
27
+ @trusted = true
13
28
  end
14
29
 
15
- # The hash of variables that have been defined in this configuration
16
- # instance.
17
- attr_reader :variables
30
+ def untrusted!
31
+ @trusted = false
32
+ yield
33
+ ensure
34
+ @trusted = true
35
+ end
18
36
 
19
- # Set a variable to the given value.
20
- def set(variable, *args, &block)
21
- if variable.to_s !~ /^[_a-z]/
22
- raise ArgumentError, "invalid variable `#{variable}' (variables must begin with an underscore, or a lower-case letter)"
23
- end
37
+ def set(key, value=nil, &block)
38
+ @trusted_keys << key if trusted? && !@trusted_keys.include?(key)
39
+ remember_location(key)
40
+ values[key] = block || value
41
+ trace_set(key)
42
+ values[key]
43
+ end
24
44
 
25
- if !block_given? && args.empty? || block_given? && !args.empty?
26
- raise ArgumentError, "you must specify exactly one of either a value or a block"
27
- end
45
+ def fetch(key, default=nil, &block)
46
+ fetched_keys << key unless fetched_keys.include?(key)
47
+ peek(key, default, &block)
48
+ end
28
49
 
29
- if args.length > 1
30
- raise ArgumentError, "wrong number of arguments (#{args.length} for 1)"
50
+ # Internal use only.
51
+ def peek(key, default=nil, &block)
52
+ value = fetch_for(key, default, &block)
53
+ while callable_without_parameters?(value)
54
+ value = (values[key] = value.call)
31
55
  end
32
-
33
- value = args.empty? ? block : args.first
34
- sym = variable.to_sym
35
- protect(sym) { @variables[sym] = value }
56
+ value
36
57
  end
37
58
 
38
- alias :[]= :set
39
-
40
- # Removes any trace of the given variable.
41
- def unset(variable)
42
- sym = variable.to_sym
43
- protect(sym) do
44
- @original_procs.delete(sym)
45
- @variables.delete(sym)
46
- end
59
+ def fetch_for(key, default, &block)
60
+ block ? values.fetch(key, &block) : values.fetch(key, default)
47
61
  end
48
62
 
49
- # Returns true if the variable has been defined, and false otherwise.
50
- def exists?(variable)
51
- @variables.key?(variable.to_sym)
63
+ def delete(key)
64
+ values.delete(key)
52
65
  end
53
66
 
54
- # If the variable was originally a proc value, it will be reset to it's
55
- # original proc value. Otherwise, this method does nothing. It returns
56
- # true if the variable was actually reset.
57
- def reset!(variable)
58
- sym = variable.to_sym
59
- protect(sym) do
60
- if @original_procs.key?(sym)
61
- @variables[sym] = @original_procs.delete(sym)
62
- true
63
- else
64
- false
65
- end
66
- end
67
+ def trusted_keys
68
+ @trusted_keys.dup
67
69
  end
68
70
 
69
- # Access a named variable. If the value of the variable responds_to? :call,
70
- # #call will be invoked (without parameters) and the return value cached
71
- # and returned.
72
- def fetch(variable, *args)
73
- if !args.empty? && block_given?
74
- raise ArgumentError, "you must specify either a default value or a block, but not both"
75
- end
71
+ def untrusted_keys
72
+ keys - @trusted_keys
73
+ end
76
74
 
77
- sym = variable.to_sym
78
- protect(sym) do
79
- if !@variables.key?(sym)
80
- return args.first unless args.empty?
81
- return yield(variable) if block_given?
82
- raise IndexError, "`#{variable}' not found"
83
- end
84
-
85
- if @variables[sym].respond_to?(:call)
86
- @original_procs[sym] = @variables[sym]
87
- @variables[sym] = @variables[sym].call
88
- end
89
- end
75
+ def keys
76
+ values.keys
77
+ end
90
78
 
91
- @variables[sym]
79
+ # Keys that have been set, but which have never been fetched.
80
+ def unused_keys
81
+ keys - fetched_keys
92
82
  end
93
83
 
94
- def [](variable)
95
- fetch(variable, nil)
84
+ # Returns an array of source file location(s) where the given key was
85
+ # assigned (i.e. where `set` was called). If the key was never assigned,
86
+ # returns `nil`.
87
+ def source_locations(key)
88
+ locations[key]
96
89
  end
97
90
 
98
- def initialize_with_variables(*args) #:nodoc:
99
- initialize_without_variables(*args)
100
- @variables = {}
101
- @original_procs = {}
102
- @variable_locks = Hash.new { |h,k| h[k] = Mutex.new }
91
+ private
103
92
 
104
- set :ssh_options, {}
105
- set :logger, logger
106
- end
107
- private :initialize_with_variables
93
+ attr_reader :locations, :values, :fetched_keys
108
94
 
109
- def protect(variable)
110
- @variable_locks[variable.to_sym].synchronize { yield }
95
+ def trusted?
96
+ @trusted
111
97
  end
112
- private :protect
113
98
 
114
- def respond_to_with_variables?(sym, include_priv=false) #:nodoc:
115
- @variables.has_key?(sym.to_sym) || respond_to_without_variables?(sym, include_priv)
99
+ def remember_location(key)
100
+ location = caller.find do |line|
101
+ IGNORED_LOCATIONS.none? { |i| line.include?(i) }
102
+ end
103
+ (locations[key] ||= []) << location
116
104
  end
117
105
 
118
- def method_missing_with_variables(sym, *args, &block) #:nodoc:
119
- if args.length == 0 && block.nil? && @variables.has_key?(sym)
120
- self[sym]
121
- else
122
- method_missing_without_variables(sym, *args, &block)
123
- end
106
+ def trace_set(key)
107
+ return unless fetch(:print_config_variables, false)
108
+ puts "Config variable set: #{key.inspect} => #{values[key].inspect}"
124
109
  end
125
110
  end
126
111
  end
@@ -1,44 +1,189 @@
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'
1
+ require_relative "configuration/filter"
2
+ require_relative "configuration/question"
3
+ require_relative "configuration/plugin_installer"
4
+ require_relative "configuration/server"
5
+ require_relative "configuration/servers"
6
+ require_relative "configuration/validated_variables"
7
+ require_relative "configuration/variables"
15
8
 
16
9
  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.
10
+ class ValidationError < RuntimeError; end
11
+
20
12
  class Configuration
21
- # The logger instance defined for this configuration.
22
- attr_accessor :debug, :logger, :dry_run, :preserve_roles
13
+ def self.env
14
+ @env ||= new
15
+ end
16
+
17
+ def self.reset!
18
+ @env = new
19
+ end
20
+
21
+ extend Forwardable
22
+ attr_reader :variables
23
+ def_delegators :variables,
24
+ :set, :fetch, :fetch_for, :delete, :keys, :validate
25
+
26
+ def initialize(values={})
27
+ @variables = ValidatedVariables.new(Variables.new(values))
28
+ end
29
+
30
+ def ask(key, default=nil, options={})
31
+ question = Question.new(key, default, options)
32
+ set(key, question)
33
+ end
34
+
35
+ def set_if_empty(key, value=nil, &block)
36
+ set(key, value, &block) unless keys.include?(key)
37
+ end
38
+
39
+ def append(key, *values)
40
+ set(key, Array(fetch(key)).concat(values))
41
+ end
23
42
 
24
- def initialize(options={}) #:nodoc:
25
- @debug = false
26
- @dry_run = false
27
- @preserve_roles = false
28
- @logger = Logger.new(options)
43
+ def remove(key, *values)
44
+ set(key, Array(fetch(key)) - values)
29
45
  end
30
46
 
31
- # make the DSL easier to read when using lazy evaluation via lambdas
32
- alias defer lambda
47
+ def any?(key)
48
+ value = fetch(key)
49
+ if value && value.respond_to?(:any?)
50
+ begin
51
+ return value.any?
52
+ rescue ArgumentError # rubocop:disable Lint/HandleExceptions
53
+ # Gracefully ignore values whose `any?` method doesn't accept 0 args
54
+ end
55
+ end
33
56
 
34
- # The includes must come at the bottom, since they may redefine methods
35
- # defined in the base class.
36
- include Connections, Execution, Loading, Namespaces, Roles, Servers, Variables
57
+ !value.nil?
58
+ end
59
+
60
+ def is_question?(key)
61
+ value = fetch_for(key, nil)
62
+ !value.nil? && value.is_a?(Question)
63
+ end
64
+
65
+ def role(name, hosts, options={})
66
+ if name == :all
67
+ raise ArgumentError, "#{name} reserved name for role. Please choose another name"
68
+ end
69
+
70
+ servers.add_role(name, hosts, options)
71
+ end
72
+
73
+ def server(name, properties={})
74
+ servers.add_host(name, properties)
75
+ end
76
+
77
+ def roles_for(names)
78
+ servers.roles_for(names)
79
+ end
80
+
81
+ def role_properties_for(names, &block)
82
+ servers.role_properties_for(names, &block)
83
+ end
84
+
85
+ def primary(role)
86
+ servers.fetch_primary(role)
87
+ end
88
+
89
+ def backend
90
+ @backend ||= SSHKit
91
+ end
37
92
 
38
- # Mix in the actions
39
- include Actions::FileTransfer, Actions::Inspect, Actions::Invocation
93
+ attr_writer :backend
40
94
 
41
- # Must mix last, because it hooks into previously defined methods
42
- include Callbacks
95
+ def configure_backend
96
+ backend.configure do |sshkit|
97
+ configure_sshkit_output(sshkit)
98
+ sshkit.output_verbosity = fetch(:log_level)
99
+ sshkit.default_env = fetch(:default_env)
100
+ sshkit.backend = fetch(:sshkit_backend, SSHKit::Backend::Netssh)
101
+ sshkit.backend.configure do |backend|
102
+ backend.pty = fetch(:pty)
103
+ backend.connection_timeout = fetch(:connection_timeout)
104
+ backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options, {}))
105
+ end
106
+ end
107
+ end
108
+
109
+ def configure_scm
110
+ Capistrano::Configuration::SCMResolver.new.resolve
111
+ end
112
+
113
+ def timestamp
114
+ @timestamp ||= Time.now.utc
115
+ end
116
+
117
+ def add_filter(filter=nil, &block)
118
+ if block
119
+ raise ArgumentError, "Both a block and an object were given" if filter
120
+
121
+ filter = Object.new
122
+ def filter.filter(servers)
123
+ block.call(servers)
124
+ end
125
+ elsif !filter.respond_to? :filter
126
+ raise TypeError, "Provided custom filter <#{filter.inspect}> does " \
127
+ "not have a public 'filter' method"
128
+ end
129
+ @custom_filters ||= []
130
+ @custom_filters << filter
131
+ end
132
+
133
+ def setup_filters
134
+ @filters = cmdline_filters
135
+ @filters += @custom_filters if @custom_filters
136
+ @filters << Filter.new(:role, ENV["ROLES"]) if ENV["ROLES"]
137
+ @filters << Filter.new(:host, ENV["HOSTS"]) if ENV["HOSTS"]
138
+ fh = fetch_for(:filter, {}) || {}
139
+ @filters << Filter.new(:host, fh[:hosts]) if fh[:hosts]
140
+ @filters << Filter.new(:role, fh[:roles]) if fh[:roles]
141
+ @filters << Filter.new(:host, fh[:host]) if fh[:host]
142
+ @filters << Filter.new(:role, fh[:role]) if fh[:role]
143
+ end
144
+
145
+ def add_cmdline_filter(type, values)
146
+ cmdline_filters << Filter.new(type, values)
147
+ end
148
+
149
+ def filter(list)
150
+ setup_filters if @filters.nil?
151
+ @filters.reduce(list) { |l, f| f.filter l }
152
+ end
153
+
154
+ def dry_run?
155
+ fetch(:sshkit_backend) == SSHKit::Backend::Printer
156
+ end
157
+
158
+ def install_plugin(plugin, load_hooks: true, load_immediately: false)
159
+ installer.install(plugin,
160
+ load_hooks: load_hooks,
161
+ load_immediately: load_immediately)
162
+ end
163
+
164
+ def scm_plugin_installed?
165
+ installer.scm_installed?
166
+ end
167
+
168
+ def servers
169
+ @servers ||= Servers.new
170
+ end
171
+
172
+ private
173
+
174
+ def cmdline_filters
175
+ @cmdline_filters ||= []
176
+ end
177
+
178
+ def installer
179
+ @installer ||= PluginInstaller.new
180
+ end
181
+
182
+ def configure_sshkit_output(sshkit)
183
+ format_args = [fetch(:format)]
184
+ format_args.push(fetch(:format_options)) if any?(:format_options)
185
+
186
+ sshkit.use_format(*format_args)
187
+ end
43
188
  end
44
189
  end
@@ -0,0 +1 @@
1
+ load File.expand_path("../tasks/console.rake", __FILE__)
@@ -0,0 +1,36 @@
1
+ validate :application do |_key, value|
2
+ changed_value = value.gsub(/[^A-Z0-9\.\-]/i, "_")
3
+ if value != changed_value
4
+ warn %Q(The :application value "#{value}" is invalid!)
5
+ warn "Use only letters, numbers, hyphens, dots, and underscores. For example:"
6
+ warn " set :application, '#{changed_value}'"
7
+ raise Capistrano::ValidationError
8
+ end
9
+ end
10
+
11
+ %i(git_strategy hg_strategy svn_strategy).each do |strategy|
12
+ validate(strategy) do |key, _value|
13
+ warn(
14
+ "[Deprecation Warning] #{key} is deprecated and will be removed in "\
15
+ "Capistrano 3.7.0.\n"\
16
+ "https://github.com/capistrano/capistrano/blob/master/UPGRADING-3.7.md"
17
+ )
18
+ end
19
+ end
20
+
21
+ # We use a special :_default_git value so that SCMResolver can tell whether the
22
+ # default has been replaced by the user via `set`.
23
+ set_if_empty :scm, Capistrano::Configuration::SCMResolver::DEFAULT_GIT
24
+ set_if_empty :branch, "master"
25
+ set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" }
26
+ set_if_empty :tmp_dir, "/tmp"
27
+
28
+ set_if_empty :default_env, {}
29
+ set_if_empty :keep_releases, 5
30
+
31
+ set_if_empty :format, :airbrussh
32
+ set_if_empty :log_level, :debug
33
+
34
+ set_if_empty :pty, false
35
+
36
+ set_if_empty :local_user, -> { ENV["USER"] || ENV["LOGNAME"] || ENV["USERNAME"] }
@@ -0,0 +1,3 @@
1
+ require "capistrano/framework"
2
+
3
+ load File.expand_path("../tasks/deploy.rake", __FILE__)
@@ -0,0 +1,19 @@
1
+ require "capistrano/doctor/output_helpers"
2
+
3
+ module Capistrano
4
+ module Doctor
5
+ class EnvironmentDoctor
6
+ include Capistrano::Doctor::OutputHelpers
7
+
8
+ def call
9
+ title("Environment")
10
+ puts <<-OUT.gsub(/^\s+/, "")
11
+ Ruby #{RUBY_DESCRIPTION}
12
+ Rubygems #{Gem::VERSION}
13
+ Bundler #{defined?(Bundler::VERSION) ? Bundler::VERSION : 'N/A'}
14
+ Command #{$PROGRAM_NAME} #{ARGV.join(' ')}
15
+ OUT
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ require "capistrano/doctor/output_helpers"
2
+
3
+ module Capistrano
4
+ module Doctor
5
+ # Prints table of all Capistrano-related gems and their version numbers. If
6
+ # there is a newer version of a gem available, call attention to it.
7
+ class GemsDoctor
8
+ include Capistrano::Doctor::OutputHelpers
9
+
10
+ def call
11
+ title("Gems")
12
+ table(all_gem_names) do |gem, row|
13
+ row.yellow if update_available?(gem)
14
+ row << gem
15
+ row << installed_gem_version(gem)
16
+ row << "(update available)" if update_available?(gem)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def installed_gem_version(gem_name)
23
+ Gem.loaded_specs[gem_name].version
24
+ end
25
+
26
+ def update_available?(gem_name)
27
+ latest = Gem.latest_version_for(gem_name)
28
+ return false if latest.nil?
29
+ latest > installed_gem_version(gem_name)
30
+ end
31
+
32
+ def all_gem_names
33
+ core_gem_names + plugin_gem_names
34
+ end
35
+
36
+ def core_gem_names
37
+ %w(capistrano airbrussh rake sshkit net-ssh) & Gem.loaded_specs.keys
38
+ end
39
+
40
+ def plugin_gem_names
41
+ (Gem.loaded_specs.keys - ["capistrano"]).grep(/capistrano/).sort
42
+ end
43
+ end
44
+ end
45
+ end