capistrano 3.4.0 → 3.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +129 -0
  3. data/.github/issue_template.md +19 -0
  4. data/.github/pull_request_template.md +22 -0
  5. data/.github/release-drafter.yml +17 -0
  6. data/.github/workflows/push.yml +12 -0
  7. data/.gitignore +8 -5
  8. data/.rubocop.yml +62 -0
  9. data/CHANGELOG.md +1 -307
  10. data/CONTRIBUTING.md +63 -93
  11. data/DEVELOPMENT.md +127 -0
  12. data/Dangerfile +1 -0
  13. data/Gemfile +40 -3
  14. data/LICENSE.txt +1 -1
  15. data/README.md +127 -44
  16. data/RELEASING.md +17 -0
  17. data/Rakefile +13 -2
  18. data/UPGRADING-3.7.md +86 -0
  19. data/bin/cap +1 -1
  20. data/capistrano.gemspec +21 -24
  21. data/features/deploy.feature +35 -1
  22. data/features/doctor.feature +11 -0
  23. data/features/installation.feature +8 -3
  24. data/features/stage_failure.feature +9 -0
  25. data/features/step_definitions/assertions.rb +51 -18
  26. data/features/step_definitions/cap_commands.rb +9 -0
  27. data/features/step_definitions/setup.rb +53 -9
  28. data/features/subdirectory.feature +9 -0
  29. data/features/support/env.rb +5 -5
  30. data/features/support/remote_command_helpers.rb +12 -6
  31. data/features/support/vagrant_helpers.rb +17 -11
  32. data/lib/Capfile +1 -1
  33. data/lib/capistrano/all.rb +10 -10
  34. data/lib/capistrano/application.rb +47 -34
  35. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  36. data/lib/capistrano/configuration/filter.rb +17 -47
  37. data/lib/capistrano/configuration/host_filter.rb +29 -0
  38. data/lib/capistrano/configuration/null_filter.rb +9 -0
  39. data/lib/capistrano/configuration/plugin_installer.rb +51 -0
  40. data/lib/capistrano/configuration/question.rb +31 -9
  41. data/lib/capistrano/configuration/role_filter.rb +29 -0
  42. data/lib/capistrano/configuration/scm_resolver.rb +149 -0
  43. data/lib/capistrano/configuration/server.rb +29 -23
  44. data/lib/capistrano/configuration/servers.rb +21 -14
  45. data/lib/capistrano/configuration/validated_variables.rb +110 -0
  46. data/lib/capistrano/configuration/variables.rb +112 -0
  47. data/lib/capistrano/configuration.rb +91 -44
  48. data/lib/capistrano/defaults.rb +26 -4
  49. data/lib/capistrano/deploy.rb +1 -1
  50. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  51. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  52. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  53. data/lib/capistrano/doctor/servers_doctor.rb +105 -0
  54. data/lib/capistrano/doctor/variables_doctor.rb +74 -0
  55. data/lib/capistrano/doctor.rb +6 -0
  56. data/lib/capistrano/dotfile.rb +1 -2
  57. data/lib/capistrano/dsl/env.rb +9 -47
  58. data/lib/capistrano/dsl/paths.rb +11 -25
  59. data/lib/capistrano/dsl/stages.rb +14 -2
  60. data/lib/capistrano/dsl/task_enhancements.rb +7 -12
  61. data/lib/capistrano/dsl.rb +47 -16
  62. data/lib/capistrano/framework.rb +1 -1
  63. data/lib/capistrano/i18n.rb +32 -24
  64. data/lib/capistrano/immutable_task.rb +30 -0
  65. data/lib/capistrano/install.rb +1 -1
  66. data/lib/capistrano/plugin.rb +95 -0
  67. data/lib/capistrano/proc_helpers.rb +13 -0
  68. data/lib/capistrano/scm/git.rb +100 -0
  69. data/lib/capistrano/scm/hg.rb +55 -0
  70. data/lib/capistrano/scm/plugin.rb +13 -0
  71. data/lib/capistrano/scm/svn.rb +56 -0
  72. data/lib/capistrano/scm/tasks/git.rake +73 -0
  73. data/lib/capistrano/scm/tasks/hg.rake +53 -0
  74. data/lib/capistrano/scm/tasks/svn.rake +53 -0
  75. data/lib/capistrano/scm.rb +7 -20
  76. data/lib/capistrano/setup.rb +20 -6
  77. data/lib/capistrano/tasks/console.rake +4 -8
  78. data/lib/capistrano/tasks/deploy.rake +105 -73
  79. data/lib/capistrano/tasks/doctor.rake +24 -0
  80. data/lib/capistrano/tasks/framework.rake +13 -14
  81. data/lib/capistrano/tasks/install.rake +14 -15
  82. data/lib/capistrano/templates/Capfile +21 -10
  83. data/lib/capistrano/templates/deploy.rb.erb +17 -26
  84. data/lib/capistrano/templates/stage.rb.erb +9 -9
  85. data/lib/capistrano/upload_task.rb +1 -1
  86. data/lib/capistrano/version.rb +1 -1
  87. data/lib/capistrano/version_validator.rb +5 -10
  88. data/spec/integration/dsl_spec.rb +289 -240
  89. data/spec/integration_spec_helper.rb +3 -5
  90. data/spec/lib/capistrano/application_spec.rb +23 -39
  91. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  92. data/spec/lib/capistrano/configuration/filter_spec.rb +83 -85
  93. data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
  94. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  95. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
  96. data/spec/lib/capistrano/configuration/question_spec.rb +58 -26
  97. data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
  98. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
  99. data/spec/lib/capistrano/configuration/server_spec.rb +106 -113
  100. data/spec/lib/capistrano/configuration/servers_spec.rb +129 -145
  101. data/spec/lib/capistrano/configuration_spec.rb +224 -63
  102. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  103. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
  104. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  105. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
  106. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
  107. data/spec/lib/capistrano/dsl/paths_spec.rb +97 -59
  108. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +57 -37
  109. data/spec/lib/capistrano/dsl_spec.rb +84 -11
  110. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  111. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  112. data/spec/lib/capistrano/scm/git_spec.rb +184 -0
  113. data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
  114. data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
  115. data/spec/lib/capistrano/scm_spec.rb +7 -8
  116. data/spec/lib/capistrano/upload_task_spec.rb +7 -7
  117. data/spec/lib/capistrano/version_validator_spec.rb +61 -46
  118. data/spec/lib/capistrano_spec.rb +2 -3
  119. data/spec/spec_helper.rb +21 -8
  120. data/spec/support/Vagrantfile +9 -10
  121. data/spec/support/tasks/database.rake +3 -3
  122. data/spec/support/tasks/fail.rake +4 -3
  123. data/spec/support/tasks/failed.rake +2 -2
  124. data/spec/support/tasks/plugin.rake +6 -0
  125. data/spec/support/tasks/root.rake +4 -4
  126. data/spec/support/test_app.rb +64 -39
  127. metadata +100 -55
  128. data/.travis.yml +0 -13
  129. data/features/remote_file_task.feature +0 -14
  130. data/lib/capistrano/git.rb +0 -46
  131. data/lib/capistrano/hg.rb +0 -43
  132. data/lib/capistrano/svn.rb +0 -38
  133. data/lib/capistrano/tasks/git.rake +0 -81
  134. data/lib/capistrano/tasks/hg.rake +0 -52
  135. data/lib/capistrano/tasks/svn.rake +0 -52
  136. data/spec/lib/capistrano/git_spec.rb +0 -81
  137. data/spec/lib/capistrano/hg_spec.rb +0 -81
  138. data/spec/lib/capistrano/svn_spec.rb +0 -79
