capistrano 3.0.0.pre14 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/README.md +1 -1
  4. data/bin/cap +1 -1
  5. data/capistrano.gemspec +3 -0
  6. data/features/deploy.feature +52 -0
  7. data/features/installation.feature +16 -0
  8. data/features/remote_file_task.feature +14 -0
  9. data/features/step_definitions/assertions.rb +90 -0
  10. data/features/step_definitions/cap_commands.rb +8 -0
  11. data/features/step_definitions/setup.rb +25 -0
  12. data/features/support/env.rb +12 -0
  13. data/features/support/remote_command_helpers.rb +20 -0
  14. data/lib/Capfile +1 -0
  15. data/lib/capistrano.rb +0 -14
  16. data/lib/capistrano/all.rb +16 -0
  17. data/lib/capistrano/application.rb +1 -10
  18. data/lib/capistrano/configuration.rb +4 -0
  19. data/lib/capistrano/configuration/server.rb +44 -6
  20. data/lib/capistrano/configuration/servers.rb +14 -51
  21. data/lib/capistrano/configuration/servers/role_filter.rb +86 -0
  22. data/lib/capistrano/defaults.rb +0 -8
  23. data/lib/capistrano/dsl.rb +1 -1
  24. data/lib/capistrano/dsl/env.rb +6 -2
  25. data/lib/capistrano/dsl/paths.rb +7 -4
  26. data/lib/capistrano/dsl/task_enhancements.rb +38 -0
  27. data/lib/capistrano/hg.rb +1 -0
  28. data/lib/capistrano/i18n.rb +1 -1
  29. data/lib/capistrano/setup.rb +7 -3
  30. data/lib/capistrano/tasks/deploy.rake +39 -9
  31. data/lib/capistrano/tasks/framework.rake +0 -2
  32. data/lib/capistrano/tasks/git.rake +3 -6
  33. data/lib/capistrano/tasks/hg.rake +39 -0
  34. data/lib/capistrano/templates/Capfile +2 -23
  35. data/lib/capistrano/templates/deploy.rb.erb +23 -0
  36. data/lib/capistrano/templates/stage.rb.erb +1 -1
  37. data/lib/capistrano/version.rb +1 -1
  38. data/spec/integration/dsl_spec.rb +71 -0
  39. data/spec/lib/capistrano/configuration/server_spec.rb +69 -0
  40. data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +140 -0
  41. data/spec/lib/capistrano/configuration/servers_spec.rb +46 -9
  42. data/spec/lib/capistrano/configuration_spec.rb +11 -0
  43. data/spec/spec_helper.rb +1 -2
  44. data/spec/support/.gitignore +1 -0
  45. data/spec/support/Vagrantfile +13 -0
  46. data/spec/support/tasks/database.cap +11 -0
  47. data/spec/support/test_app.rb +55 -6
  48. metadata +74 -16
  49. data/lib/capistrano/bundler.rb +0 -1
  50. data/lib/capistrano/tasks/bundler.rake +0 -13
  51. data/spec/integration/deploy_finalize_spec.rb +0 -34
  52. data/spec/integration/deploy_finished_spec.rb +0 -36
  53. data/spec/integration/deploy_started_spec.rb +0 -74
  54. data/spec/integration/deploy_update_spec.rb +0 -45
  55. data/spec/integration/installation_spec.rb +0 -76
@@ -0,0 +1,39 @@
1
+ namespace :hg do
2
+ desc 'Check that the repo is reachable'
3
+ task :check do
4
+ on roles :all do
5
+ execute "hg", "id", repo_url
6
+ end
7
+ end
8
+
9
+ desc 'Clone the repo to the cache'
10
+ task :clone do
11
+ on roles :all do
12
+ if test " [ -d #{repo_path}/.hg ] "
13
+ info t(:mirror_exists, at: repo_path)
14
+ else
15
+ within deploy_path do
16
+ execute "hg", "clone", "--noupdate", repo_url, repo_path
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'Pull changes from the remote repo'
23
+ task :update => :'hg:clone' do
24
+ on roles :all do
25
+ within repo_path do
26
+ execute "hg", "pull"
27
+ end
28
+ end
29
+ end
30
+
31
+ desc 'Copy repo to releases'
32
+ task :create_release => :'hg:update' do
33
+ on roles :all do
34
+ within repo_path do
35
+ execute "hg", "archive", release_path, "--rev", fetch(:branch)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -10,38 +10,17 @@ require 'capistrano/deploy'
10
10
  #
11
11
  # https://github.com/capistrano/rvm
12
12
  # https://github.com/capistrano/rbenv
13
+ # https://github.com/capistrano/chruby
13
14
  # https://github.com/capistrano/bundler
