capistrano 3.4.1 → 3.5.0

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