@@ -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,200 @@ 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 string without error" do
154
+ config.set(:key, "longer_value")
155
+ end
156
+
157
+ it "validates block without error" do
158
+ config.set(:key) { "longer_value" }
159
+ expect(config.fetch(:key)).to eq "longer_value"
160
+ end
161
+
162
+ it "validates lambda without error" do
163
+ config.set :key, -> { "longer_value" }
164
+ expect(config.fetch(:key)).to eq "longer_value"
165
+ end
166
+
167
+ it "raises an exception on invalid string" do
168
+ expect { config.set(:key, "sho") }.to raise_error(Capistrano::ValidationError)
169
+ end
170
+
171
+ it "raises an exception on invalid string provided by block" do
172
+ config.set(:key) { "sho" }
173
+ expect { config.fetch(:key) }.to raise_error(Capistrano::ValidationError)
174
+ end
175
+
176
+ it "raises an exception on invalid string provided by lambda" do
177
+ config.set :key, -> { "sho" }
178
+ expect { config.fetch(:key) }.to raise_error(Capistrano::ValidationError)
179
+ end
180
+ end
181
+
182
+ context "appending" do
183
+ subject { config.append(:linked_dirs, "vendor/bundle", "tmp") }
184
+
185
+ it "returns appended value" do
186
+ expect(subject).to eq ["vendor/bundle", "tmp"]
187
+ end
188
+
189
+ context "on non-array variable" do
190
+ before { config.set(:linked_dirs, "string") }
191
+ subject { config.append(:linked_dirs, "vendor/bundle") }
192
+
193
+ it "returns appended value" do
194
+ expect(subject).to eq ["string", "vendor/bundle"]
195
+ end
196
+ end
197
+ end
198
+
199
+ context "removing" do
200
+ before :each do
201
+ config.set(:linked_dirs, ["vendor/bundle", "tmp"])
202
+ end
203
+
204
+ subject { config.remove(:linked_dirs, "vendor/bundle") }
128
205
 
