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,14 +1,24 @@
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
+
1
11
  set_if_empty :scm, :git
2
- set_if_empty :branch, :master
12
+ set_if_empty :branch, "master"
3
13
  set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" }
4
14
  set_if_empty :tmp_dir, "/tmp"
5
15
 
6
16
  set_if_empty :default_env, {}
7
17
  set_if_empty :keep_releases, 5
8
18
 
9
- set_if_empty :format, :pretty
19
+ set_if_empty :format, :airbrussh
10
20
  set_if_empty :log_level, :debug
11
21
 
12
22
  set_if_empty :pty, false
13
23
 
14
- set_if_empty :local_user, -> { Etc.getlogin }
24
+ set_if_empty :local_user, -> { ENV["USER"] || ENV["LOGNAME"] || ENV["USERNAME"] }
@@ -1,3 +1,3 @@
1
- require 'capistrano/framework'
1
+ require "capistrano/framework"
2
2
 
3
3
  load File.expand_path("../tasks/deploy.rake", __FILE__)
@@ -0,0 +1,5 @@
1
+ require "capistrano/doctor/environment_doctor"
2
+ require "capistrano/doctor/gems_doctor"
3
+ require "capistrano/doctor/variables_doctor"
4
+
5
+ load File.expand_path("../tasks/doctor.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) & 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
@@ -0,0 +1,79 @@
1
+ module Capistrano
2
+ module Doctor
3
+ # Helper methods for pretty-printing doctor output to stdout. All output
4
+ # (other than `title`) is indented by four spaces to facilitate copying and
5
+ # pasting this output into e.g. GitHub or Stack Overflow to achieve code
6
+ # formatting.
7
+ module OutputHelpers
8
+ class Row
9
+ attr_reader :color
10
+ attr_reader :values
11
+
12
+ def initialize
13
+ @values = []
14
+ end
15
+
16
+ def <<(value)
17
+ values << value
18
+ end
19
+
20
+ def yellow
21
+ @color = :yellow
22
+ end
23
+ end
24
+
25
+ # Prints a table for a given array of records. For each record, the block
26
+ # is yielded two arguments: the record and a Row object. To print values
27
+ # for that record, add values using `row << "some value"`. A row can
28
+ # optionally be highlighted in yellow using `row.yellow`.
29
+ def table(records, &block)
30
+ return if records.empty?
31
+ rows = collect_rows(records, &block)
32
+ col_widths = calculate_column_widths(rows)
33
+
34
+ rows.each do |row|
35
+ line = row.values.each_with_index.map do |value, col|
36
+ value.to_s.ljust(col_widths[col])
37
+ end.join(" ").rstrip
38
+ line = color.colorize(line, row.color) if row.color
39
+ puts line
40
+ end
41
+ end
42
+
43
+ # Prints a title in blue with surrounding newlines.
44
+ def title(text)
45
+ # Use $stdout directly to bypass the indentation that our `puts` does.
46
+ $stdout.puts(color.colorize("\n#{text}\n", :blue))
47
+ end
48
+
49
+ # Prints text in yellow.
50
+ def warning(text)
51
+ puts color.colorize(text, :yellow)
52
+ end
53
+
54
+ # Override `Kernel#puts` to prepend four spaces to each line.
55
+ def puts(string=nil)
56
+ $stdout.puts(string.to_s.gsub(/^/, " "))
57
+ end
58
+
59
+ private
60
+
61
+ def collect_rows(records)
62
+ records.map do |rec|
63
+ Row.new.tap { |row| yield(rec, row) }
64
+ end
65
+ end
66
+
67
+ def calculate_column_widths(rows)
68
+ num_columns = rows.map { |row| row.values.length }.max
69
+ Array.new(num_columns) do |col|
70
+ rows.map { |row| row.values[col].to_s.length }.max
71
+ end
72
+ end
73
+
74
+ def color
75
+ @color ||= SSHKit::Color.new($stdout)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,66 @@
1
+ require "capistrano/doctor/output_helpers"
2
+
3
+ module Capistrano
4
+ module Doctor
5
+ # Prints a table of all Capistrano variables and their current values. If
6
+ # there are unrecognized variables, print warnings for them.
7
+ class VariablesDoctor
8
+ # These are keys that have no default values in Capistrano, but are
9
+ # nonetheless expected to be set.
10
+ WHITELIST = [:application, :repo_url].freeze
11
+ private_constant :WHITELIST
12
+
13
+ include Capistrano::Doctor::OutputHelpers
14
+
15
+ def initialize(env=Capistrano::Configuration.env)
16
+ @env = env
17
+ end
18
+
19
+ def call
20
+ title("Variables")
21
+ values = inspect_all_values
22
+
23
+ table(variables.keys.sort) do |key, row|
24
+ row.yellow if suspicious_keys.include?(key)
25
+ row << ":#{key}"
26
+ row << values[key]
27
+ end
28
+
29
+ puts if suspicious_keys.any?
30
+
31
+ suspicious_keys.sort.each do |key|
32
+ warning(
33
+ ":#{key} is not a recognized Capistrano setting (#{location(key)})"
34
+ )
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :env
41
+
42
+ def variables
43
+ env.variables
44
+ end
45
+
46
+ def inspect_all_values
47
+ variables.keys.each_with_object({}) do |key, inspected|
48
+ inspected[key] = if env.is_question?(key)
49
+ "<ask>"
50
+ else
51
+ variables.peek(key).inspect
52
+ end
53
+ end
54
+ end
55
+
56
+ def suspicious_keys
57
+ (variables.untrusted_keys & variables.unused_keys) - WHITELIST
58
+ end
59
+
60
+ def location(key)
61
+ loc = variables.source_locations(key).first
62
+ loc && loc.sub(/^#{Regexp.quote(Dir.pwd)}/, "").sub(/:in.*/, "")
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,2 @@
1
- dotfile = Pathname.new(File.join(Dir.home, '.capfile'))
1
+ dotfile = Pathname.new(File.join(Dir.home, ".capfile"))
2
2
  load dotfile if dotfile.file?
3
-
@@ -1,9 +1,8 @@
1
- require 'etc'
2
- require 'capistrano/dsl/task_enhancements'
3
- require 'capistrano/dsl/paths'
4
- require 'capistrano/dsl/stages'
5
- require 'capistrano/dsl/env'
6
- require 'capistrano/configuration/filter'
1
+ require "capistrano/dsl/task_enhancements"
2
+ require "capistrano/dsl/paths"
3
+ require "capistrano/dsl/stages"
4
+ require "capistrano/dsl/env"
5
+ require "capistrano/configuration/filter"
7
6
 
8
7
  module Capistrano
9
8
  module DSL
@@ -30,12 +29,12 @@ module Capistrano
30
29
 
31
30
  def revision_log_message
32
31
  fetch(:revision_log_message,
33
- t(:revision_log_message,
34
- branch: fetch(:branch),
35
- user: local_user,
36
- sha: fetch(:current_revision),
37
- release: fetch(:release_timestamp))
38
- )
32
+ t(:revision_log_message,
33
+ branch: fetch(:branch),
34
+ user: local_user,
35
+ sha: fetch(:current_revision),
36
+ release: fetch(:release_timestamp))
37
+ )
39
38
  end
40
39
 
41
40
  def rollback_log_message
@@ -58,7 +57,6 @@ module Capistrano
58
57
  def run_locally(&block)
59
58
  SSHKit::Backend::Local.new(&block).run
60
59
  end
61
-
62
60
  end
63
61
  end
64
- self.extend Capistrano::DSL
62
+ extend Capistrano::DSL
@@ -1,46 +1,20 @@
1
+ require "forwardable"
2
+
1
3
  module Capistrano
2
4
  module DSL
3
5
  module Env
6
+ extend Forwardable
7
+ def_delegators :env,
8
+ :configure_backend, :fetch, :set, :set_if_empty, :delete,
9
+ :ask, :role, :server, :primary, :validate, :append,
10
+ :remove, :dry_run?, :install_plugin
4
11
 
5
- def configure_backend
6
- env.configure_backend
7
- end
8
-
9
- def fetch(key, default=nil, &block)
10
- env.fetch(key, default, &block)
12
+ def is_question?(key)
13
+ env.is_question?(key)
11
14
  end
12
15
 
13
16
  def any?(key)
14
- value = fetch(key)
15
- if value && value.respond_to?(:any?)
16
- value.any?
17
- else
18
- !fetch(key).nil?
19
- end
20
- end
21
-
22
- def set(key, value)
23
- env.set(key, value)
24
- end
25
-
26
- def set_if_empty(key, value)
27
- env.set_if_empty(key, value)
28
- end
29
-
30
- def delete(key)
31
- env.delete(key)
32
- end
33
-
34
- def ask(key, value, options={})
35
- env.ask(key, value, options)
36
- end
37
-
38
- def role(name, servers, options={})
39
- env.role(name, servers, options)
40
- end
41
-
42
- def server(name, properties={})
43
- env.server(name, properties)
17
+ env.any?(key)
44
18
  end
45
19
 
46
20
  def roles(*names)
@@ -53,17 +27,13 @@ module Capistrano
53
27
 
54
28
  def release_roles(*names)
55
29
  if names.last.is_a? Hash
56
- names.last.merge!({ :exclude => :no_release })
30
+ names.last[:exclude] = :no_release
57
31
  else
58
32
  names << { exclude: :no_release }
59
33
  end
60
34
  roles(*names)
61
35
  end
62
36
 
63
- def primary(role)
64
- env.primary(role)
65
- end
66
-
67
37
  def env
68
38
  Configuration.env
69
39
  end
@@ -75,7 +45,6 @@ module Capistrano
75
45
  def asset_timestamp
76
46
  env.timestamp.strftime("%Y%m%d%H%M.%S")
77
47
  end
78
-
79
48
  end
80
49
  end
81
50
  end
@@ -1,8 +1,7 @@
1
- require 'pathname'
1
+ require "pathname"
2
2
  module Capistrano
3
3
  module DSL
4
4
  module Paths
5
-
6
5
  def deploy_to
7
6
  fetch(:deploy_to)
8
7
  end
@@ -12,11 +11,11 @@ module Capistrano
12
11
  end
13
12
 
14
13
  def current_path
15
- deploy_path.join('current')
14
+ deploy_path.join("current")
16
15
  end
17
16
 
18
17
  def releases_path
19
- deploy_path.join('releases')
18
+ deploy_path.join("releases")
20
19
  end
21
20
 
22
21
  def release_path
@@ -29,17 +28,17 @@ module Capistrano
29
28
  end
30
29
 
31
30
  def stage_config_path
32
- Pathname.new fetch(:stage_config_path, 'config/deploy')
31
+ Pathname.new fetch(:stage_config_path, "config/deploy")
33
32
  end
34
33
 
35
34
  def deploy_config_path
36
- Pathname.new fetch(:deploy_config_path, 'config/deploy.rb')
35
+ Pathname.new fetch(:deploy_config_path, "config/deploy.rb")
37
36
  end
38
37
 
39
38
  def repo_url
40
- require 'cgi'
41
- require 'uri'
42
- if fetch(:git_http_username) and fetch(:git_http_password)
39
+ require "cgi"
40
+ require "uri"
41
+ if fetch(:git_http_username) && fetch(:git_http_password)
43
42
  URI.parse(fetch(:repo_url)).tap do |repo_uri|
44
43
  repo_uri.user = fetch(:git_http_username)
45
44
  repo_uri.password = CGI.escape(fetch(:git_http_password))
@@ -54,15 +53,15 @@ module Capistrano
54
53
  end
55
54
 
56
55
  def repo_path
57
- Pathname.new(fetch(:repo_path, ->(){deploy_path.join('repo')}))
56
+ Pathname.new(fetch(:repo_path, ->() { deploy_path.join("repo") }))
58
57
  end
59
58
 
60
59
  def shared_path
61
- deploy_path.join('shared')
60
+ deploy_path.join("shared")
62
61
  end
63
62
 
64
63
  def revision_log
65
- deploy_path.join('revisions.log')
64
+ deploy_path.join("revisions.log")
66
65
  end
67
66
 
68
67
  def now
@@ -96,7 +95,7 @@ module Capistrano
96
95
  end
97
96
 
98
97
  def map_dirnames(paths)
99
- paths.map { |path| path.dirname }
98
+ paths.map(&:dirname).uniq
100
99
  end
101
100
  end
102
101
  end