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,34 +1,34 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  module Capistrano
4
4
  describe Configuration do
5
5
  let(:config) { Configuration.new }
6
6
  let(:servers) { stub }
7
7
 
8
- describe '.new' do
9
- it 'accepts initial hash' do
10
- configuration = described_class.new(custom: 'value')
11
- expect(configuration.fetch(:custom)).to eq('value')
8
+ describe ".new" do
9
+ it "accepts initial hash" do
10
+ configuration = described_class.new(custom: "value")
11
+ expect(configuration.fetch(:custom)).to eq("value")
12
12
  end
13
13
  end
14
14
 
15
- describe '.env' do
16
- it 'is a global accessor to a single instance' do
15
+ describe ".env" do
16
+ it "is a global accessor to a single instance" do
17
17
  Configuration.env.set(:test, true)
18
18
  expect(Configuration.env.fetch(:test)).to be_truthy
19
19
  end
20
20
  end
21
21
 
22
- describe '.reset!' do
23
- it 'blows away the existing `env` and creates a new one' do
22
+ describe ".reset!" do
23
+ it "blows away the existing `env` and creates a new one" do
24
24
  old_env = Configuration.env
25
25
  Configuration.reset!
26
26
  expect(Configuration.env).not_to be old_env
27
27
  end
28
28
  end
29
29
 
30
- describe 'roles' do
31
- context 'adding a role' do
30
+ describe "roles" do
31
+ context "adding a role" do
32
32
  subject { config.role(:app, %w{server1 server2}) }
33
33
 
34
34
  before do
@@ -36,103 +36,180 @@ module Capistrano
36
36
  servers.expects(:add_role).with(:app, %w{server1 server2}, {})
37
37
  end
38
38
 
39
- it 'adds the role' do
39
+ it "adds the role" do
40
40
  expect(subject)
41
41
  end
42
42
  end
43
43
  end
44
44
 
45
- describe 'setting and fetching' do
45
+ describe "setting and fetching" do
46
46
  subject { config.fetch(:key, :default) }
47
47
 
48
- context 'value is set' do
49
- before do
48
+ context "set" do
49
+ it "sets by value" do
50
50
  config.set(:key, :value)
51
+ expect(subject).to eq :value
51
52
  end
52
53
 
53
- it 'returns the set value' do
54
+ it "sets by block" do
55
+ config.set(:key) { :value }
54
56
  expect(subject).to eq :value
55
57
  end
58
+
59
+ it "raises an exception when given both a value and block" do
60
+ expect { config.set(:key, :value) { :value } }.to raise_error(Capistrano::ValidationError)
61
+ end
56
62
  end
57
63
 
58
- context 'set_if_empty' do
59
- it 'sets the value when none is present' do
64
+ context "set_if_empty" do
65
+ it "sets by value when none is present" do
60
66
  config.set_if_empty(:key, :value)
61
67
  expect(subject).to eq :value
62
68
  end
63
69
 
64
- it 'does not overwrite the value' do
70
+ it "sets by block when none is present" do
71
+ config.set_if_empty(:key) { :value }
72
+ expect(subject).to eq :value
73
+ end
74
+
75
+ it "does not overwrite existing values" do
65
76
  config.set(:key, :value)
66
77
  config.set_if_empty(:key, :update)
78
+ config.set_if_empty(:key) { :update }
67
79
  expect(subject).to eq :value
68
80
  end
69
81
  end
70
82
 
71
- context 'value is not set' do
72
- it 'returns the default value' do
83
+ context "value is not set" do
84
+ it "returns the default value" do
73
85
  expect(subject).to eq :default
74
86
  end
75
87
  end
76
88
 
77
- context 'value is a proc' do
78
- subject { config.fetch(:key, Proc.new { :proc } ) }
79
- it 'calls the proc' do
89
+ context "value is a proc" do
90
+ subject { config.fetch(:key, proc { :proc }) }
91
+ it "calls the proc" do
80
92
  expect(subject).to eq :proc
81
93
  end
82
94
  end
83
95
 
