capistrano 3.4.0 → 3.17.1

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 (138) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +129 -0
  3. data/.github/issue_template.md +19 -0
  4. data/.github/pull_request_template.md +22 -0
  5. data/.github/release-drafter.yml +17 -0
  6. data/.github/workflows/push.yml +12 -0
  7. data/.gitignore +8 -5
  8. data/.rubocop.yml +62 -0
  9. data/CHANGELOG.md +1 -307
  10. data/CONTRIBUTING.md +63 -93
  11. data/DEVELOPMENT.md +127 -0
  12. data/Dangerfile +1 -0
  13. data/Gemfile +40 -3
  14. data/LICENSE.txt +1 -1
  15. data/README.md +127 -44
  16. data/RELEASING.md +17 -0
  17. data/Rakefile +13 -2
  18. data/UPGRADING-3.7.md +86 -0
  19. data/bin/cap +1 -1
  20. data/capistrano.gemspec +21 -24
  21. data/features/deploy.feature +35 -1
  22. data/features/doctor.feature +11 -0
  23. data/features/installation.feature +8 -3
  24. data/features/stage_failure.feature +9 -0
  25. data/features/step_definitions/assertions.rb +51 -18
  26. data/features/step_definitions/cap_commands.rb +9 -0
  27. data/features/step_definitions/setup.rb +53 -9
  28. data/features/subdirectory.feature +9 -0
  29. data/features/support/env.rb +5 -5
  30. data/features/support/remote_command_helpers.rb +12 -6
  31. data/features/support/vagrant_helpers.rb +17 -11
  32. data/lib/Capfile +1 -1
  33. data/lib/capistrano/all.rb +10 -10
  34. data/lib/capistrano/application.rb +47 -34
  35. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  36. data/lib/capistrano/configuration/filter.rb +17 -47
  37. data/lib/capistrano/configuration/host_filter.rb +29 -0
  38. data/lib/capistrano/configuration/null_filter.rb +9 -0
  39. data/lib/capistrano/configuration/plugin_installer.rb +51 -0
  40. data/lib/capistrano/configuration/question.rb +31 -9
  41. data/lib/capistrano/configuration/role_filter.rb +29 -0
  42. data/lib/capistrano/configuration/scm_resolver.rb +149 -0
  43. data/lib/capistrano/configuration/server.rb +29 -23
  44. data/lib/capistrano/configuration/servers.rb +21 -14
  45. data/lib/capistrano/configuration/validated_variables.rb +110 -0
  46. data/lib/capistrano/configuration/variables.rb +112 -0
  47. data/lib/capistrano/configuration.rb +91 -44
  48. data/lib/capistrano/defaults.rb +26 -4
  49. data/lib/capistrano/deploy.rb +1 -1
  50. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  51. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  52. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  53. data/lib/capistrano/doctor/servers_doctor.rb +105 -0
  54. data/lib/capistrano/doctor/variables_doctor.rb +74 -0
  55. data/lib/capistrano/doctor.rb +6 -0
  56. data/lib/capistrano/dotfile.rb +1 -2
  57. data/lib/capistrano/dsl/env.rb +9 -47
  58. data/lib/capistrano/dsl/paths.rb +11 -25
  59. data/lib/capistrano/dsl/stages.rb +14 -2
  60. data/lib/capistrano/dsl/task_enhancements.rb +7 -12
  61. data/lib/capistrano/dsl.rb +47 -16
  62. data/lib/capistrano/framework.rb +1 -1
  63. data/lib/capistrano/i18n.rb +32 -24
  64. data/lib/capistrano/immutable_task.rb +30 -0
  65. data/lib/capistrano/install.rb +1 -1
  66. data/lib/capistrano/plugin.rb +95 -0
  67. data/lib/capistrano/proc_helpers.rb +13 -0
  68. data/lib/capistrano/scm/git.rb +100 -0
  69. data/lib/capistrano/scm/hg.rb +55 -0
  70. data/lib/capistrano/scm/plugin.rb +13 -0
  71. data/lib/capistrano/scm/svn.rb +56 -0
  72. data/lib/capistrano/scm/tasks/git.rake +73 -0
  73. data/lib/capistrano/scm/tasks/hg.rake +53 -0
  74. data/lib/capistrano/scm/tasks/svn.rake +53 -0
  75. data/lib/capistrano/scm.rb +7 -20
  76. data/lib/capistrano/setup.rb +20 -6
  77. data/lib/capistrano/tasks/console.rake +4 -8
  78. data/lib/capistrano/tasks/deploy.rake +105 -73
  79. data/lib/capistrano/tasks/doctor.rake +24 -0
  80. data/lib/capistrano/tasks/framework.rake +13 -14
  81. data/lib/capistrano/tasks/install.rake +14 -15
  82. data/lib/capistrano/templates/Capfile +21 -10
  83. data/lib/capistrano/templates/deploy.rb.erb +17 -26
  84. data/lib/capistrano/templates/stage.rb.erb +9 -9
  85. data/lib/capistrano/upload_task.rb +1 -1
  86. data/lib/capistrano/version.rb +1 -1
  87. data/lib/capistrano/version_validator.rb +5 -10
  88. data/spec/integration/dsl_spec.rb +289 -240
  89. data/spec/integration_spec_helper.rb +3 -5
  90. data/spec/lib/capistrano/application_spec.rb +23 -39
  91. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  92. data/spec/lib/capistrano/configuration/filter_spec.rb +83 -85
  93. data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
  94. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  95. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
  96. data/spec/lib/capistrano/configuration/question_spec.rb +58 -26
  97. data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
  98. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
  99. data/spec/lib/capistrano/configuration/server_spec.rb +106 -113
  100. data/spec/lib/capistrano/configuration/servers_spec.rb +129 -145
  101. data/spec/lib/capistrano/configuration_spec.rb +224 -63
  102. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  103. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
  104. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  105. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
  106. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
  107. data/spec/lib/capistrano/dsl/paths_spec.rb +97 -59
  108. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +57 -37
  109. data/spec/lib/capistrano/dsl_spec.rb +84 -11
  110. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  111. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  112. data/spec/lib/capistrano/scm/git_spec.rb +184 -0
  113. data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
  114. data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
  115. data/spec/lib/capistrano/scm_spec.rb +7 -8
  116. data/spec/lib/capistrano/upload_task_spec.rb +7 -7
  117. data/spec/lib/capistrano/version_validator_spec.rb +61 -46
  118. data/spec/lib/capistrano_spec.rb +2 -3
  119. data/spec/spec_helper.rb +21 -8
  120. data/spec/support/Vagrantfile +9 -10
  121. data/spec/support/tasks/database.rake +3 -3
  122. data/spec/support/tasks/fail.rake +4 -3
  123. data/spec/support/tasks/failed.rake +2 -2
  124. data/spec/support/tasks/plugin.rake +6 -0
  125. data/spec/support/tasks/root.rake +4 -4
  126. data/spec/support/test_app.rb +64 -39
  127. metadata +100 -55
  128. data/.travis.yml +0 -13
  129. data/features/remote_file_task.feature +0 -14
  130. data/lib/capistrano/git.rb +0 -46
  131. data/lib/capistrano/hg.rb +0 -43
  132. data/lib/capistrano/svn.rb +0 -38
  133. data/lib/capistrano/tasks/git.rake +0 -81
  134. data/lib/capistrano/tasks/hg.rake +0 -52
  135. data/lib/capistrano/tasks/svn.rake +0 -52
  136. data/spec/lib/capistrano/git_spec.rb +0 -81
  137. data/spec/lib/capistrano/hg_spec.rb +0 -81
  138. data/spec/lib/capistrano/svn_spec.rb +0 -79
