capistrano 3.0.0.pre14 → 3.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/README.md +1 -1
- data/bin/cap +1 -1
- data/capistrano.gemspec +3 -0
- data/features/deploy.feature +52 -0
- data/features/installation.feature +16 -0
- data/features/remote_file_task.feature +14 -0
- data/features/step_definitions/assertions.rb +90 -0
- data/features/step_definitions/cap_commands.rb +8 -0
- data/features/step_definitions/setup.rb +25 -0
- data/features/support/env.rb +12 -0
- data/features/support/remote_command_helpers.rb +20 -0
- data/lib/Capfile +1 -0
- data/lib/capistrano.rb +0 -14
- data/lib/capistrano/all.rb +16 -0
- data/lib/capistrano/application.rb +1 -10
- data/lib/capistrano/configuration.rb +4 -0
- data/lib/capistrano/configuration/server.rb +44 -6
- data/lib/capistrano/configuration/servers.rb +14 -51
- data/lib/capistrano/configuration/servers/role_filter.rb +86 -0
- data/lib/capistrano/defaults.rb +0 -8
- data/lib/capistrano/dsl.rb +1 -1
- data/lib/capistrano/dsl/env.rb +6 -2
- data/lib/capistrano/dsl/paths.rb +7 -4
- data/lib/capistrano/dsl/task_enhancements.rb +38 -0
- data/lib/capistrano/hg.rb +1 -0
- data/lib/capistrano/i18n.rb +1 -1
- data/lib/capistrano/setup.rb +7 -3
- data/lib/capistrano/tasks/deploy.rake +39 -9
- data/lib/capistrano/tasks/framework.rake +0 -2
- data/lib/capistrano/tasks/git.rake +3 -6
- data/lib/capistrano/tasks/hg.rake +39 -0
- data/lib/capistrano/templates/Capfile +2 -23
- data/lib/capistrano/templates/deploy.rb.erb +23 -0
- data/lib/capistrano/templates/stage.rb.erb +1 -1
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/dsl_spec.rb +71 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +69 -0
- data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +140 -0
- data/spec/lib/capistrano/configuration/servers_spec.rb +46 -9
- data/spec/lib/capistrano/configuration_spec.rb +11 -0
- data/spec/spec_helper.rb +1 -2
- data/spec/support/.gitignore +1 -0
- data/spec/support/Vagrantfile +13 -0
- data/spec/support/tasks/database.cap +11 -0
- data/spec/support/test_app.rb +55 -6
- metadata +74 -16
- data/lib/capistrano/bundler.rb +0 -1
- data/lib/capistrano/tasks/bundler.rake +0 -13
- data/spec/integration/deploy_finalize_spec.rb +0 -34
- data/spec/integration/deploy_finished_spec.rb +0 -36
- data/spec/integration/deploy_started_spec.rb +0 -74
- data/spec/integration/deploy_update_spec.rb +0 -45
- 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
|
data/lib/capistrano/version.rb
CHANGED
@@ -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 '
|
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 '
|
119
|
-
|
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:
|
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:
|
135
|
+
expect(servers.roles_for([:app, select: ->(h) { h.properties.active }]).length).to eq 1
|
137
136
|
end
|
138
137
|
|
139
|
-
it '
|
140
|
-
|
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
|