129
- it 'returns the block value' do
130
- expect { subject }.to raise_error
206
+ it "returns without removed value" do
207
+ expect(subject).to eq ["tmp"]
208
+ end
209
+
210
+ context "on non-array variable" do
211
+ before { config.set(:linked_dirs, "string") }
212
+
213
+ context "when removing same value" do
214
+ subject { config.remove(:linked_dirs, "string") }
215
+
216
+ it "returns without removed value" do
217
+ expect(subject).to eq []
218
+ end
219
+ end
220
+
221
+ context "when removing different value" do
222
+ subject { config.remove(:linked_dirs, "othervalue") }
223
+
224
+ it "returns without removed value" do
225
+ expect(subject).to eq ["string"]
226
+ end
227
+ end
131
228
  end
132
229
  end
133
230
  end
134
231
 
135
- describe 'keys' do
232
+ describe "keys" do
136
233
  subject { config.keys }
137
234
 
138
235
  before do
@@ -140,56 +237,120 @@ module Capistrano
140
237
  config.set(:key2, :value2)
141
238
  end
142
239
 
143
- it 'returns all set keys' do
144
- expect(subject).to match_array [:key1, :key2]
240
+ it "returns all set keys" do
241
+ expect(subject).to match_array %i(key1 key2)
145
242
  end
146
243
  end
147
244
 
148
- describe 'deleting' do
245
+ describe "deleting" do
149
246
  before do
150
247
  config.set(:key, :value)
151
248
  end
152
249
 
153
- it 'deletes the value' do
250
+ it "deletes the value" do
154
251
  config.delete(:key)
155
252
  expect(config.fetch(:key)).to be_nil
156
253
  end
157
254
  end
158
255
 
159
- describe 'asking' do
256
+ describe "asking" do
160
257
  let(:question) { stub }
161
- let(:options) { Hash.new }
258
+ let(:options) { {} }
162
259
 
163
260
  before do
164
- Configuration::Question.expects(:new).with(:branch, :default, options).
165
- returns(question)
261
+ Configuration::Question.expects(:new).with(:branch, :default, options)
262
+ .returns(question)
166
263
  end
167
264
 
168
- it 'prompts for the value when fetching' do
265
+ it "prompts for the value when fetching" do
169
266
  config.ask(:branch, :default, options)
170
267
  expect(config.fetch(:branch)).to eq question
171
268
  end
172
269
  end
173
270
 
174
- describe 'setting the backend' do
175
- it 'by default, is SSHKit' do
271
+ describe "setting the backend" do
272
+ it "by default, is SSHKit" do
176
273
  expect(config.backend).to eq SSHKit
177
274
  end
178
275
 
179
- it 'can be set to another class' do
276
+ it "can be set to another class" do
180
277
  config.backend = :test
181
278
  expect(config.backend).to eq :test
182
279
  end
183
280
 
184
281
  describe "ssh_options for Netssh" do
185
- it 'merges them with the :ssh_options variable' do
282
+ it "merges them with the :ssh_options variable" do
186
283
  config.set :format, :pretty
187
284
  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
285
+ config.set :ssh_options, user: "albert"
286
+ SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = { password: "einstein" } }
190
287
  config.configure_backend
191
- expect(config.backend.config.backend.config.ssh_options).to eq({ user: 'albert', password: 'einstein' })
288
+
289
+ expect(
290
+ config.backend.config.backend.config.ssh_options
291
+ ).to include(user: "albert", password: "einstein")
292
+ end
293
+ end
294
+ end
295
+
296
+ describe "dry_run?" do
297
+ it "returns false when using default backend" do
298
+ expect(config.dry_run?).to eq(false)
299
+ end
300
+
301
+ it "returns true when using printer backend" do
302
+ config.set :sshkit_backend, SSHKit::Backend::Printer
303
+
304
+ expect(config.dry_run?).to eq(true)
305
+ end
306
+ end
307
+
308
+ describe "custom filtering" do
309
+ it "accepts a custom filter object" do
310
+ filter = Object.new
311
+ def filter.filter(servers)
312
+ servers
192
313
  end
