capistrano 2.8.0 → 3.19.0

Sign up to get free protection for your applications and to get access to all the features.
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