14
15
  # https://github.com/capistrano/rails/tree/master/assets
15
16
  # https://github.com/capistrano/rails/tree/master/migrations
16
17
  #
17
18
  # require 'capistrano/rvm'
18
19
  # require 'capistrano/rbenv'
20
+ # require 'capistrano/chruby'
19
21
  # require 'capistrano/bundler'
20
22
  # require 'capistrano/rails/assets'
21
23
  # require 'capistrano/rails/migrations'
22
24
 
23
25
  # Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
24
26
  Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
25
-
26
- namespace :deploy do
27
-
28
- desc 'Restart application'
29
- task :restart do
30
- on roles(:app), in: :sequence, wait: 5 do
31
- # Your restart mechanism here, for example:
32
- # execute :touch, release_path.join('tmp/restart.txt')
33
- end
34
- end
35
-
36
- after :restart, :clear_cache do
37
- on roles(:web), in: :groups, limit: 3, wait: 10 do
38
- # Here we can do anything such as:
39
- # within latest_release_path do
40
- # execute :rake, 'cache:clear'
41
- # end
42
- end
43
- end
44
-
45
- after :finishing, 'deploy:cleanup'
46
-
47
- end
@@ -15,3 +15,26 @@ set :repo_url, 'git@example.com:me/my_repo.git'
15
15
 
16
16
  # set :default_env, { path: "/opt/ruby/bin:$PATH" }
17
17
  # set :keep_releases, 5
18
+
19
+ namespace :deploy do
20
+
21
+ desc 'Restart application'
22
+ task :restart do
23
+ on roles(:app), in: :sequence, wait: 5 do
24
+ # Your restart mechanism here, for example:
25
+ # execute :touch, release_path.join('tmp/restart.txt')
26
+ end
27
+ end
28
+
29
+ after :restart, :clear_cache do
30
+ on roles(:web), in: :groups, limit: 3, wait: 10 do
31
+ # Here we can do anything such as:
32
+ # within release_path do
33
+ # execute :rake, 'cache:clear'
34
+ # end
35
+ end
36
+ end
37
+
38
+ after :finishing, 'deploy:cleanup'
39
+
40
+ end
@@ -39,4 +39,4 @@ server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value
39
39
  # }
40
40
  # setting per server overrides global ssh_options
41
41
 
42
- # fetch(:default_env).merge!(:rails_env, :<%= stage %>)
42
+ # fetch(:default_env).merge!(rails_env: :<%= stage %>)
@@ -1,3 +1,3 @@
1
1
  module Capistrano
2
- VERSION = "3.0.0.pre14"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -165,6 +165,20 @@ describe Capistrano::DSL do
165
165
  end
166
166
  end
167
167
 
168
+ context 'with a block' do
169
+ context 'when the variables is defined' do
170
+ it 'returns the variable' do
171
+ expect(dsl.fetch(:scm) { :svn }).to eq :git
172
+ end
173
+ end
174
+
175
+ context 'when the variables is undefined' do
176
+ it 'calls the block' do
177
+ expect(dsl.fetch(:source_control) { :svn }).to eq :svn
178
+ end
179
+ end
180
+ end
181
+
168
182
  end
169
183
 
170
184
  describe 'asking for a variable' do
@@ -270,4 +284,61 @@ describe Capistrano::DSL do
270
284
 
271
285
  end
272
286
 
287
+ describe 'release path' do
288
+
289
+ before do
290
+ dsl.set(:deploy_to, '/var/www')
291
+ end
292
+
293
+ describe 'fetching release path' do
294
+ subject { dsl.release_path }
295
+
296
+ context 'where no release path has been set' do
297
+ before do
298
+ dsl.delete(:release_path)
299
+ end
300
+
301
+ it 'returns the `current_path` value' do
302
+ expect(subject.to_s).to eq '/var/www/current'
303
+ end
304
+ end
305
+
306
+ context 'where the release path has been set' do
307
+ before do
308
+ dsl.set(:release_path, '/var/www/release_path')
309
+ end
310
+
311
+ it 'returns the set `release_path` value' do
312
+ expect(subject.to_s).to eq '/var/www/release_path'
313
+ end
314
+ end
315
+ end
316
+
317
+ describe 'setting release path' do
318
+ let(:now) { Time.parse("Oct 21 16:29:00 2015") }
319
+ subject { dsl.release_path }
320
+
321
+ context 'without a timestamp' do
322
+ before do
323
+ dsl.env.expects(:timestamp).returns(now)
324
+ dsl.set_release_path
325
+ end
326
+
327
+ it 'returns the release path with the current env timestamp' do
328
+ expect(subject.to_s).to eq '/var/www/releases/20151021162900'
329
+ end
330
+ end
331
+
332
+ context 'with a timestamp' do
333
+ before do
334
+ dsl.set_release_path('timestamp')
335
+ end
336
+
337
+ it 'returns the release path with the timestamp' do
338
+ expect(subject.to_s).to eq '/var/www/releases/timestamp'
339
+ end
340
+ end
341
+ end
342
+
343
+ end
273
344
  end
