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,7 +1,5 @@
1
- require 'spec_helper'
2
- require 'support/test_app'
3
- require 'support/matchers'
1
+ require "spec_helper"
2
+ require "support/test_app"
3
+ require "support/matchers"
4
4
 
5
5
  include TestApp
6
-
7
-
@@ -1,14 +1,13 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe Capistrano::Application do
4
-
5
4
  it "provides a --trace option which enables SSHKit/NetSSH trace output"
6
5
 
7
6
  it "provides a --format option which enables the choice of output formatting"
8
7
 
9
8
  let(:help_output) do
10
- out, _ = capture_io do
11
- flags '--help', '-h'
9
+ out, _err = capture_io do
10
+ flags "--help", "-h"
12
11
  end
13
12
  out
14
13
  end
@@ -24,8 +23,8 @@ describe Capistrano::Application do
24
23
  end
25
24
 
26
25
  it "overrides the rake method, but still prints the rake version" do
27
- out, _ = capture_io do
28
- flags '--version', '-V'
26
+ out, _err = capture_io do
27
+ flags "--version", "-V"
29
28
  end
30
29
  expect(out).to match(/\bCapistrano Version\b/)
31
30
  expect(out).to match(/\b#{Capistrano::VERSION}\b/)
@@ -34,13 +33,20 @@ describe Capistrano::Application do
34
33
  end
35
34
 
36
35
  it "overrides the rake method, and sets the sshkit_backend to SSHKit::Backend::Printer" do
37
- out, _ = capture_io do
38
- flags '--dry-run', '-n'
36
+ capture_io do
37
+ flags "--dry-run", "-n"
39
38
  end
40
39
  sshkit_backend = Capistrano::Configuration.fetch(:sshkit_backend)
41
40
  expect(sshkit_backend).to eq(SSHKit::Backend::Printer)
42
41
  end
43
42
 
43
+ it "enables printing all config variables on command line parameter" do
44
+ capture_io do
45
+ flags "--print-config-variables", "-p"
46
+ end
47
+ expect(Capistrano::Configuration.fetch(:print_config_variables)).to be true
48
+ end
49
+
44
50
  def flags(*sets)
45
51
  sets.each do |set|
46
52
  ARGV.clear
@@ -51,7 +57,7 @@ describe Capistrano::Application do
51
57
 
52
58
  def command_line(*options)
53
59
  options.each { |opt| ARGV << opt }
54
- def subject.exit(*args)
60
+ subject.define_singleton_method(:exit) do |*_args|
55
61
  throw(:system_exit, :exit)
56
62
  end
57
63
  subject.run
@@ -59,11 +65,14 @@ describe Capistrano::Application do
59
65
  end
60
66
 
61
67
  def capture_io
62
- require 'stringio'
68
+ require "stringio"
63
69
 
64
- orig_stdout, orig_stderr = $stdout, $stderr
65
- captured_stdout, captured_stderr = StringIO.new, StringIO.new
66
- $stdout, $stderr = captured_stdout, captured_stderr
70
+ orig_stdout = $stdout
71
+ orig_stderr = $stderr
72
+ captured_stdout = StringIO.new
73
+ captured_stderr = StringIO.new
74
+ $stdout = captured_stdout
75
+ $stderr = captured_stderr
67
76
 
68
77
  yield
69
78
 
@@ -72,5 +81,4 @@ describe Capistrano::Application do
72
81
  $stdout = orig_stdout
73
82
  $stderr = orig_stderr
74
83
  end
75
-
76
84
  end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ module Capistrano
4
+ class Configuration
5
+ describe EmptyFilter do
6
+ subject(:empty_filter) { EmptyFilter.new }
7
+
8
+ describe '#filter' do
9
+ let(:servers) { mock("servers") }
10
+
11
+ it "returns an empty array" do
12
+ expect(empty_filter.filter(servers)).to eq([])
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,109 +1,107 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  module Capistrano
4
4
  class Configuration
5
-
6
5
  describe Filter do
7
- let(:available) { [ Server.new('server1').add_roles([:web,:db]),
8
- Server.new('server2').add_role(:web),
9
- Server.new('server3').add_role(:redis),
10
- Server.new('server4').add_role(:db),
11
- Server.new('server5').add_role(:stageweb) ] }
6
+ let(:available) do
7
+ [
8
+ Server.new("server1").add_roles([:web, :db]),
9
+ Server.new("server2").add_role(:web),
10
+ Server.new("server3").add_role(:redis),
11
+ Server.new("server4").add_role(:db),
12
+ Server.new("server5").add_role(:stageweb)
13
+ ]
14
+ end
12
15
 
13
16
  describe '#new' do
14
17
  it "won't create an invalid type of filter" do
15
- expect {
16
- f = Filter.new(:zarg)
17
- }.to raise_error RuntimeError
18
+ expect do
19
+ Filter.new(:zarg)
20
+ end.to raise_error RuntimeError
18
21
  end
19
22
 
20
- it 'creates an empty host filter' do
21
- expect(Filter.new(:host).filter(available)).to be_empty
22
- end
23
+ context "with type :host" do
24
+ context "and no values" do
25
+ it "creates an EmptyFilter strategy" do
26
+ expect(Filter.new(:host).instance_variable_get(:@strategy)).to be_a(EmptyFilter)
27
+ end
28
+ end
23
29
 
24
- it 'creates a null host filter' do
25
- expect(Filter.new(:host, :all).filter(available)).to eq(available)
26
- end
30
+ context "and :all" do
31
+ it "creates an NullFilter strategy" do
32
+ expect(Filter.new(:host, :all).instance_variable_get(:@strategy)).to be_a(NullFilter)
33
+ end
34
+ end
27
35
 
28
- it 'creates an empty role filter' do
29
- expect(Filter.new(:role).filter(available)).to be_empty
30
- end
36
+ context "and [:all]" do
37
+ it "creates an NullFilter strategy" do
38
+ expect(Filter.new(:host, [:all]).instance_variable_get(:@strategy)).to be_a(NullFilter)
39
+ end
40
+ end
31
41
 
32
- it 'creates a null role filter' do
33
- expect(Filter.new(:role, :all).filter(available)).to eq(available)
42
+ context "and [:all]" do
43
+ it "creates an NullFilter strategy" do
44
+ expect(Filter.new(:host, "all").instance_variable_get(:@strategy)).to be_a(NullFilter)
45
+ end
46
+ end
34
47
  end
35
48
 
36
- end
49
+ context "with type :role" do
50
+ context "and no values" do
51
+ it "creates an EmptyFilter strategy" do
52
+ expect(Filter.new(:role).instance_variable_get(:@strategy)).to be_a(EmptyFilter)
53
+ end
54
+ end
37
55
 
38
- describe 'host filter' do
39
- it 'works with a single server' do
40
- set = Filter.new(:host, 'server1').filter(available.first)
41
- expect(set.map(&:hostname)).to eq(%w{server1})
42
- end
43
- it 'returns all hosts matching a string' do
44
- set = Filter.new(:host, 'server1').filter(available)
45
- expect(set.map(&:hostname)).to eq(%w{server1})
46
- end
47
- it 'returns all hosts matching a comma-separated string' do
48
- set = Filter.new(:host, 'server1,server3').filter(available)
49
- expect(set.map(&:hostname)).to eq(%w{server1 server3})
50
- end
51
- it 'returns all hosts matching an array of strings' do
52
- set = Filter.new(:host, %w{server1 server3}).filter(available)
53
- expect(set.map(&:hostname)).to eq(%w{server1 server3})
54
- end
55
- it 'returns all hosts matching regexp' do
56
- set = Filter.new(:host, 'server[13]$').filter(available)
57
- expect(set.map(&:hostname)).to eq(%w{server1 server3})
58
- end
59
- it 'correctly identifies a regex with a comma in' do
60
- set = Filter.new(:host, 'server\d{1,3}$').filter(available)
61
- expect(set.map(&:hostname)).to eq(%w{server1 server2 server3 server4 server5})
56
+ context "and :all" do
57
+ it "creates an NullFilter strategy" do
58
+ expect(Filter.new(:role, :all).instance_variable_get(:@strategy)).to be_a(NullFilter)
59
+ end
60
+ end
61
+
62
+ context "and [:all]" do
63
+ it "creates an NullFilter strategy" do
64
+ expect(Filter.new(:role, [:all]).instance_variable_get(:@strategy)).to be_a(NullFilter)
65
+ end
66
+ end
67
+
68
+ context "and [:all]" do
69
+ it "creates an NullFilter strategy" do
70
+ expect(Filter.new(:role, "all").instance_variable_get(:@strategy)).to be_a(NullFilter)
71
+ end
72
+ end
62
73
  end
63
74
  end
64
75
 
65
- describe 'role filter' do
66
- it 'returns all hosts' do
67
- set = Filter.new(:role, [:all]).filter(available)
68
- expect(set.size).to eq(available.size)
69
- expect(set.first.hostname).to eq('server1')
70
- end
71
- it 'returns hosts in a single string role' do
72
- set = Filter.new(:role, 'web').filter(available)
73
- expect(set.size).to eq(2)
74
- expect(set.map(&:hostname)).to eq(%w{server1 server2})
75
- end
76
- it 'returns hosts in a single role' do
77
- set = Filter.new(:role, [:web]).filter(available)
78
- expect(set.size).to eq(2)
79
- expect(set.map(&:hostname)).to eq(%w{server1 server2})
80
- end
81
- it 'returns hosts in multiple roles specified by a string' do
82
- set = Filter.new(:role, 'web,db').filter(available)
83
- expect(set.size).to eq(3)
84
- expect(set.map(&:hostname)).to eq(%w{server1 server2 server4})
85
- end
86
- it 'returns hosts in multiple roles' do
87
- set = Filter.new(:role, [:web, :db]).filter(available)
88
- expect(set.size).to eq(3)
89
- expect(set.map(&:hostname)).to eq(%w{server1 server2 server4})
76
+ describe '#filter' do
77
+ let(:strategy) { filter.instance_variable_get(:@strategy) }
78
+ let(:results) { mock("result") }
79
+
80
+ shared_examples 'it calls #filter on its strategy' do
81
+ it 'calls #filter on its strategy' do
82
+ strategy.expects(:filter).with(available).returns(results)
83
+ expect(filter.filter(available)).to eq(results)
84
+ end
90
85
  end
91
- it 'returns only hosts for explicit roles' do
92
- set = Filter.new(:role, [:web]).filter(available)
93
- expect(set.size).to eq(2)
94
- expect(set.map(&:hostname)).to eq(%w{server1 server2})
86
+
87
+ context "for an empty filter" do
88
+ let(:filter) { Filter.new(:role) }
89
+ it_behaves_like 'it calls #filter on its strategy'
95
90
  end
96
- it 'returns hosts with regex role selection' do
97
- set = Filter.new(:role, /red/).filter(available)
98
- expect(set.map(&:hostname)).to eq(%w{server3})
91
+
92
+ context "for a null filter" do
93
+ let(:filter) { Filter.new(:role, :all) }
94
+ it_behaves_like 'it calls #filter on its strategy'
99
95
  end
100
- it 'returns hosts with regex role selection using a string' do
101
- set = Filter.new(:role, '/red|web/').filter(available)
102
- expect(set.map(&:hostname)).to eq(%w{server1 server2 server3 server5})
96
+
97
+ context "for a role filter" do
98
+ let(:filter) { Filter.new(:role, "web") }
99
+ it_behaves_like 'it calls #filter on its strategy'
103
100
  end
104
- it 'returns hosts with combination of string role and regex' do
105
- set = Filter.new(:role, 'db,/red/').filter(available)
106
- expect(set.map(&:hostname)).to eq(%w{server1 server3 server4})
101
+
102
+ context "for a host filter" do
103
+ let(:filter) { Filter.new(:host, "server1") }
104
+ it_behaves_like 'it calls #filter on its strategy'
107
105
  end
108
106
  end
109
107
  end
@@ -0,0 +1,61 @@
1
+ require "spec_helper"
2
+
3
+ module Capistrano
4
+ class Configuration
5
+ describe HostFilter do
6
+ subject(:host_filter) { HostFilter.new(values) }
7
+
8
+ let(:available) do
9
+ [Server.new("server1"),
10
+ Server.new("server2"),
11
+ Server.new("server3"),
12
+ Server.new("server4"),
13
+ Server.new("server5")]
14
+ end
15
+
16
+ shared_examples "it filters hosts correctly" do |expected|
17
+ it "filters correctly" do
18
+ set = host_filter.filter(available)
19
+ expect(set.map(&:hostname)).to eq(expected)
20
+ end
21
+ end
22
+
23
+ describe '#filter' do
24
+ context "with a string" do
25
+ let(:values) { "server1" }
26
+ it_behaves_like "it filters hosts correctly", %w{server1}
27
+
28
+ context "and a single server" do
29
+ let(:available) { Server.new("server1") }
30
+ it_behaves_like "it filters hosts correctly", %w{server1}
31
+ end
32
+ end
33
+
34
+ context "with a comma separated string" do
35
+ let(:values) { "server1,server3" }
36
+ it_behaves_like "it filters hosts correctly", %w{server1 server3}
37
+ end
38
+
39
+ context "with an array of strings" do
40
+ let(:values) { %w{server1 server3} }
41
+ it_behaves_like "it filters hosts correctly", %w{server1 server3}
42
+ end
43
+
44
+ context "with a regexp" do
45
+ let(:values) { "server[13]$" }
46
+ it_behaves_like "it filters hosts correctly", %w{server1 server3}
47
+ end
48
+
49
+ context "with a regexp with line boundaries" do
50
+ let(:values) { "^server" }
51
+ it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5}
52
+ end
53
+
54
+ context "with a regexp with a comma" do
55
+ let(:values) { 'server\d{1,3}$' }
56
+ it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5}
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ module Capistrano
4
+ class Configuration
5
+ describe NullFilter do
6
+ subject(:null_filter) { NullFilter.new }
7
+
8
+ describe '#filter' do
9
+ let(:servers) { mock("servers") }
10
+
11
+ it "returns the servers passed in as arguments" do
12
+ expect(null_filter.filter(servers)).to eq(servers)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,38 +1,36 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  module Capistrano
4
4
  class Configuration