@@ -1,9 +1,8 @@
1
1
  module Capistrano
2
2
  class Application < Rake::Application
3
-
4
3
  def initialize
5
4
  super
6
- @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb} << capfile
5
+ @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb}
7
6
  end
8
7
 
9
8
  def name
@@ -21,11 +20,11 @@ module Capistrano
21
20
  switch =~ /--#{Regexp.union(not_applicable_to_capistrano)}/
22
21
  end
23
22
 
24
- super.push(version, dry_run, roles, hostfilter)
23
+ super.push(version, dry_run, roles, hostfilter, print_config_variables)
25
24
  end
26
25
 
27
26
  def handle_options
28
- options.rakelib = ['rakelib']
27
+ options.rakelib = ["rakelib"]
29
28
  options.trace_output = $stderr
30
29
 
31
30
  OptionParser.new do |opts|
@@ -48,11 +47,10 @@ module Capistrano
48
47
  end
49
48
 
50
49
  standard_rake_options.each { |args| opts.on(*args) }
51
- opts.environment('RAKEOPT')
50
+ opts.environment("RAKEOPT")
52
51
  end.parse!
53
52
  end
54
53
 
55
-
56
54
  def top_level_tasks
57
55
  if tasks_without_stage_dependency.include?(@top_level_tasks.first)