@@ -134,6 +134,75 @@ module Capistrano
134
134
  end
135
135
  end
136
136
 
137
+ describe '#include?' do
138
+ let(:options) { {} }
139
+
140
+ subject { server.select?(options) }
141
+
142
+ before do
143
+ server.properties.active = true
144
+ end
145
+
146
+ context 'options are empty' do
147
+ it { should be_true }
148
+ end
149
+
150
+ context 'value is a symbol' do
151
+ context 'value matches server property' do
152
+
153
+ context 'with :filter' do
154
+ let(:options) { { filter: :active }}
155
+ it { should be_true }
156
+ end
157
+
158
+ context 'with :select' do
159
+ let(:options) { { select: :active }}
160
+ it { should be_true }
161
+ end
162
+ end
163
+
164
+ context 'value does not match server properly' do
165
+ context 'with :filter' do
166
+ let(:options) { { filter: :inactive }}
167
+ it { should be_false }
168
+ end
169
+
170
+ context 'with :select' do
171
+ let(:options) { { select: :inactive }}
172
+ it { should be_false }
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'value is a proc' do
178
+ context 'value matches server property' do
179
+
180
+ context 'with :filter' do
181
+ let(:options) { { filter: ->(s) { s.properties.active } } }
182
+ it { should be_true }
183
+ end
184
+
185
+ context 'with :select' do
186
+ let(:options) { { select: ->(s) { s.properties.active } } }
187
+ it { should be_true }
188
+ end
189
+ end
190
+
191
+ context 'value does not match server properly' do
192
+ context 'with :filter' do
193
+ let(:options) { { filter: ->(s) { s.properties.inactive } } }
194
+ it { should be_false }
195
+ end
196
+
197
+ context 'with :select' do
198
+ let(:options) { { select: ->(s) { s.properties.inactive } } }
199
+ it { should be_false }
200
+ end
201
+ end
202
+ end
203
+
204
+ end
205
+
137
206
  describe 'assign ssh_options' do
138
207
  let(:server) { Server.new('user_name@hostname') }
139
208
 
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ class Configuration
5
+ class Servers
6
+
7
+ describe RoleFilter do
8
+ let(:role_filter) { RoleFilter.new(required, available) }
9
+ let(:required) { [] }
10
+ let(:available) { [:web, :app, :db] }
11
+
12
+ describe '#new' do
13
+ it 'takes two arrays of role names' do
14
+ expect(role_filter)
15
+ end
16
+ end
17
+
18
+ describe '.for' do
19
+
20
+ subject { RoleFilter.for(required, available) }
21
+
22
+ context 'without env vars' do
23
+ context ':all required' do
24
+ let(:required) { [:all] }
25
+
26
+ it 'returns all available names' do
27
+ expect(subject).to eq available
28
+ end
29
+ end
30
+
31
+ context 'role names required' do
32
+ let(:required) { [:web, :app] }
33
+ it 'returns all required names' do
34
+ expect(subject).to eq required
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'with ENV vars' do
40
+ before do
41
+ ENV.stubs(:[]).with('ROLES').returns('app,web')
42
+ end
43
+
44
+ context ':all required' do
45
+ let(:required) { [:all] }
46
+
47
+ it 'returns available names defined in ROLES' do
48
+ expect(subject).to eq [:app, :web]
49
+ end
50
+ end
51
+
52
+ context 'role names required' do
53
+ let(:required) { [:web, :db] }
54
+ it 'returns all required names defined in ROLES' do
55
+ expect(subject).to eq [:web]
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'with configuration filters' do
61
+ before do
62
+ Configuration.env.set(:filter, roles: %w{app web})
63
+ end
64
+
65
+ context ':all required' do
66
+ let(:required) { [:all] }
67
+
68
+ it 'returns available names defined in the filter' do
69
+ expect(subject).to eq [:app, :web]
70
+ end
71
+ end
72
+
73
+ context 'role names required' do
74
+ let(:required) { [:web, :db] }
75
+ it 'returns all required names defined in the filter' do
76
+ expect(subject).to eq [:web]
77
+ end
78
+ end
79
+
80
+ after do
81
+ Configuration.env.delete(:filter)
82
+ end
83
+ end
84
+
85
+ context 'with a single configuration filter' do
86
+ before do
87
+ Configuration.env.set(:filter, roles: 'web')
88
+ end
89
+
90
+ context ':all required' do
91
+ let(:required) { [:all] }
92
+
93
+ it 'returns available names defined in the filter' do
94
+ expect(subject).to eq [:web]
95
+ end
96
+ end
97
+
98
+ context 'role names required' do
99
+ let(:required) { [:web, :db] }
100
+ it 'returns all required names defined in the filter' do
101
+ expect(subject).to eq [:web]
102
+ end
103
+ end
104
+
105
+ after do
106
+ Configuration.env.delete(:filter)
107
+ end
108
+ end
109
+
110
+ context 'with configuration filters and ENV vars' do
111
+ before do
112
+ Configuration.env.set(:filter, roles: %w{app})
113
+ ENV.stubs(:[]).with('ROLES').returns('web')
114
+ end
115
+
116
+ context ':all required' do
117
+ let(:required) { [:all] }
118
+
119
+ it 'returns available names defined in the filter' do
120
+ expect(subject).to eq [:web, :app]
121
+ end
122
+ end
123
+
124
+ context 'role names required' do
125
+ let(:required) { [:web, :db] }
126
+ it 'returns all required names defined in the filter' do
127
+ expect(subject).to eq [:web]
128
+ end
129
+ end
130
+
131
+ after do
132
+ Configuration.env.delete(:filter)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+ end
@@ -108,16 +108,15 @@ module Capistrano
108
108
 
