capistrano 3.4.1 → 3.5.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -5
  3. data/.rubocop.yml +49 -0
  4. data/.travis.yml +5 -4
  5. data/CHANGELOG.md +72 -9
  6. data/CONTRIBUTING.md +61 -93
  7. data/DEVELOPMENT.md +122 -0
  8. data/Gemfile +2 -2
  9. data/LICENSE.txt +1 -1
  10. data/README.md +121 -43
  11. data/RELEASING.md +16 -0
  12. data/Rakefile +4 -1
  13. data/bin/cap +1 -1
  14. data/capistrano.gemspec +16 -21
  15. data/features/doctor.feature +11 -0
  16. data/features/step_definitions/assertions.rb +17 -17
  17. data/features/step_definitions/cap_commands.rb +0 -1
  18. data/features/step_definitions/setup.rb +12 -8
  19. data/features/support/env.rb +5 -5
  20. data/features/support/remote_command_helpers.rb +8 -6
  21. data/features/support/vagrant_helpers.rb +5 -4
  22. data/issue_template.md +21 -0
  23. data/lib/Capfile +5 -1
  24. data/lib/capistrano/all.rb +9 -10
  25. data/lib/capistrano/application.rb +36 -26
  26. data/lib/capistrano/configuration.rb +56 -41
  27. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  28. data/lib/capistrano/configuration/filter.rb +18 -47
  29. data/lib/capistrano/configuration/host_filter.rb +30 -0
  30. data/lib/capistrano/configuration/null_filter.rb +9 -0
  31. data/lib/capistrano/configuration/plugin_installer.rb +33 -0
  32. data/lib/capistrano/configuration/question.rb +10 -7
  33. data/lib/capistrano/configuration/role_filter.rb +30 -0
  34. data/lib/capistrano/configuration/server.rb +22 -23
  35. data/lib/capistrano/configuration/servers.rb +6 -7
  36. data/lib/capistrano/configuration/variables.rb +136 -0
  37. data/lib/capistrano/defaults.rb +13 -3
  38. data/lib/capistrano/deploy.rb +1 -1
  39. data/lib/capistrano/doctor.rb +5 -0
  40. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  41. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  42. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  43. data/lib/capistrano/doctor/variables_doctor.rb +66 -0
  44. data/lib/capistrano/dotfile.rb +1 -2
  45. data/lib/capistrano/dsl.rb +12 -14
  46. data/lib/capistrano/dsl/env.rb +11 -42
  47. data/lib/capistrano/dsl/paths.rb +12 -13
  48. data/lib/capistrano/dsl/stages.rb +2 -4
  49. data/lib/capistrano/dsl/task_enhancements.rb +5 -7
  50. data/lib/capistrano/framework.rb +1 -1
  51. data/lib/capistrano/git.rb +17 -9
  52. data/lib/capistrano/hg.rb +4 -4
  53. data/lib/capistrano/i18n.rb +24 -24
  54. data/lib/capistrano/immutable_task.rb +29 -0
  55. data/lib/capistrano/install.rb +1 -1
  56. data/lib/capistrano/plugin.rb +95 -0
  57. data/lib/capistrano/scm.rb +7 -20
  58. data/lib/capistrano/setup.rb +19 -5
  59. data/lib/capistrano/svn.rb +9 -5
  60. data/lib/capistrano/tasks/console.rake +4 -8
  61. data/lib/capistrano/tasks/deploy.rake +75 -62
  62. data/lib/capistrano/tasks/doctor.rake +19 -0
  63. data/lib/capistrano/tasks/framework.rake +13 -14
  64. data/lib/capistrano/tasks/git.rake +10 -11
  65. data/lib/capistrano/tasks/hg.rake +7 -7
  66. data/lib/capistrano/tasks/install.rake +14 -15
  67. data/lib/capistrano/tasks/svn.rake +7 -7
  68. data/lib/capistrano/templates/Capfile +3 -3
  69. data/lib/capistrano/templates/deploy.rb.erb +6 -5
  70. data/lib/capistrano/upload_task.rb +1 -1
  71. data/lib/capistrano/version.rb +1 -1
  72. data/lib/capistrano/version_validator.rb +4 -6
  73. data/spec/integration/dsl_spec.rb +286 -239
  74. data/spec/integration_spec_helper.rb +3 -5
  75. data/spec/lib/capistrano/application_spec.rb +22 -14
  76. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  77. data/spec/lib/capistrano/configuration/filter_spec.rb +82 -84
  78. data/spec/lib/capistrano/configuration/host_filter_spec.rb +61 -0
  79. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  80. data/spec/lib/capistrano/configuration/question_spec.rb +12 -16
  81. data/spec/lib/capistrano/configuration/role_filter_spec.rb +64 -0
  82. data/spec/lib/capistrano/configuration/server_spec.rb +102 -110
  83. data/spec/lib/capistrano/configuration/servers_spec.rb +124 -141
  84. data/spec/lib/capistrano/configuration_spec.rb +150 -61
  85. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  86. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +61 -0
  87. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  88. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +79 -0
  89. data/spec/lib/capistrano/dsl/paths_spec.rb +58 -50
  90. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +62 -32
  91. data/spec/lib/capistrano/dsl_spec.rb +6 -8
  92. data/spec/lib/capistrano/git_spec.rb +35 -7
  93. data/spec/lib/capistrano/hg_spec.rb +14 -5
  94. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  95. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  96. data/spec/lib/capistrano/scm_spec.rb +6 -7
  97. data/spec/lib/capistrano/svn_spec.rb +40 -14
  98. data/spec/lib/capistrano/upload_task_spec.rb +7 -7
  99. data/spec/lib/capistrano/version_validator_spec.rb +37 -45
  100. data/spec/lib/capistrano_spec.rb +2 -3
  101. data/spec/spec_helper.rb +8 -8
  102. data/spec/support/Vagrantfile +9 -10
  103. data/spec/support/tasks/database.rake +3 -3
  104. data/spec/support/tasks/fail.rake +4 -3
  105. data/spec/support/tasks/failed.rake +2 -2
  106. data/spec/support/tasks/plugin.rake +6 -0
  107. data/spec/support/tasks/root.rake +4 -4
  108. data/spec/support/test_app.rb +31 -30
  109. metadata +93 -14