58
56
  @top_level_tasks
@@ -63,13 +61,10 @@ module Capistrano
63
61
 
64
62
  def display_error_message(ex)
65
63
  unless options.backtrace
66
- if loc = Rake.application.find_rakefile_location
67
- whitelist = (@imported.dup << loc[0]).map{|f| File.absolute_path(f, loc[1])}
68
- pattern = %r@^(?!#{whitelist.map{|p| Regexp.quote(p)}.join('|')})@
69
- Rake.application.options.suppress_backtrace_pattern = pattern
70
- end
64
+ Rake.application.options.suppress_backtrace_pattern = backtrace_pattern if backtrace_pattern
71
65
  trace "(Backtrace restricted to imported tasks)"
72
66
  end
67
+
73
68
  super
74
69
  end
75
70
 
@@ -81,60 +76,78 @@ module Capistrano
81
76
  end
82
77
  end
83
78
 
79
+ # allows the `cap install` task to load without a capfile
80
+ def find_rakefile_location
81
+ if (location = super).nil?
82
+ [capfile, Dir.pwd]
83
+ else
84
+ location
85
+ end
86
+ end
87
+
84
88
  private
85
89
 
90
+ def backtrace_pattern
91
+ loc = Rake.application.find_rakefile_location
92
+ return unless loc
93
+
94
+ whitelist = (@imported.dup << loc[0]).map { |f| File.absolute_path(f, loc[1]) }
95
+ /^(?!#{whitelist.map { |p| Regexp.quote(p) }.join('|')})/
96
+ end
97
+
86
98
  def load_imports
87
- if options.show_tasks
88
- invoke 'load:defaults'
89
- set(:stage, '')
99
+ if options.show_tasks && Rake::Task.task_defined?("load:defaults")
100
+ invoke "load:defaults"
101
+ set(:stage, "")
90
102
  Dir[deploy_config_path].each { |f| add_import f }
91
103
  end
92
104
 
93
105
  super
94
106
  end
95
107
 
96
- # allows the `cap install` task to load without a capfile
97
108
  def capfile
98
- File.expand_path(File.join(File.dirname(__FILE__),'..','Capfile'))
109
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "Capfile"))
99
110
  end
100
111
 
101
112
  def version
102
- ['--version', '-V',
113
+ ["--version", "-V",
103
114
  "Display the program version.",
104
- lambda { |value|
105
- puts "Capistrano Version: #{Capistrano::VERSION} (Rake Version: #{RAKEVERSION})"
115
+ lambda do |_value|
116
+ puts "Capistrano Version: #{Capistrano::VERSION} (Rake Version: #{Rake::VERSION})"
106
117
  exit
107
- }
108
- ]
118
+ end]
109
119
  end
110
120
 
111
121
  def dry_run
112
- ['--dry-run', '-n',
122
+ ["--dry-run", "-n",
113
123
  "Do a dry run without executing actions",
114
- lambda { |value|
124
+ lambda do |_value|
115
125
  Configuration.env.set(:sshkit_backend, SSHKit::Backend::Printer)
116
- }
117
- ]
126
+ end]
118
127
  end
119
128
 
120
129
  def roles
121
- ['--roles ROLES', '-r',
130
+ ["--roles ROLES", "-r",
122
131
  "Run SSH commands only on hosts matching these roles",
123
- lambda { |value|
132
+ lambda do |value|
124
133
  Configuration.env.add_cmdline_filter(:role, value)
125
- }
126
- ]
134
+ end]
127
135
  end
128
136
 
129
137
  def hostfilter
130
- ['--hosts HOSTS', '-z',
138
+ ["--hosts HOSTS", "-z",
131
139
  "Run SSH commands only on matching hosts",
132
- lambda { |value|
140
+ lambda do |value|
133
141
  Configuration.env.add_cmdline_filter(:host, value)
134
- }
135
- ]
142
+ end]
136
143
  end