84
- context 'value is a lambda' do
85
- subject { config.fetch(:key, lambda { :lambda } ) }
86
- it 'calls the lambda' do
96
+ context "value is a lambda" do
97
+ subject { config.fetch(:key, -> { :lambda }) }
98
+ it "calls the lambda" do
87
99
  expect(subject).to eq :lambda
88
100
  end
89
101
  end
90
102
 
91
- context 'value inside proc inside a proc' do
92
- subject { config.fetch(:key, Proc.new { Proc.new { "some value" } } ) }
93
- it 'calls all procs and lambdas' do
103
+ context "value inside proc inside a proc" do
104
+ subject { config.fetch(:key, proc { proc { "some value" } }) }
105
+ it "calls all procs and lambdas" do
94
106
  expect(subject).to eq "some value"
95
107
  end
96
108
  end
97
109
 
98
- context 'value inside lambda inside a lambda' do
99
- subject { config.fetch(:key, lambda { lambda { "some value" } } ) }
100
- it 'calls all procs and lambdas' do
110
+ context "value inside lambda inside a lambda" do
111
+ subject { config.fetch(:key, -> { -> { "some value" } }) }
112
+ it "calls all procs and lambdas" do
101
113
  expect(subject).to eq "some value"
102
114
  end
103
115
  end
104
116
 
105
- context 'value inside lambda inside a proc' do
106
- subject { config.fetch(:key, Proc.new { lambda { "some value" } } ) }
107
- it 'calls all procs and lambdas' do
117
+ context "value inside lambda inside a proc" do
118
+ subject { config.fetch(:key, proc { -> { "some value" } }) }
119
+ it "calls all procs and lambdas" do
108
120
  expect(subject).to eq "some value"
109
121
  end
110
122
  end
111
123
 
112
- context 'value inside proc inside a lambda' do
113
- subject { config.fetch(:key, lambda { Proc.new { "some value" } } ) }
114
- it 'calls all procs and lambdas' do
124
+ context "value inside proc inside a lambda" do
125
+ subject { config.fetch(:key, -> { proc { "some value" } }) }
126
+ it "calls all procs and lambdas" do
115
127
  expect(subject).to eq "some value"
116
128
  end
117
129
  end
118
130
 
119
- context 'lambda with parameters' do
120
- subject { config.fetch(:key, lambda { |c| c }).call(42) }
121
- it 'is returned as a lambda' do
131
+ context "lambda with parameters" do
132
+ subject { config.fetch(:key, ->(c) { c }).call(42) }
133
+ it "is returned as a lambda" do
122
134
  expect(subject).to eq 42
123
135
  end
124
136
  end
125
137
 
126
- context 'block is passed to fetch' do
127
- subject { config.fetch(:key, :default) { fail 'we need this!' } }
138
+ context "block is passed to fetch" do
139
+ subject { config.fetch(:key, :default) { raise "we need this!" } }
140
+
141
+ it "returns the block value" do
142
+ expect { subject }.to raise_error(RuntimeError)
143
+ end
144
+ end
145
+
146
+ context "validations" do
147
+ before do
148
+ config.validate :key do |_, value|
149
+ raise Capistrano::ValidationError unless value.length > 3
150
+ end
151
+ end
152
+
153
+ it "validates without error" do
154
+ config.set(:key, "longer_value")
155
+ end
156
+
157
+ it "raises an exception" do
158
+ expect { config.set(:key, "sho") }.to raise_error(Capistrano::ValidationError)
159
+ end
160
+ end
161
+
162
+ context "appending" do
163
+ subject { config.append(:linked_dirs, "vendor/bundle", "tmp") }
164
+
165
+ it "returns appended value" do
166
+ expect(subject).to eq ["vendor/bundle", "tmp"]
167
+ end
168
+
169
+ context "on non-array variable" do
170
+ before { config.set(:linked_dirs, "string") }
171
+ subject { config.append(:linked_dirs, "vendor/bundle") }
172
+
173
+ it "returns appended value" do
174
+ expect(subject).to eq ["string", "vendor/bundle"]
175
+ end
176
+ end
177
+ end
178
+
179
+ context "removing" do
180
+ before :each do
181
+ config.set(:linked_dirs, ["vendor/bundle", "tmp"])
182
+ end
183
+
184
+ subject { config.remove(:linked_dirs, "vendor/bundle") }
185
+
186
+ it "returns without removed value" do
187
+ expect(subject).to eq ["tmp"]
188
+ end
189
+
190
+ context "on non-array variable" do
191
+ before { config.set(:linked_dirs, "string") }
192
+
193
+ context "when removing same value" do
194
+ subject { config.remove(:linked_dirs, "string") }
195
+
196
+ it "returns without removed value" do
197
+ expect(subject).to eq []
198
+ end
199
+ end
128
200
 