109
109
  end
110
110
 
111
- describe '#roles' do
111
+ describe 'selecting roles' do
112
112
 
113
113
  before do
114
114
  servers.add_host('1', roles: :app, active: true)
115
115
  servers.add_host('2', roles: :app)
116
116
  end
117
117
 
118
- it 'raises if the filter would remove all matching hosts' do
119
- I18n.expects(:t)
120
- expect { servers.roles_for([:app, select: :inactive]) }.to raise_error
118
+ it 'is empty if the filter would remove all matching hosts' do
119
+ expect(servers.roles_for([:app, select: :inactive])).to be_empty
121
120
  end
122
121
 
123
122
  it 'can filter hosts by properties on the host object using symbol as shorthand' do
@@ -129,19 +128,57 @@ module Capistrano
129
128
  end
130
129
 
131
130
  it 'can filter hosts by properties on the host using a regular proc' do
132
- expect(servers.roles_for([:app, filter: lambda { |h| h.properties.active }]).length).to eq 1
131
+ expect(servers.roles_for([:app, filter: ->(h) { h.properties.active }]).length).to eq 1
133
132
  end
134
133
 
135
134
  it 'can select hosts by properties on the host using a regular proc' do
136
- expect(servers.roles_for([:app, select: lambda { |h| h.properties.active }]).length).to eq 1
135
+ expect(servers.roles_for([:app, select: ->(h) { h.properties.active }]).length).to eq 1
137
136
  end
138
137
 
139
- it 'raises if the regular proc filter would remove all matching hosts' do
140
- I18n.expects(:t)
141
- expect { servers.roles_for([:app, select: lambda { |h| h.properties.inactive }])}.to raise_error
138
+ it 'is empty if the regular proc filter would remove all matching hosts' do
139
+ expect(servers.roles_for([:app, select: ->(h) { h.properties.inactive }])).to be_empty
142
140
  end
143
141
 
144
142
  end
143
+
144
+ describe 'filtering roles' do
145
+
146
+ before do
147
+ ENV.stubs(:[]).with('ROLES').returns('web,db')
148
+ servers.add_host('1', roles: :app, active: true)
149
+ servers.add_host('2', roles: :app)
150
+ servers.add_host('3', roles: :web)
151
+ servers.add_host('4', roles: :web)
152
+ servers.add_host('5', roles: :db)
153
+ end
154
+
155
+ subject { servers.roles_for(roles).map(&:hostname) }
156
+
157
+ context 'when selecting all roles' do
158
+ let(:roles) { [:all] }
159
+
160
+ it 'returns the roles specified by ROLE' do
161
+ expect(subject).to eq %w{3 4 5}
162
+ end
163
+ end
164
+
165
+ context 'when selecting roles included in ROLE' do
166
+ let(:roles) { [:app, :web] }
167
+
168
+ it 'returns only roles that match ROLE' do
169
+ expect(subject).to eq %w{3 4}
170
+ end
171
+ end
172
+
173
+ context 'when selecting roles not included in ROLE' do
174
+ let(:roles) { [:app] }
175
+
176
+ it 'is empty' do
177
+ expect(subject).to be_empty
178
+ end
179
+ end
180
+ end
181
+
145
182
  end
146
183
  end
147
184
  end