capistrano 3.3.5 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +36 -2
- data/README.md +30 -287
- data/capistrano.gemspec +0 -1
- data/features/sshconnect.feature +11 -0
- data/features/step_definitions/assertions.rb +6 -2
- data/features/step_definitions/setup.rb +4 -0
- data/lib/capistrano/configuration.rb +5 -1
- data/lib/capistrano/configuration/filter.rb +2 -2
- data/lib/capistrano/configuration/server.rb +15 -53
- data/lib/capistrano/configuration/servers.rb +26 -7
- data/lib/capistrano/deploy.rb +0 -1
- data/lib/capistrano/dsl.rb +2 -2
- data/lib/capistrano/dsl/env.rb +9 -1
- data/lib/capistrano/dsl/paths.rb +1 -1
- data/lib/capistrano/git.rb +1 -1
- data/lib/capistrano/tasks/deploy.rake +0 -1
- data/lib/capistrano/templates/Capfile +1 -1
- data/lib/capistrano/templates/deploy.rb.erb +3 -3
- data/lib/capistrano/templates/stage.rb.erb +31 -15
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/dsl_spec.rb +86 -113
- data/spec/lib/capistrano/configuration/filter_spec.rb +9 -3
- data/spec/lib/capistrano/configuration/server_spec.rb +34 -3
- data/spec/lib/capistrano/configuration/servers_spec.rb +37 -1
- data/spec/lib/capistrano/configuration_spec.rb +11 -0
- data/spec/lib/capistrano/dsl/paths_spec.rb +162 -41
- data/spec/support/Vagrantfile +17 -6
- data/spec/support/tasks/root.rake +11 -0
- data/spec/support/test_app.rb +1 -1
- metadata +7 -17
@@ -7,7 +7,8 @@ module Capistrano
|
|
7
7
|
let(:available) { [ Server.new('server1').add_roles([:web,:db]),
|
8
8
|
Server.new('server2').add_role(:web),
|
9
9
|
Server.new('server3').add_role(:redis),
|
10
|
-
Server.new('server4').add_role(:db)
|
10
|
+
Server.new('server4').add_role(:db),
|
11
|
+
Server.new('server5').add_role(:stageweb) ] }
|
11
12
|
|
12
13
|
describe '#new' do
|
13
14
|
it "won't create an invalid type of filter" do
|
@@ -57,7 +58,7 @@ module Capistrano
|
|
57
58
|
end
|
58
59
|
it 'correctly identifies a regex with a comma in' do
|
59
60
|
set = Filter.new(:host, 'server\d{1,3}$').filter(available)
|
60
|
-
expect(set.map(&:hostname)).to eq(%w{server1 server2 server3 server4})
|
61
|
+
expect(set.map(&:hostname)).to eq(%w{server1 server2 server3 server4 server5})
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
@@ -87,13 +88,18 @@ module Capistrano
|
|
87
88
|
expect(set.size).to eq(3)
|
88
89
|
expect(set.map(&:hostname)).to eq(%w{server1 server2 server4})
|
89
90
|
end
|
91
|
+
it 'returns only hosts for explicit roles' do
|
92
|
+
set = Filter.new(:role, [:web]).filter(available)
|
93
|
+
expect(set.size).to eq(2)
|
94
|
+
expect(set.map(&:hostname)).to eq(%w{server1 server2})
|
95
|
+
end
|
90
96
|
it 'returns hosts with regex role selection' do
|
91
97
|
set = Filter.new(:role, /red/).filter(available)
|
92
98
|
expect(set.map(&:hostname)).to eq(%w{server3})
|
93
99
|
end
|
94
100
|
it 'returns hosts with regex role selection using a string' do
|
95
101
|
set = Filter.new(:role, '/red|web/').filter(available)
|
96
|
-
expect(set.map(&:hostname)).to eq(%w{server1 server2 server3})
|
102
|
+
expect(set.map(&:hostname)).to eq(%w{server1 server2 server3 server5})
|
97
103
|
end
|
98
104
|
it 'returns hosts with combination of string role and regex' do
|
99
105
|
set = Filter.new(:role, 'db,/red/').filter(available)
|
@@ -33,7 +33,7 @@ module Capistrano
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe 'comparing identity' do
|
36
|
-
subject { server.
|
36
|
+
subject { server.hostname == Server[hostname].hostname }
|
37
37
|
|
38
38
|
context 'with the same user, hostname and port' do
|
39
39
|
let(:hostname) { 'root@hostname:1234' }
|
@@ -42,12 +42,12 @@ module Capistrano
|
|
42
42
|
|
43
43
|
context 'with a different user' do
|
44
44
|
let(:hostname) { 'deployer@hostname:1234' }
|
45
|
-
it { expect(subject).to
|
45
|
+
it { expect(subject).to be_truthy }
|
46
46
|
end
|
47
47
|
|
48
48
|
context 'with a different port' do
|
49
49
|
let(:hostname) { 'root@hostname:5678' }
|
50
|
-
it { expect(subject).to
|
50
|
+
it { expect(subject).to be_truthy }
|
51
51
|
end
|
52
52
|
|
53
53
|
context 'with a different hostname' do
|
@@ -94,6 +94,10 @@ module Capistrano
|
|
94
94
|
it 'sets the user' do
|
95
95
|
expect(server.user).to eq 'tomc'
|
96
96
|
end
|
97
|
+
|
98
|
+
it 'sets the netssh_options user' do
|
99
|
+
expect(server.netssh_options[:user]).to eq 'tomc'
|
100
|
+
end
|
97
101
|
end
|
98
102
|
|
99
103
|
context 'properties contains port' do
|
@@ -171,6 +175,18 @@ module Capistrano
|
|
171
175
|
end
|
172
176
|
end
|
173
177
|
|
178
|
+
context 'value does not match server properly' do
|
179
|
+
context 'with :active true' do
|
180
|
+
let(:options) { { active: true }}
|
181
|
+
it { expect(subject).to be_truthy }
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'with :active false' do
|
185
|
+
let(:options) { { active: false }}
|
186
|
+
it { expect(subject).to be_falsey }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
174
190
|
context 'value does not match server properly' do
|
175
191
|
context 'with :filter' do
|
176
192
|
let(:options) { { filter: :inactive }}
|
@@ -189,6 +205,18 @@ module Capistrano
|
|
189
205
|
end
|
190
206
|
end
|
191
207
|
|
208
|
+
context 'key is a property' do
|
209
|
+
context 'with :active true' do
|
210
|
+
let(:options) { { active: true }}
|
211
|
+
it { expect(subject).to be_truthy }
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'with :active false' do
|
215
|
+
let(:options) { { active: false }}
|
216
|
+
it { expect(subject).to be_falsey }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
192
220
|
context 'value is a proc' do
|
193
221
|
context 'value matches server property' do
|
194
222
|
|
@@ -261,6 +289,9 @@ module Capistrano
|
|
261
289
|
it 'contains correct user' do
|
262
290
|
expect(server.netssh_options[:user]).to eq 'another_user'
|
263
291
|
end
|
292
|
+
it 'does not affect server user in host' do
|
293
|
+
expect(server.user).to eq 'user_name'
|
294
|
+
end
|
264
295
|
it 'contains keys' do
|
265
296
|
expect(server.netssh_options[:keys]).to eq %w(/home/another_user/.ssh/id_rsa)
|
266
297
|
end
|
@@ -18,6 +18,12 @@ module Capistrano
|
|
18
18
|
expect(servers.count).to eq 1
|
19
19
|
end
|
20
20
|
|
21
|
+
it 'handles de-duplification within roles with users' do
|
22
|
+
servers.add_role(:app, %w{1}, user: 'nick')
|
23
|
+
servers.add_role(:app, %w{1}, user: 'fred')
|
24
|
+
expect(servers.count).to eq 1
|
25
|
+
end
|
26
|
+
|
21
27
|
it 'accepts instances of server objects' do
|
22
28
|
servers.add_role(:app, [Capistrano::Configuration::Server.new('example.net'), 'example.com'])
|
23
29
|
expect(servers.roles_for([:app]).length).to eq 2
|
@@ -134,7 +140,23 @@ module Capistrano
|
|
134
140
|
servers.add_host('1', roles: [:app, 'web'], test: :value, user: 'root', port: 34)
|
135
141
|
servers.add_host('1', roles: [:app, 'web'], test: :value, user: 'deployer', port: 34)
|
136
142
|
servers.add_host('1', roles: [:app, 'web'], test: :value, user: 'deployer', port: 56)
|
137
|
-
expect(servers.count).to eq(
|
143
|
+
expect(servers.count).to eq(1)
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "with a :user property" do
|
147
|
+
|
148
|
+
it 'sets the server ssh username' do
|
149
|
+
servers.add_host('1', roles: [:app, 'web'], user: 'nick')
|
150
|
+
expect(servers.count).to eq(1)
|
151
|
+
expect(servers.roles_for([:all]).first.user).to eq 'nick'
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'overwrites the value of a user specified in the hostname' do
|
155
|
+
servers.add_host('brian@1', roles: [:app, 'web'], user: 'nick')
|
156
|
+
expect(servers.count).to eq(1)
|
157
|
+
expect(servers.roles_for([:all]).first.user).to eq 'nick'
|
158
|
+
end
|
159
|
+
|
138
160
|
end
|
139
161
|
|
140
162
|
it 'overwrites the value of a previously defined scalar property' do
|
@@ -169,6 +191,20 @@ module Capistrano
|
|
169
191
|
expect(servers.roles_for([:array_test]).first.properties.array_property).to eq [1,2]
|
170
192
|
end
|
171
193
|
|
194
|
+
it 'updates roles when custom user defined' do
|
195
|
+
servers.add_host('1', roles: ['foo'], user: 'custom')
|
196
|
+
servers.add_host('1', roles: ['bar'], user: 'custom')
|
197
|
+
expect(servers.roles_for([:foo]).first.hostname).to eq '1'
|
198
|
+
expect(servers.roles_for([:bar]).first.hostname).to eq '1'
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'updates roles when custom port defined' do
|
202
|
+
servers.add_host('1', roles: ['foo'], port: 1234)
|
203
|
+
servers.add_host('1', roles: ['bar'], port: 1234)
|
204
|
+
expect(servers.roles_for([:foo]).first.hostname).to eq '1'
|
205
|
+
expect(servers.roles_for([:bar]).first.hostname).to eq '1'
|
206
|
+
end
|
207
|
+
|
172
208
|
end
|
173
209
|
|
174
210
|
describe 'selecting roles' do
|
@@ -180,6 +180,17 @@ module Capistrano
|
|
180
180
|
config.backend = :test
|
181
181
|
expect(config.backend).to eq :test
|
182
182
|
end
|
183
|
+
|
184
|
+
describe "ssh_options for Netssh" do
|
185
|
+
it 'merges them with the :ssh_options variable' do
|
186
|
+
config.set :format, :pretty
|
187
|
+
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
|
190
|
+
config.configure_backend
|
191
|
+
expect(config.backend.config.backend.config.ssh_options).to eq({ user: 'albert', password: 'einstein' })
|
192
|
+
end
|
193
|
+
end
|
183
194
|
end
|
184
195
|
end
|
185
196
|
end
|
@@ -1,69 +1,190 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
module DSL
|
3
|
+
describe Capistrano::DSL::Paths do
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
let(:dsl) { Class.new.extend Capistrano::DSL }
|
6
|
+
let(:parent) { Pathname.new('/var/shared') }
|
7
|
+
let(:paths) { Class.new.extend Capistrano::DSL::Paths }
|
8
|
+
|
9
|
+
let(:linked_dirs) { %w{log public/system} }
|
10
|
+
let(:linked_files) { %w{config/database.yml log/my.log} }
|
11
|
+
|
12
|
+
before do
|
13
|
+
dsl.set(:deploy_to, '/var/www')
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#linked_dirs' do
|
17
|
+
subject { paths.linked_dirs(parent) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
paths.expects(:fetch).with(:linked_dirs).returns(linked_dirs)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns the full pathnames' do
|
24
|
+
expect(subject).to eq [Pathname.new('/var/shared/log'), Pathname.new('/var/shared/public/system')]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
describe '#linked_files' do
|
30
|
+
subject { paths.linked_files(parent) }
|
31
|
+
|
32
|
+
before do
|
33
|
+
paths.expects(:fetch).with(:linked_files).returns(linked_files)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the full pathnames' do
|
37
|
+
expect(subject).to eq [Pathname.new('/var/shared/config/database.yml'), Pathname.new('/var/shared/log/my.log')]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#linked_file_dirs' do
|
42
|
+
subject { paths.linked_file_dirs(parent) }
|
43
|
+
|
44
|
+
before do
|
45
|
+
paths.expects(:fetch).with(:linked_files).returns(linked_files)
|
8
46
|
end
|
9
47
|
|
10
|
-
|
11
|
-
|
12
|
-
|
48
|
+
it 'returns the full paths names of the parent dirs' do
|
49
|
+
expect(subject).to eq [Pathname.new('/var/shared/config'), Pathname.new('/var/shared/log')]
|
50
|
+
end
|
51
|
+
end
|
13
52
|
|
14
|
-
|
15
|
-
|
53
|
+
describe '#linked_dir_parents' do
|
54
|
+
subject { paths.linked_dir_parents(parent) }
|
16
55
|
|
56
|
+
before do
|
57
|
+
paths.expects(:fetch).with(:linked_dirs).returns(linked_dirs)
|
58
|
+
end
|
17
59
|
|
18
|
-
|
19
|
-
|
60
|
+
it 'returns the full paths names of the parent dirs' do
|
61
|
+
expect(subject).to eq [Pathname.new('/var/shared'), Pathname.new('/var/shared/public')]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#release path' do
|
20
66
|
|
21
|
-
|
22
|
-
paths.expects(:fetch).with(:linked_dirs).returns(linked_dirs)
|
23
|
-
end
|
67
|
+
subject { dsl.release_path }
|
24
68
|
|
25
|
-
|
26
|
-
|
27
|
-
|
69
|
+
context 'where no release path has been set' do
|
70
|
+
before do
|
71
|
+
dsl.delete(:release_path)
|
28
72
|
end
|
29
73
|
|
74
|
+
it 'returns the `current_path` value' do
|
75
|
+
expect(subject.to_s).to eq '/var/www/current'
|
76
|
+
end
|
77
|
+
end
|
30
78
|
|
31
|
-
|
32
|
-
|
79
|
+
context 'where the release path has been set' do
|
80
|
+
before do
|
81
|
+
dsl.set(:release_path,'/var/www/release_path')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns the set `release_path` value' do
|
85
|
+
expect(subject.to_s).to eq '/var/www/release_path'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
33
89
|
|
34
|
-
|
35
|
-
|
36
|
-
|
90
|
+
describe '#set_release_path' do
|
91
|
+
let(:now) { Time.parse("Oct 21 16:29:00 2015") }
|
92
|
+
subject { dsl.release_path }
|
37
93
|
|
38
|
-
|
39
|
-
|
40
|
-
|
94
|
+
context 'without a timestamp' do
|
95
|
+
before do
|
96
|
+
dsl.env.expects(:timestamp).returns(now)
|
97
|
+
dsl.set_release_path
|
41
98
|
end
|
42
99
|
|
43
|
-
|
44
|
-
subject
|
100
|
+
it 'returns the release path with the current env timestamp' do
|
101
|
+
expect(subject.to_s).to eq '/var/www/releases/20151021162900'
|
102
|
+
end
|
103
|
+
end
|
45
104
|
|
46
|
-
|
47
|
-
|
48
|
-
|
105
|
+
context 'with a timestamp' do
|
106
|
+
before do
|
107
|
+
dsl.set_release_path('timestamp')
|
108
|
+
end
|
49
109
|
|
50
|
-
|
51
|
-
|
52
|
-
end
|
110
|
+
it 'returns the release path with the timestamp' do
|
111
|
+
expect(subject.to_s).to eq '/var/www/releases/timestamp'
|
53
112
|
end
|
113
|
+
end
|
114
|
+
end
|
54
115
|
|
55
|
-
|
56
|
-
|
116
|
+
describe '#deploy_config_path' do
|
117
|
+
subject { dsl.deploy_config_path.to_s }
|
57
118
|
|
58
|
-
|
59
|
-
|
60
|
-
|
119
|
+
context 'when not specified' do
|
120
|
+
before do
|
121
|
+
dsl.delete(:deploy_config_path)
|
122
|
+
end
|
61
123
|
|
62
|
-
|
63
|
-
|
64
|
-
end
|
124
|
+
it 'returns "config/deploy.rb"' do
|
125
|
+
expect(subject).to eq 'config/deploy.rb'
|
65
126
|
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when the variable :deploy_config_path is set' do
|
130
|
+
before do
|
131
|
+
dsl.set(:deploy_config_path, 'my/custom/path.rb')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'returns the custom path' do
|
135
|
+
expect(subject).to eq 'my/custom/path.rb'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#stage_config_path' do
|
141
|
+
subject { dsl.stage_config_path.to_s }
|
142
|
+
|
143
|
+
context 'when not specified' do
|
144
|
+
|
145
|
+
before do
|
146
|
+
dsl.delete(:stage_config_path)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns "config/deploy"' do
|
150
|
+
expect(subject).to eq 'config/deploy'
|
151
|
+
end
|
152
|
+
end
|
66
153
|
|
154
|
+
context 'when the variable :stage_config_path is set' do
|
155
|
+
before do
|
156
|
+
dsl.set(:stage_config_path, 'my/custom/path')
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'returns the custom path' do
|
160
|
+
expect(subject).to eq 'my/custom/path'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#repo_path' do
|
166
|
+
subject { dsl.repo_path.to_s }
|
167
|
+
|
168
|
+
context 'when not specified' do
|
169
|
+
|
170
|
+
before do
|
171
|
+
dsl.delete(:repo_path)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'returns the default #{deploy_to}/repo' do
|
175
|
+
dsl.set(:deploy_to, '/var/www')
|
176
|
+
expect(subject).to eq '/var/www/repo'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'when the variable :repo_path is set' do
|
181
|
+
before do
|
182
|
+
dsl.set(:repo_path, 'my/custom/path')
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'returns the custom path' do
|
186
|
+
expect(subject).to eq 'my/custom/path'
|
187
|
+
end
|
67
188
|
end
|
68
189
|
end
|
69
190
|
end
|
data/spec/support/Vagrantfile
CHANGED
@@ -1,13 +1,24 @@
|
|
1
|
-
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
Vagrant.configure("2") do |config|
|
4
|
+
|
5
|
+
config.ssh.insert_key = false
|
2
6
|
|
3
7
|
[:app].each_with_index do |role, i|
|
4
8
|
config.vm.define(role, primary: true) do |config|
|
5
|
-
config.vm.
|
6
|
-
config.vm.box = 'precise64'
|
7
|
-
config.vm.
|
8
|
-
config.vm.forward_port 22, "222#{i}".to_i
|
9
|
+
config.vm.define role
|
10
|
+
config.vm.box = 'hashicorp/precise64'
|
11
|
+
config.vm.network "forwarded_port", guest: 22, host: "222#{i}".to_i
|
9
12
|
config.vm.provision :shell, inline: 'sudo apt-get -y install git-core'
|
13
|
+
|
14
|
+
vagrantkey = open("https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub", "r",&:read)
|
15
|
+
|
16
|
+
config.vm.provision :shell,
|
17
|
+
inline: <<-INLINE
|
18
|
+
install -d -m 700 /root/.ssh
|
19
|
+
echo -e "#{vagrantkey}" > /root/.ssh/authorized_keys
|
20
|
+
chmod 0600 /root/.ssh/authorized_keys
|
21
|
+
INLINE
|
10
22
|
end
|
11
23
|
end
|
12
|
-
|
13
24
|
end
|