capistrano 3.4.0 → 3.17.1

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