314
+ config.add_filter(filter)
315
+ end
316
+
317
+ it "accepts a custom filter as a block" do
318
+ config.add_filter { |servers| servers }
319
+ end
320
+
321
+ it "raises an error if passed a block and an object" do
322
+ filter = Object.new
323
+ def filter.filter(servers)
324
+ servers
325
+ end
326
+
327
+ expect { config.add_filter(filter) { |servers| servers } }.to raise_error(ArgumentError)
328
+ end
329
+
330
+ it "raises an error if the filter lacks a filter method" do
331
+ filter = Object.new
332
+ expect { config.add_filter(filter) }.to raise_error(TypeError)
333
+ end
334
+
335
+ it "calls the filter method of a custom filter" do
336
+ ENV.delete "ROLES"
337
+ ENV.delete "HOSTS"
338
+
339
+ servers = Configuration::Servers.new
340
+
341
+ servers.add_host("test1")
342
+ servers.add_host("test2")
343
+ servers.add_host("test3")
344
+
345
+ filtered_servers = servers.take(2)
346
+
347
+ filter = mock("custom filter")
348
+ filter.expects(:filter)
349
+ .with { |subset| subset.is_a? Configuration::Servers }
350
+ .returns(filtered_servers)
351
+
352
+ config.add_filter(filter)
353
+ expect(config.filter(servers)).to eq(filtered_servers)
193
354
  end
194
355
  end
195
356
  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", capture_io: true 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,67 @@