129
- it 'returns the block value' do
130
- expect { subject }.to raise_error
201
+ context "when removing different value" do
202
+ subject { config.remove(:linked_dirs, "othervalue") }
203
+
204
+ it "returns without removed value" do
205
+ expect(subject).to eq ["string"]
206
+ end
207
+ end
131
208
  end
132
209
  end
133
210
  end
134
211
 
135
- describe 'keys' do
212
+ describe "keys" do
136
213
  subject { config.keys }
137
214
 
138
215
  before do
@@ -140,57 +217,69 @@ module Capistrano
140
217
  config.set(:key2, :value2)
141
218
  end
142
219
 
143
- it 'returns all set keys' do
220
+ it "returns all set keys" do
144
221
  expect(subject).to match_array [:key1, :key2]
145
222
  end
146
223
  end
147
224
 
148
- describe 'deleting' do
225
+ describe "deleting" do
149
226
  before do
150
227
  config.set(:key, :value)
151
228
  end
152
229
 
153
- it 'deletes the value' do
230
+ it "deletes the value" do
154
231
  config.delete(:key)
155
232
  expect(config.fetch(:key)).to be_nil
156
233
  end
157
234
  end
158
235
 
159
- describe 'asking' do
236
+ describe "asking" do
160
237
  let(:question) { stub }
161
238
  let(:options) { Hash.new }
162
239
 
163
240
  before do
164
- Configuration::Question.expects(:new).with(:branch, :default, options).
165
- returns(question)
241
+ Configuration::Question.expects(:new).with(:branch, :default, options)
242
+ .returns(question)
166
243
  end
167
244
 
168
- it 'prompts for the value when fetching' do
245
+ it "prompts for the value when fetching" do
169
246
  config.ask(:branch, :default, options)
170
247
  expect(config.fetch(:branch)).to eq question
171
248
  end
172
249
  end
173
250
 
174
- describe 'setting the backend' do
175
- it 'by default, is SSHKit' do
251
+ describe "setting the backend" do
252
+ it "by default, is SSHKit" do
176
253
  expect(config.backend).to eq SSHKit
177
254
  end
178
255
 
179
- it 'can be set to another class' do
256
+ it "can be set to another class" do
180
257
  config.backend = :test
181
258
  expect(config.backend).to eq :test
182
259
  end
183
260
 
184
261
  describe "ssh_options for Netssh" do
185
- it 'merges them with the :ssh_options variable' do
262
+ it "merges them with the :ssh_options variable" do
186
263
  config.set :format, :pretty
187
264
  config.set :log_level, :debug
188
- config.set :ssh_options, { user: 'albert' }
189
- SSHKit::Backend::Netssh.configure do |ssh| ssh.ssh_options = { password: 'einstein' } end
265
+ config.set :ssh_options, user: "albert"
266
+ SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = { password: "einstein" } }
190
267
  config.configure_backend
191
- expect(config.backend.config.backend.config.ssh_options).to eq({ user: 'albert', password: 'einstein' })
268
+ expect(config.backend.config.backend.config.ssh_options).to eq(user: "albert", password: "einstein")
192
269
  end
193
270
  end
194
271
  end
