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,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