1
+ require "spec_helper"
2
+ require "capistrano/doctor/gems_doctor"
3
+ require "airbrussh/version"
4
+ require "sshkit/version"
5
+ require "net/ssh/version"
6
+
7
+ module Capistrano
8
+ module Doctor
9
+ describe GemsDoctor do
10
+ let(:doc) { GemsDoctor.new }
11
+
12
+ it "prints using 4-space indentation" do
13
+ expect { doc.call }.to output(/^ {4}/).to_stdout
14
+ end
15
+
16
+ it "prints the Capistrano version" do
17
+ expect { doc.call }.to\
18
+ output(/capistrano\s+#{Regexp.quote(Capistrano::VERSION)}/).to_stdout
19
+ end
20
+
21
+ it "prints the Rake version" do
22
+ expect { doc.call }.to\
23
+ output(/rake\s+#{Regexp.quote(Rake::VERSION)}/).to_stdout
24
+ end
25
+
26
+ it "prints the SSHKit version" do
27
+ expect { doc.call }.to\
28
+ output(/sshkit\s+#{Regexp.quote(SSHKit::VERSION)}/).to_stdout
29
+ end
30
+
31
+ it "prints the Airbrussh version" do
32
+ expect { doc.call }.to\
33
+ output(/airbrussh\s+#{Regexp.quote(Airbrussh::VERSION)}/).to_stdout
34
+ end
35
+
36
+ it "prints the net-ssh version" do
37
+ expect { doc.call }.to\
38
+ output(/net-ssh\s+#{Regexp.quote(Net::SSH::Version::STRING)}/).to_stdout
39
+ end
40
+
41
+ it "warns that new version is available" do
42
+ Gem.stubs(:latest_version_for).returns(Gem::Version.new("99.0.0"))
43
+ expect { doc.call }.to output(/\(update available\)/).to_stdout
44
+ end
45
+
46
+ describe "Rake" do
47
+ before do
48
+ load File.expand_path("../../../../../lib/capistrano/doctor.rb",
49
+ __FILE__)
50
+ end
51
+
52
+ after do
53
+ Rake::Task.clear
54
+ end
55
+
56
+ it "has an doctor:gems task that calls GemsDoctor", capture_io: true do
57
+ GemsDoctor.any_instance.expects(:call)
58
+ Rake::Task["doctor:gems"].invoke
59
+ end
60
+
61
+ it "has a doctor task that depends on doctor:gems" do
62
+ expect(Rake::Task["doctor"].prerequisites).to include("doctor:gems")
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+ require "capistrano/doctor/output_helpers"
3
+
4
+ module Capistrano
5
+ module Doctor
6
+ describe OutputHelpers do
7
+ include OutputHelpers
8
+
9
+ # Force color for the purpose of these tests
10
+ before { ENV.stubs(:[]).with("SSHKIT_COLOR").returns("1") }
11
+
12
+ it "prints titles in blue with newlines and without indentation" do
13
+ expect { title("Hello!") }.to\
14
+ output("\e[0;34;49m\nHello!\n\e[0m\n").to_stdout
15
+ end
16
+
17
+ it "prints warnings in yellow with 4-space indentation" do
18
+ expect { warning("Yikes!") }.to\
19
+ output(" \e[0;33;49mYikes!\e[0m\n").to_stdout
20
+ end
21
+
22
+ it "overrides puts to indent 4 spaces per line" do
23
+ expect { puts("one\ntwo") }.to output(" one\n two\n").to_stdout
24
+ end
25
+
26
+ it "formats tables with indent, aligned columns and per-row color" do
27
+ data = [
28
+ ["one", ".", "1"],
29
+ ["two", "..", "2"],
30
+ ["three", "...", "3"]
31
+ ]
32
+ block = proc do |record, row|
33
+ row.yellow if record.first == "two"
34
+ row << record[0]
35
+ row << record[1]
36
+ row << record[2]
37
+ end
38
+ expected_output = <<-OUT
39
+ one . 1
40
+ \e[0;33;49mtwo .. 2\e[0m
41
+ three ... 3
42
+ OUT
43
+ expect { table(data, &block) }.to output(expected_output).to_stdout
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+ require "capistrano/doctor/servers_doctor"
3
+
4
+ module Capistrano
5
+ module Doctor
6
+ describe ServersDoctor do
7
+ include Capistrano::DSL
8
+ let(:doc) { ServersDoctor.new }
9
+
10
+ before { Capistrano::Configuration.reset! }
11
+ after { Capistrano::Configuration.reset! }
12
+
13
+ it "prints using 4-space indentation" do
14
+ expect { doc.call }.to output(/^ {4}/).to_stdout
15
+ end
16
+
17
+ it "prints the number of defined servers" do
18
+ role :app, %w(example.com)
19
+ server "www@example.com:22"
20
+
21
+ expect { doc.call }.to output(/Servers \(2\)/).to_stdout
22
+ end
23
+
24
+ describe "prints the server's details" do
25
+ it "including username" do
26
+ server "www@example.com"
27
+ expect { doc.call }.to output(/www@example.com/).to_stdout
28
+ end
29
+
30
+ it "including port" do
31
+ server "www@example.com:22"
32
+ expect { doc.call }.to output(/www@example.com:22/).to_stdout
33
+ end
34
+
35
+ it "including roles" do
36
+ role :app, %w(example.com)
37
+ expect { doc.call }.to output(/example.com\s+\[:app\]/).to_stdout
38
+ end
39
+
40
+ it "including empty roles" do
41
+ server "example.com"
42
+ expect { doc.call }.to output(/example.com\s+\[\]/).to_stdout
43
+ end
44
+
45
+ it "including properties" do
46
+ server "example.com", roles: %w(app db), primary: true
47
+ expect { doc.call }.to \
48
+ output(/example.com\s+\[:app, :db\]\s+\{ :primary => true \}/).to_stdout
49
+ end
50
+
51
+ it "including misleading role name alert" do
52
+ server "example.com", roles: ["web app db"]
53
+ warning_msg = 'Whitespace detected in role(s) :"web app db". ' \
54
+ 'This might be a result of a mistyped "%w()" array literal'
55
+
56
+ expect { doc.call }.to output(/#{Regexp.escape(warning_msg)}/).to_stdout
57
+ end
58
+ end
59
+
60
+ it "doesn't fail for no servers" do
61
+ expect { doc.call }.to output("\nServers (0)\n \n").to_stdout
62
+ end
63
+
64
+ describe "Rake" do
65
+ before do
66
+ load File.expand_path("../../../../../lib/capistrano/doctor.rb",
67
+ __FILE__)
68
+ end
69
+
70
+ after do
71
+ Rake::Task.clear
72
+ end
73
+
74
+ it "has an doctor:servers task that calls ServersDoctor", capture_io: true do
75
+ ServersDoctor.any_instance.expects(:call)
76
+ Rake::Task["doctor:servers"].invoke
77
+ end
78
+
79
+ it "has a doctor task that depends on doctor:servers" do
80
+ expect(Rake::Task["doctor"].prerequisites).to \
81
+ include("doctor:servers")
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end