272
+
273
+ describe "dry_run?" do
274
+ it "returns false when using default backend" do
275
+ expect(config.dry_run?).to eq(false)
276
+ end
277
+
278
+ it "returns true when using printer backend" do
279
+ config.set :sshkit_backend, SSHKit::Backend::Printer
280
+
281
+ expect(config.dry_run?).to eq(true)
282
+ end
283
+ end
195
284
  end
196
285
  end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+ require "capistrano/doctor/environment_doctor"
3
+
4
+ module Capistrano
5
+ module Doctor
6
+ describe EnvironmentDoctor do
7
+ let(:doc) { EnvironmentDoctor.new }
8
+
9
+ it "prints using 4-space indentation" do
10
+ expect { doc.call }.to output(/^ {4}/).to_stdout
11
+ end
12
+
13
+ it "prints the Ruby version" do
14
+ expect { doc.call }.to\
15
+ output(/#{Regexp.quote(RUBY_DESCRIPTION)}/).to_stdout
16
+ end
17
+
18
+ it "prints the Rubygems version" do
19
+ expect { doc.call }.to output(/#{Regexp.quote(Gem::VERSION)}/).to_stdout
20
+ end
21
+
22
+ describe "Rake" do
23
+ before do
24
+ load File.expand_path("../../../../../lib/capistrano/doctor.rb",
25
+ __FILE__)
26
+ end
27
+
28
+ after do
29
+ Rake::Task.clear
30
+ end
31
+
32
+ it "has an doctor:environment task that calls EnvironmentDoctor" do
33
+ EnvironmentDoctor.any_instance.expects(:call)
34
+ Rake::Task["doctor:environment"].invoke
35
+ end
36
+
37
+ it "has a doctor task that depends on doctor:environment" do
38
+ expect(Rake::Task["doctor"].prerequisites).to \
39
+ include("doctor:environment")
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,61 @@
1
+ require "spec_helper"
2
+ require "capistrano/doctor/gems_doctor"
3
+ require "airbrussh/version"
4
+ require "sshkit/version"
5
+
6
+ module Capistrano
7
+ module Doctor
8
+ describe GemsDoctor do
9
+ let(:doc) { GemsDoctor.new }
10
+
11
+ it "prints using 4-space indentation" do
12
+ expect { doc.call }.to output(/^ {4}/).to_stdout
13
+ end
14
+
15
+ it "prints the Capistrano version" do
16
+ expect { doc.call }.to\
17
+ output(/capistrano\s+#{Regexp.quote(Capistrano::VERSION)}/).to_stdout
18
+ end
19
+
20
+ it "prints the Rake version" do
21
+ expect { doc.call }.to\
22
+ output(/rake\s+#{Regexp.quote(Rake::VERSION)}/).to_stdout
23
+ end
24
+
25
+ it "prints the SSHKit version" do
26
+ expect { doc.call }.to\
27
+ output(/sshkit\s+#{Regexp.quote(SSHKit::VERSION)}/).to_stdout
28
+ end
29
+
30
+ it "prints the Airbrussh version" do
31
+ expect { doc.call }.to\
32
+ output(/airbrussh\s+#{Regexp.quote(Airbrussh::VERSION)}/).to_stdout
33
+ end
34
+
35
+ it "warns that new version is available" do
36
+ Gem.stubs(:latest_version_for).returns(Gem::Version.new("99.0.0"))
37
+ expect { doc.call }.to output(/\(update available\)/).to_stdout
38
+ end
39
+
40
+ describe "Rake" do
41
+ before do
42
+ load File.expand_path("../../../../../lib/capistrano/doctor.rb",
43
+ __FILE__)
44
+ end
45
+
46
+ after do
47
+ Rake::Task.clear
48
+ end
49
+
50
+ it "has an doctor:gems task that calls GemsDoctor" do
51
+ GemsDoctor.any_instance.expects(:call)
52
+ Rake::Task["doctor:gems"].invoke
53
+ end
54
+
55
+ it "has a doctor task that depends on doctor:gems" do
56
+ expect(Rake::Task["doctor"].prerequisites).to include("doctor:gems")
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end