137
144
 
145
+ def print_config_variables
146
+ ["--print-config-variables", "-p",
147
+ "Display the defined config variables before starting the deployment tasks.",
148
+ lambda do |_value|
149
+ Configuration.env.set(:print_config_variables, true)
150
+ end]
151
+ end
138
152
  end
139
-
140
153
  end
@@ -0,0 +1,9 @@
1
+ module Capistrano
2
+ class Configuration
3
+ class EmptyFilter
4
+ def filter(_servers)
5
+ []
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,55 +1,25 @@
1
- require 'capistrano/configuration'
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"
2
6
 
3
7
  module Capistrano
4
8
  class Configuration
5
9
  class Filter
6
- def initialize type, values = nil
7
- raise "Invalid filter type #{type}" unless [:host,:role].include? type
8
- av = Array(values).dup
9
- @mode = case
10
- when av.size == 0 then :none
11
- when av.include?(:all) then :all
12
- else type
13
- end
14
- @rex = case @mode
15
- when :host
16
- av.map!{|v| (v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/) ? v.split(',') : v }
17
- av.flatten!
18
- av.map! do |v|
19
- case v
20
- when Regexp then v
21
- else
22
- vs = v.to_s
23
- vs =~ /^[-A-Za-z0-9.]+$/ ? vs : Regexp.new(vs)
24
- end
25
- end
26
- Regexp.union av
27
- when :role
28
- av.map!{|v| v.is_a?(String) ? v.split(',') : v }
29
- av.flatten!
30
- av.map! do |v|
31
- case v
32
- when Regexp then v
33
- else
34
- vs = v.to_s
35
- vs =~ %r{^/(.+)/$} ? Regexp.new($1) : %r{^#{vs}$}
36
- end
37
- end
38
- Regexp.union av
39
- else
40
- nil
41
- end
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
42
19
  end
43
- def filter servers
44
- as = Array(servers)
45
- case @mode
46
- when :none then return []
47
- when :all then return servers
48
- when :host
49
- as.select {|s| @rex.match s.hostname}
50
- when :role
51
- as.select {|s| s.roles.any? {|r| @rex.match r} }
52
- end
20
+
21
+ def filter(servers)
22
+ @strategy.filter servers
53
23
  end
54
24
  end
55
25
  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
@@ -1,9 +1,10 @@
1
1
  module Capistrano
2
2
  class Configuration
3
3
  class Question
4
-
5
- def initialize(key, default, options = {})
6
- @key, @default, @options = key, default, options
4
+ def initialize(key, default, options={})
5
+ @key = key
6
+ @default = default
7
+ @options = options
7
8
  end
8
9
 
9
10
  def call
@@ -12,10 +13,12 @@ module Capistrano
12
13
  end
13
14
 
14
15
  private
16
+
15
17
  attr_reader :key, :default, :options
16
18
 
17
19
  def ask_question
18
20
  $stdout.print question
21
+ $stdout.flush
19
22
  end
20
23
 
21
24
  def value_or_default
@@ -28,27 +31,46 @@ module Capistrano
28
31
 
29
32
  def response
30
33
  return @response if defined? @response
31
-
34
+
32
35
  @response = (gets || "").chomp
33
36
  end
34
-
37
+
35
38
  def gets
39
+ return unless stdin.tty?
40
+
36
41
  if echo?
37
- $stdin.gets
42
+ stdin.gets
38
43
  else
39
- $stdin.noecho(&:gets).tap{ $stdout.print "\n" }
44
+ stdin.noecho(&:gets).tap { $stdout.print "\n" }
40
45
  end
41
46
  rescue Errno::EIO
42
47
  # when stdio gets closed
48
+ return
43
49
  end
44
-
50
+
45
51
  def question
46
- I18n.t(:question, key: key, default_value: default, scope: :capistrano)
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
47
61
  end
48
62
 
49
63
  def echo?
50
64
  (options || {}).fetch(:echo, true)
51
65
  end
66
+
67
+ def stdin
68
+ (options || {}).fetch(:stdin, $stdin)
69
+ end
70
+
71
+ def prompt
72
+ (options || {}).fetch(:prompt, nil)
73
+ end
52
74
  end
53
75
  end
54
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