@@ -1,11 +1,11 @@
1
- PROJECT_ROOT = File.expand_path('../../../', __FILE__)
2
- VAGRANT_ROOT = File.join(PROJECT_ROOT, 'spec/support')
3
- VAGRANT_BIN = ENV['VAGRANT_BIN'] || "vagrant"
1
+ PROJECT_ROOT = File.expand_path("../../../", __FILE__)
2
+ VAGRANT_ROOT = File.join(PROJECT_ROOT, "spec/support")
3
+ VAGRANT_BIN = ENV["VAGRANT_BIN"] || "vagrant"
4
4
 
5
5
  at_exit do
6
- if ENV['KEEP_RUNNING']
6
+ if ENV["KEEP_RUNNING"]
7
7
  VagrantHelpers.run_vagrant_command("rm -rf /home/vagrant/var")
8
8
  end
9
9
  end
10
10
 
11
- require_relative '../../spec/support/test_app'
11
+ require_relative "../../spec/support/test_app"
@@ -1,22 +1,24 @@
1
1
  module RemoteCommandHelpers
2
2
  def test_dir_exists(path)
3
- exists?('d', path)
3
+ exists?("d", path)
4
4
  end
5
5
 
6
6
  def test_symlink_exists(path)
7
- exists?('L', path)
7
+ exists?("L", path)
8
8
  end
9
9
 
10
10
  def test_file_exists(path)
11
- exists?('f', path)
11
+ exists?("f", path)
12
12
  end
13
13
 
14
14
  def exists?(type, path)