5
-
6
5
  describe Question do
7
-
8
6
  let(:question) { Question.new(key, default, options) }
9
7
  let(:question_without_echo) { Question.new(key, default, echo: false) }
10
8
  let(:default) { :default }
11
9
  let(:key) { :branch }
12
10
  let(:options) { nil }
13
11
 
14
- describe '.new' do
15
- it 'takes a key, default, options' do
12
+ describe ".new" do
13
+ it "takes a key, default, options" do
16
14
  question
17
15
  end
18
16
  end
19
17
 
20
18
  describe '#call' do
21
- context 'value is entered' do
22
- let(:branch) { 'branch' }
19
+ context "value is entered" do
20
+ let(:branch) { "branch" }
23
21
 
24
22
  before do
25
- $stdout.expects(:print).with('Please enter branch (default): ')
23
+ $stdout.expects(:print).with("Please enter branch (default): ")
26
24
  end
27
25
 
28
- it 'returns the echoed value' do
26
+ it "returns the echoed value" do
29
27
  $stdin.expects(:gets).returns(branch)
30
28
  $stdin.expects(:noecho).never
31
29
 
32
30
  expect(question.call).to eq(branch)
33
31
  end
34
32
 
35
- it 'returns the value but does not echo it' do
33
+ it "returns the value but does not echo it" do
36
34
  $stdin.expects(:noecho).returns(branch)
37
35
  $stdout.expects(:print).with("\n")
38
36
 
@@ -40,21 +38,19 @@ module Capistrano
40
38
  end
41
39
  end
42
40
 
43
- context 'value is not entered' do
41
+ context "value is not entered" do
44
42
  let(:branch) { default }
45
43
 
46
44
  before do
47
- $stdout.expects(:print).with('Please enter branch (default): ')
48
- $stdin.expects(:gets).returns('')
45
+ $stdout.expects(:print).with("Please enter branch (default): ")
46
+ $stdin.expects(:gets).returns("")
49
47
  end
50
48
 
51
-
52
- it 'returns the default as the value' do
49
+ it "returns the default as the value" do
53
50
  expect(question.call).to eq(branch)
54
51
  end
55
52
  end
56
53
  end
57
54
  end
58
-
59
55
  end
60
56
  end