capistrano 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -5
  3. data/.rubocop.yml +49 -0
  4. data/.travis.yml +5 -4
  5. data/CHANGELOG.md +72 -9
  6. data/CONTRIBUTING.md +61 -93
  7. data/DEVELOPMENT.md +122 -0
  8. data/Gemfile +2 -2
  9. data/LICENSE.txt +1 -1
  10. data/README.md +121 -43
  11. data/RELEASING.md +16 -0
  12. data/Rakefile +4 -1
  13. data/bin/cap +1 -1
  14. data/capistrano.gemspec +16 -21
  15. data/features/doctor.feature +11 -0
  16. data/features/step_definitions/assertions.rb +17 -17
  17. data/features/step_definitions/cap_commands.rb +0 -1
  18. data/features/step_definitions/setup.rb +12 -8
  19. data/features/support/env.rb +5 -5
  20. data/features/support/remote_command_helpers.rb +8 -6
  21. data/features/support/vagrant_helpers.rb +5 -4
  22. data/issue_template.md +21 -0
  23. data/lib/Capfile +5 -1
  24. data/lib/capistrano/all.rb +9 -10
  25. data/lib/capistrano/application.rb +36 -26
  26. data/lib/capistrano/configuration.rb +56 -41
  27. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  28. data/lib/capistrano/configuration/filter.rb +18 -47
  29. data/lib/capistrano/configuration/host_filter.rb +30 -0
  30. data/lib/capistrano/configuration/null_filter.rb +9 -0
  31. data/lib/capistrano/configuration/plugin_installer.rb +33 -0
  32. data/lib/capistrano/configuration/question.rb +10 -7
  33. data/lib/capistrano/configuration/role_filter.rb +30 -0
  34. data/lib/capistrano/configuration/server.rb +22 -23
  35. data/lib/capistrano/configuration/servers.rb +6 -7
  36. data/lib/capistrano/configuration/variables.rb +136 -0
  37. data/lib/capistrano/defaults.rb +13 -3
  38. data/lib/capistrano/deploy.rb +1 -1
  39. data/lib/capistrano/doctor.rb +5 -0
  40. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  41. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  42. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  43. data/lib/capistrano/doctor/variables_doctor.rb +66 -0
  44. data/lib/capistrano/dotfile.rb +1 -2
  45. data/lib/capistrano/dsl.rb +12 -14
  46. data/lib/capistrano/dsl/env.rb +11 -42
  47. data/lib/capistrano/dsl/paths.rb +12 -13
  48. data/lib/capistrano/dsl/stages.rb +2 -4
  49. data/lib/capistrano/dsl/task_enhancements.rb +5 -7
  50. data/lib/capistrano/framework.rb +1 -1
  51. data/lib/capistrano/git.rb +17 -9
  52. data/lib/capistrano/hg.rb +4 -4
  53. data/lib/capistrano/i18n.rb +24 -24
  54. data/lib/capistrano/immutable_task.rb +29 -0
  55. data/lib/capistrano/install.rb +1 -1
  56. data/lib/capistrano/plugin.rb +95 -0
  57. data/lib/capistrano/scm.rb +7 -20
  58. data/lib/capistrano/setup.rb +19 -5
  59. data/lib/capistrano/svn.rb +9 -5
  60. data/lib/capistrano/tasks/console.rake +4 -8
  61. data/lib/capistrano/tasks/deploy.rake +75 -62
  62. data/lib/capistrano/tasks/doctor.rake +19 -0
  63. data/lib/capistrano/tasks/framework.rake +13 -14
  64. data/lib/capistrano/tasks/git.rake +10 -11
  65. data/lib/capistrano/tasks/hg.rake +7 -7
  66. data/lib/capistrano/tasks/install.rake +14 -15
  67. data/lib/capistrano/tasks/svn.rake +7 -7
  68. data/lib/capistrano/templates/Capfile +3 -3
  69. data/lib/capistrano/templates/deploy.rb.erb +6 -5
  70. data/lib/capistrano/upload_task.rb +1 -1
  71. data/lib/capistrano/version.rb +1 -1
  72. data/lib/capistrano/version_validator.rb +4 -6
  73. data/spec/integration/dsl_spec.rb +286 -239
  74. data/spec/integration_spec_helper.rb +3 -5
  75. data/spec/lib/capistrano/application_spec.rb +22 -14
  76. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  77. data/spec/lib/capistrano/configuration/filter_spec.rb +82 -84
  78. data/spec/lib/capistrano/configuration/host_filter_spec.rb +61 -0
  79. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  80. data/spec/lib/capistrano/configuration/question_spec.rb +12 -16
  81. data/spec/lib/capistrano/configuration/role_filter_spec.rb +64 -0
  82. data/spec/lib/capistrano/configuration/server_spec.rb +102 -110
  83. data/spec/lib/capistrano/configuration/servers_spec.rb +124 -141
  84. data/spec/lib/capistrano/configuration_spec.rb +150 -61
  85. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  86. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +61 -0
  87. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  88. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +79 -0
  89. data/spec/lib/capistrano/dsl/paths_spec.rb +58 -50
  90. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +62 -32
  91. data/spec/lib/capistrano/dsl_spec.rb +6 -8
  92. data/spec/lib/capistrano/git_spec.rb +35 -7
  93. data/spec/lib/capistrano/hg_spec.rb +14 -5
  94. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  95. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  96. data/spec/lib/capistrano/scm_spec.rb +6 -7
  97. data/spec/lib/capistrano/svn_spec.rb +40 -14
  98. data/spec/lib/capistrano/upload_task_spec.rb +7 -7
  99. data/spec/lib/capistrano/version_validator_spec.rb +37 -45
  100. data/spec/lib/capistrano_spec.rb +2 -3
  101. data/spec/spec_helper.rb +8 -8
  102. data/spec/support/Vagrantfile +9 -10
  103. data/spec/support/tasks/database.rake +3 -3
  104. data/spec/support/tasks/fail.rake +4 -3
  105. data/spec/support/tasks/failed.rake +2 -2
  106. data/spec/support/tasks/plugin.rake +6 -0
  107. data/spec/support/tasks/root.rake +4 -4
  108. data/spec/support/test_app.rb +31 -30
  109. metadata +93 -14
@@ -1,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