15
- %{[ -#{type} "#{path}" ]}
15
+ %Q{[ -#{type} "#{path}" ]}
16
16
  end
17
17
 
18
- def safely_remove_file(path)
19
- run_vagrant_command("rm #{test_file}") rescue VagrantHelpers::VagrantSSHCommandError
18
+ def safely_remove_file(_path)
19
+ run_vagrant_command("rm #{test_file}")
20
+ rescue
21
+ VagrantHelpers::VagrantSSHCommandError
20
22
  end
21
23
  end
22
24
 
@@ -1,10 +1,12 @@
1
+ require "English"
2
+
1
3
  module VagrantHelpers
2
4
  extend self
3
5
 
4
6
  class VagrantSSHCommandError < RuntimeError; end
5
7
 
6
8
  at_exit do
7
- if ENV['KEEP_RUNNING']
9
+ if ENV["KEEP_RUNNING"]
8
10
  puts "Vagrant vm will be left up because KEEP_RUNNING is set."
9
11
  puts "Rerun without KEEP_RUNNING set to cleanup the vm."
10
12
  else
@@ -19,17 +21,16 @@ module VagrantHelpers
19
21
  puts "[vagrant] #{line}"
20
22
  end
21
23
  end
22
- $?
24
+ $CHILD_STATUS
23
25
  end
24
26
 
25
27
  def run_vagrant_command(command)
26
28
  if (status = vagrant_cli_command("ssh -c #{command.inspect}")).success?
27
29
  true
28
30
  else
29
- fail VagrantSSHCommandError, status
31
+ raise VagrantSSHCommandError, status
30
32
  end
31
33
  end
32
-
33
34
  end
34
35
 
35
36
  World(VagrantHelpers)
@@ -0,0 +1,21 @@
1
+ **Important:** GitHub issues are for feature requests or bug reports. The Capistrano team recommends you use [Stack Overflow](http://stackoverflow.com/questions/tagged/capistrano) for general questions. For more details, please see our [contribution policy](https://github.com/capistrano/capistrano/blob/master/CONTRIBUTING.md).
2
+
3
+ ---
4
+
5
+ #### Steps to reproduce
6
+
7
+ 1. Lorem.
8
+ 2. Ipsum..
9
+ 3. Dolor...
10
+
11
+ #### Expected behaviour
12
+
13
+ Tell us what should happen
14
+
15
+ #### Actual behaviour
16
+
17
+ Tell us what happens instead
18
+
19
+ #### Your configuration
20
+
21
+ Paste Capistrano's `doctor` output here (`cap <stage> doctor`):
@@ -1,3 +1,7 @@
1
1
  #!/usr/bin/env cap
2
2
  include Capistrano::DSL
3
- require 'capistrano/install'
3
+ require "capistrano/install"
4
+
5
+ require "capistrano/harrow"
6
+ require "capistrano/harrow/plugin"
7
+ install_plugin Capistrano::Harrow::Plugin
@@ -1,17 +1,16 @@
1
- require 'rake'
2
- require 'sshkit'
1
+ require "rake"
2
+ require "sshkit"
3
3
 
4
- require 'io/console'
4
+ require "io/console"
5
5
 
6
6
  Rake.application.options.trace = true
7
7
 
8
- require 'capistrano/version'
9
- require 'capistrano/version_validator'
10
- require 'capistrano/i18n'
11
- require 'capistrano/dsl'
12
- require 'capistrano/application'
13
- require 'capistrano/configuration'
8
+ require "capistrano/version"
9
+ require "capistrano/version_validator"
10
+ require "capistrano/i18n"
11
+ require "capistrano/dsl"
12
+ require "capistrano/application"
13
+ require "capistrano/configuration"
14
14
 
15
15
  module Capistrano
16
-
17
16
  end
@@ -1,6 +1,5 @@
1
1
  module Capistrano
2
2
  class Application < Rake::Application
3
-
4
3
  def initialize
5
4
  super
6
5
  @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb} << capfile
@@ -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
 
@@ -83,10 +78,18 @@ module Capistrano
83
78
 
84
79
  private
85
80
 
81
+ def backtrace_pattern
82
+ loc = Rake.application.find_rakefile_location
83
+ return unless loc
84
+
85
+ whitelist = (@imported.dup << loc[0]).map { |f| File.absolute_path(f, loc[1]) }
86
+ /^(?!#{whitelist.map { |p| Regexp.quote(p) }.join('|')})/
87
+ end
88
+
86
89
  def load_imports
87
90
  if options.show_tasks
88
- invoke 'load:defaults'
89
- set(:stage, '')
91
+ invoke "load:defaults"
92
+ set(:stage, "")
90
93
  Dir[deploy_config_path].each { |f| add_import f }
91
94
  end
92
95
 
@@ -95,46 +98,53 @@ module Capistrano
95
98
 
96
99
  # allows the `cap install` task to load without a capfile
97
100
  def capfile
98
- File.expand_path(File.join(File.dirname(__FILE__),'..','Capfile'))
101
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "Capfile"))
99
102
  end
100
103
 
101
104
  def version
102
- ['--version', '-V',
105
+ ["--version", "-V",
103
106
  "Display the program version.",
104
- lambda { |value|
107
+ lambda do |_value|
105
108
  puts "Capistrano Version: #{Capistrano::VERSION} (Rake Version: #{Rake::VERSION})"
106
109
  exit
107
- }
110
+ end
108
111
  ]
109
112
  end
110
113
 
111
114
  def dry_run
112
- ['--dry-run', '-n',
115
+ ["--dry-run", "-n",
113
116
  "Do a dry run without executing actions",
114
- lambda { |value|
117
+ lambda do |_value|
115
118
  Configuration.env.set(:sshkit_backend, SSHKit::Backend::Printer)
116
- }
119
+ end
117
120
  ]
118
121
  end
119
122
 
120
123
  def roles
121
- ['--roles ROLES', '-r',
124
+ ["--roles ROLES", "-r",
122
125
  "Run SSH commands only on hosts matching these roles",
123
- lambda { |value|
126
+ lambda do |value|
124
127
  Configuration.env.add_cmdline_filter(:role, value)
125
- }
128
+ end
126
129
  ]
127
130
  end
128
131
 
129
132
  def hostfilter
130
- ['--hosts HOSTS', '-z',
133
+ ["--hosts HOSTS", "-z",
131
134
  "Run SSH commands only on matching hosts",
132
- lambda { |value|
135
+ lambda do |value|
133
136
  Configuration.env.add_cmdline_filter(:host, value)
134
- }
137
+ end
135
138
  ]
136
139
  end
137
140
 
141
+ def print_config_variables
142
+ ["--print-config-variables", "-p",
143
+ "Display the defined config variables before starting the deployment tasks.",
144
+ lambda do |_value|
145
+ Configuration.env.set(:print_config_variables, true)
146
+ end
147
+ ]
148
+ end
138
149
  end
139
-
140
150
  end
@@ -1,15 +1,14 @@
1
- require_relative 'configuration/filter'
2
- require_relative 'configuration/question'
3
- require_relative 'configuration/server'
4
- require_relative 'configuration/servers'
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/variables"
5
7
 
6
8
  module Capistrano
7
- class Configuration
8
-
9
- def initialize(config = nil)
10
- @config ||= config
11
- end
9
+ class ValidationError < Exception; end
12
10
 
11
+ class Configuration
13
12
  def self.env
14
13
  @env ||= new
15
14
  end
@@ -18,38 +17,49 @@ module Capistrano
18
17
  @env = new
19
18
  end
20
19
 
20
+ extend Forwardable
21
+ attr_reader :variables
22
+ def_delegators :variables,
23
+ :set, :fetch, :fetch_for, :delete, :keys, :validate
24
+
25
+ def initialize(values={})
26
+ @variables = Variables.new(values)
27
+ end
28
+
21
29
  def ask(key, default=nil, options={})
22
30
  question = Question.new(key, default, options)
23
31
  set(key, question)
24
32
  end
25
33
 
26
- def set(key, value)
27
- config[key] = value
34
+ def set_if_empty(key, value=nil, &block)
35
+ set(key, value, &block) unless keys.include?(key)
28
36
  end
29
37
 
30
- def set_if_empty(key, value)
31
- config[key] = value unless config.has_key? key
38
+ def append(key, *values)
39
+ set(key, Array(fetch(key)).concat(values))
32
40
  end
33
41
 
34
- def delete(key)
35
- config.delete(key)
42
+ def remove(key, *values)
43
+ set(key, Array(fetch(key)) - values)
36
44
  end
37
45
 
38
- def fetch(key, default=nil, &block)
39
- value = fetch_for(key, default, &block)
40
- while callable_without_parameters?(value)
41
- value = set(key, value.call)
46
+ def any?(key)
47
+ value = fetch(key)
48
+ if value && value.respond_to?(:any?)
49
+ value.any?
50
+ else
51
+ !fetch(key).nil?
42
52
  end
43
- return value
44
53
  end
45
54
 
46
- def keys
47
- config.keys
55
+ def is_question?(key)
56
+ value = fetch_for(key, nil)
57
+ !value.nil? && value.is_a?(Question)
48
58
  end
49
59
 
50
60
  def role(name, hosts, options={})
51
61
  if name == :all
52
- raise ArgumentError.new("#{name} reserved name for role. Please choose another name")
62
+ raise ArgumentError, "#{name} reserved name for role. Please choose another name"
53
63
  end
54
64
 
55
65
  servers.add_role(name, hosts, options)
@@ -79,14 +89,14 @@ module Capistrano
79
89
 
80
90
  def configure_backend
81
91
  backend.configure do |sshkit|
82
- sshkit.format = fetch(:format)
92
+ configure_sshkit_output(sshkit)
83
93
  sshkit.output_verbosity = fetch(:log_level)
84
94
  sshkit.default_env = fetch(:default_env)
85
95
  sshkit.backend = fetch(:sshkit_backend, SSHKit::Backend::Netssh)
86
96
  sshkit.backend.configure do |backend|
87
97
  backend.pty = fetch(:pty)
88
98
  backend.connection_timeout = fetch(:connection_timeout)
89
- backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options,{}))
99
+ backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options, {}))
90
100
  end
91
101
  end
92
102
  end
@@ -97,9 +107,11 @@ module Capistrano
97
107
 
98
108
  def setup_filters
99
109
  @filters = cmdline_filters.clone
100
- @filters << Filter.new(:role, ENV['ROLES']) if ENV['ROLES']
101
- @filters << Filter.new(:host, ENV['HOSTS']) if ENV['HOSTS']
102
- fh = fetch_for(:filter,{})
110
+ @filters << Filter.new(:role, ENV["ROLES"]) if ENV["ROLES"]
111
+ @filters << Filter.new(:host, ENV["HOSTS"]) if ENV["HOSTS"]
112
+ fh = fetch_for(:filter, {}) || {}
113
+ @filters << Filter.new(:host, fh[:hosts]) if fh[:hosts]
114
+ @filters << Filter.new(:role, fh[:roles]) if fh[:roles]
103
115
  @filters << Filter.new(:host, fh[:host]) if fh[:host]
104
116
  @filters << Filter.new(:role, fh[:role]) if fh[:role]
105
117
  end
@@ -108,9 +120,17 @@ module Capistrano
108
120
  cmdline_filters << Filter.new(type, values)
109
121
  end
110
122
 
111
- def filter list
123
+ def filter(list)
112
124
  setup_filters if @filters.nil?
113
- @filters.reduce(list) { |l,f| f.filter l }
125
+ @filters.reduce(list) { |l, f| f.filter l }
126
+ end
127
+
128
+ def dry_run?
129
+ fetch(:sshkit_backend) == SSHKit::Backend::Printer
130
+ end
131
+
132
+ def install_plugin(plugin, load_hooks:true)
133
+ installer.install(plugin, load_hooks: load_hooks)
114
134
  end
115
135
 
116
136
  private
@@ -123,20 +143,15 @@ module Capistrano
123
143
  @servers ||= Servers.new
124
144
  end
125
145
 
126
- def config
127
- @config ||= Hash.new
146
+ def installer
147
+ @installer ||= PluginInstaller.new
128
148
  end
129
149
 
130
- def fetch_for(key, default, &block)
131
- if block_given?
132
- config.fetch(key, &block)
133
- else
134
- config.fetch(key, default)
135
- end
136
- end
150
+ def configure_sshkit_output(sshkit)
151
+ format_args = [fetch(:format)]
152
+ format_args.push(fetch(:format_options)) if any?(:format_options)
137
153
 
138
- def callable_without_parameters?(x)
139
- x.respond_to?(:call) && ( !x.respond_to?(:arity) || x.arity == 0)
154
+ sshkit.use_format(*format_args)
140
155
  end
141
156
  end
142
157
  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