capistrano 3.3.5 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -106,10 +106,14 @@ When(/^an error is raised$/) do
106
106
  run_vagrant_command(test_file_exists(error))
107
107
  end
108
108
 
109
- Then(/contains "(.*?)" in the output/) do |expected|
109
+ Then(/contains "([^"]*)" in the output/) do |expected|
110
110
  expect(@output).to include(expected)
111
111
  end
112
112
 
113
- Then(/doesn't contain "(.*?)" in the output/) do |expected|
113
+ Then(/the output matches "([^"]*)" followed by "([^"]*)"/) do |expected, followedby|
114
+ expect(@output).to match(/#{expected}.*#{followedby}/m)
115
+ end
116
+
117
+ Then(/doesn't contain "([^"]*)" in the output/) do |expected|
114
118
  expect(@output).not_to include(expected)
115
119
  end
@@ -27,6 +27,10 @@ Given(/^a custom task to generate a file$/) do
27
27
  TestApp.copy_task_to_test_app('spec/support/tasks/database.rake')
28
28
  end
29
29
 
30
+ Given(/^a task which executes as root$/) do
31
+ TestApp.copy_task_to_test_app('spec/support/tasks/root.rake')
32
+ end
33
+
30
34
  Given(/config stage file has line "(.*?)"/) do |line|
31
35
  TestApp.append_to_deploy_file(line)
32
36
  end
@@ -63,6 +63,10 @@ module Capistrano
63
63
  servers.roles_for(names)
64
64
  end
65
65
 
66
+ def role_properties_for(names, &block)
67
+ servers.role_properties_for(names, &block)
68
+ end
69
+
66
70
  def primary(role)
67
71
  servers.fetch_primary(role)
68
72
  end
@@ -82,7 +86,7 @@ module Capistrano
82
86
  sshkit.backend.configure do |backend|
83
87
  backend.pty = fetch(:pty)
84
88
  backend.connection_timeout = fetch(:connection_timeout)
85
- backend.ssh_options = fetch(:ssh_options) if fetch(:ssh_options)
89
+ backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options,{}))
86
90
  end
87
91
  end
88
92
  end
@@ -5,7 +5,7 @@ module Capistrano
5
5
  class Filter
6
6
  def initialize type, values = nil
7
7
  raise "Invalid filter type #{type}" unless [:host,:role].include? type
8
- av = Array(values)
8
+ av = Array(values).dup
9
9
  @mode = case
10
10
  when av.size == 0 then :none
11
11
  when av.include?(:all) then :all
@@ -32,7 +32,7 @@ module Capistrano
32
32
  when Regexp then v
33
33
  else
34
34
  vs = v.to_s
35
- vs =~ %r{^/(.+)/$} ? Regexp.new($1) : vs
35
+ vs =~ %r{^/(.+)/$} ? Regexp.new($1) : %r{^#{vs}$}
36
36
  end
37
37
  end
38
38
  Regexp.union av
@@ -25,8 +25,19 @@ module Capistrano
25
25
  end
26
26
 
27
27
  def select?(options)
28
- selector = Selector.for(options)
29
- selector.call(self)
28
+ options.each do |k,v|
29
+ callable = v.respond_to?(:call) ? v: ->(server){server.fetch(v)}
30
+ result = case k
31
+ when :filter, :select
32
+ callable.call(self)
33
+ when :exclude
34
+ !callable.call(self)
35
+ else
36
+ self.fetch(k) == v
37
+ end
38
+ return false unless result
39
+ end
40
+ return true
30
41
  end
31
42
 
32
43
  def primary
@@ -51,7 +62,7 @@ module Capistrano
51
62
  end
52
63
 
53
64
  def matches?(other)
54
- user == other.user && hostname == other.hostname && port == other.port
65
+ hostname == other.hostname
55
66
  end
56
67
 
57
68
  private
@@ -87,7 +98,7 @@ module Capistrano
87
98
  @properties[key]
88
99
  end
89
100
 
90
- def respond_to?(method)
101
+ def respond_to?(method, include_all=false)
91
102
  @properties.has_key?(method)
92
103
  end
93
104
 
@@ -115,55 +126,6 @@ module Capistrano
115
126
 
116
127
  end
117
128
 
118
- class Selector
119
- def initialize(options)
120
- @options = options
121
- end
122
-
123
- def self.for(options)
124
- if options.has_key?(:exclude)
125
- Exclusive
126
- else
127
- self
128
- end.new(options)
129
- end
130
-
131
- def callable
132
- if key.respond_to?(:call)
133
- key
134
- else
135
- ->(server) { server.fetch(key) }
136
- end
137
- end
138
-
139
- def call(server)
140
- callable.call(server)
141
- end
142
-
143
- private
144
- attr_reader :options
145
-
146
- def key
147
- options[:filter] || options[:select] || all
148
- end
149
-
150
- def all
151
- ->(server) { :all }
152
- end
153
-
154
- class Exclusive < Selector
155
-
156
- def key
157
- options[:exclude]
158
- end
159
-
160
- def call(server)
161
- !callable.call(server)
162
- end
163
- end
164
-
165
- end
166
-
167
129
  end
168
130
  end
169
131
  end
@@ -8,7 +8,14 @@ module Capistrano
8
8
  include Enumerable
9
9
 
10
10
  def add_host(host, properties={})
11
- servers.add server(host).with(properties)
11
+ new_host = Server[host]
12
+ if server = servers.find { |s| s.matches? new_host }
13
+ server.user = new_host.user if new_host.user
14
+ server.port = new_host.port if new_host.port
15
+ server.with(properties)
16
+ else
17
+ servers << new_host.with(properties)
18
+ end
12
19
  end
13
20
 
14
21
  def add_role(role, hosts, options={})
@@ -22,6 +29,23 @@ module Capistrano
22
29
  s.select { |server| server.select?(options) }
23
30
  end
24
31
 
32
+ def role_properties_for(rolenames)
33
+ roles = rolenames.to_set
34
+ rps = Set.new unless block_given?
35
+ roles_for(rolenames).each do |host|
36
+ host.roles.intersection(roles).each do |role|
37
+ [host.properties.fetch(role)].flatten(1).each do |props|
38
+ if block_given?
39
+ yield host, role, props
40
+ else
41
+ rps << (props || {}).merge( role: role, hostname: host.hostname )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ block_given? ? nil: rps
47
+ end
48
+
25
49
  def fetch_primary(role)
26
50
  hosts = roles_for([role])
27
51
  hosts.find(&:primary) || hosts.first
@@ -33,13 +57,8 @@ module Capistrano
33
57
 
34
58
  private
35
59
 
36
- def server(host)
37
- new_host = Server[host]
38
- servers.find { |server| server.matches? new_host } || new_host
39
- end
40
-
41
60
  def servers
42
- @servers ||= Set.new
61
+ @servers ||= []
43
62
  end
44
63
 
45
64
  def extract_options(array)
@@ -1,4 +1,3 @@
1
1
  require 'capistrano/framework'
2
- require 'capistrano-stats'
3
2
 
4
3
  load File.expand_path("../tasks/deploy.rake", __FILE__)
@@ -51,8 +51,8 @@ module Capistrano
51
51
  end
52
52
 
53
53
  def on(hosts, options={}, &block)
54
- subset = Configuration.env.filter hosts
55
- SSHKit::Coordinator.new(subset).each(options, &block)
54
+ subset_copy = Marshal.dump(Configuration.env.filter(hosts))
55
+ SSHKit::Coordinator.new(Marshal.load(subset_copy)).each(options, &block)
56
56
  end
57
57
 
58
58
  def run_locally(&block)
@@ -47,8 +47,16 @@ module Capistrano
47
47
  env.roles_for(names.flatten)
48
48
  end
49
49
 
50
+ def role_properties(*names, &block)
51
+ env.role_properties_for(names, &block)
52
+ end
53
+
50
54
  def release_roles(*names)
51
- names << { exclude: :no_release } unless names.last.is_a? Hash
55
+ if names.last.is_a? Hash
56
+ names.last.merge!({ :exclude => :no_release })
57
+ else
58
+ names << { exclude: :no_release }
59
+ end
52
60
  roles(*names)
53
61
  end
54
62
 
@@ -54,7 +54,7 @@ module Capistrano
54
54
  end
55
55
 
56
56
  def repo_path
57
- deploy_path.join('repo')
57
+ Pathname.new(fetch(:repo_path, ->(){deploy_path.join('repo')}))
58
58
  end
59
59
 
60
60
  def shared_path
@@ -40,7 +40,7 @@ class Capistrano::Git < Capistrano::SCM
40
40
  end
41
41
 
42
42
  def fetch_revision
43
- context.capture(:git, "rev-parse --short #{fetch(:branch)}")
43
+ context.capture(:git, "rev-list --max-count=1 --abbrev-commit #{fetch(:branch)}")
44
44
  end
45
45
  end
46
46
  end
@@ -1,7 +1,6 @@
1
1
  namespace :deploy do
2
2
 
3
3
  task :starting do
4
- invoke 'metrics:collect'
5
4
  invoke 'deploy:check'
6
5
  invoke 'deploy:set_previous_revision'
7
6
  end
@@ -23,5 +23,5 @@ require 'capistrano/deploy'
23
23
  # require 'capistrano/rails/migrations'
24
24
  # require 'capistrano/passenger'
25
25
 
26
- # Load custom tasks from `lib/capistrano/tasks' if you have any defined
26
+ # Load custom tasks from `lib/capistrano/tasks` if you have any defined
27
27
  Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
@@ -5,7 +5,7 @@ set :application, 'my_app_name'
5
5
  set :repo_url, 'git@example.com:me/my_repo.git'
6
6
 
7
7
  # Default branch is :master
8
- # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
8
+ # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
9
9
 
10
10
  # Default deploy_to directory is /var/www/my_app_name
11
11
  # set :deploy_to, '/var/www/my_app_name'
@@ -23,10 +23,10 @@ set :repo_url, 'git@example.com:me/my_repo.git'
23
23
  # set :pty, true
24
24
 
25
25
  # Default value for :linked_files is []
26
- # set :linked_files, fetch(:linked_files, []).push('config/database.yml')
26
+ # set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')
27
27
 
28
28
  # Default value for linked_dirs is []
29
- # set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
29
+ # set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
30
30
 
31
31
  # Default value for default_env is {}
32
32
  # set :default_env, { path: "/opt/ruby/bin:$PATH" }
@@ -1,27 +1,43 @@
1
- # Simple Role Syntax
1
+ # server-based syntax
2
+ # ======================
3
+ # Defines a single server with a list of roles and multiple properties.
4
+ # You can define all roles on a single server, or split them:
5
+
6
+ # server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
7
+ # server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
8
+ # server 'db.example.com', user: 'deploy', roles: %w{db}
9
+
10
+
11
+
12
+ # role-based syntax
2
13
  # ==================
3
- # Supports bulk-adding hosts to roles, the primary server in each group
4
- # is considered to be the first unless any hosts have the primary
5
- # property set. Don't declare `role :all`, it's a meta role.
6
14
 
7
- role :app, %w{deploy@example.com}
8
- role :web, %w{deploy@example.com}
9
- role :db, %w{deploy@example.com}
15
+ # Defines a role with one or multiple servers. The primary server in each
16
+ # group is considered to be the first unless any hosts have the primary
17
+ # property set. Specify the username and a domain or IP for the server.
18
+ # Don't use `:all`, it's a meta role.
10
19
 
20
+ # role :app, %w{deploy@example.com}, my_property: :my_value
21
+ # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
22
+ # role :db, %w{deploy@example.com}
11
23
 
12
- # Extended Server Syntax
13
- # ======================
14
- # This can be used to drop a more detailed server definition into the
15
- # server list. The second argument is a, or duck-types, Hash and is
16
- # used to set extended properties on the server.
17
24
 
18
- server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value
25
+
26
+ # Configuration
27
+ # =============
28
+ # You can set any configuration variable like in config/deploy.rb
29
+ # These variables are then only loaded and set in this stage.
30
+ # For available Capistrano configuration variables see the documentation page.
31
+ # http://capistranorb.com/documentation/getting-started/configuration/
32
+ # Feel free to add new variables to customise your setup.
33
+
19
34
 
20
35
 
21
36
  # Custom SSH Options
22
37
  # ==================
23
38
  # You may pass any option but keep in mind that net/ssh understands a
24
- # limited set of options, consult[net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start).
39
+ # limited set of options, consult the Net::SSH documentation.
40
+ # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
25
41
  #
26
42
  # Global options
27
43
  # --------------
@@ -31,7 +47,7 @@ server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value
31
47
  # auth_methods: %w(password)
32
48
  # }
33
49
  #
34
- # And/or per server (overrides global)
50
+ # The server-based syntax can be used to override options:
35
51
  # ------------------------------------
36
52
  # server 'example.com',
37
53
  # user: 'user_name',
@@ -1,3 +1,3 @@
1
1
  module Capistrano
2
- VERSION = "3.3.5"
2
+ VERSION = "3.4.0"
3
3
  end
@@ -15,7 +15,7 @@ describe Capistrano::DSL do
15
15
  dsl.server 'example2.com', roles: %w{web}
16
16
  dsl.server 'example3.com', roles: %w{app web}, active: true
17
17
  dsl.server 'example4.com', roles: %w{app}, primary: true
18
- dsl.server 'example5.com', roles: %w{db}, no_release: true
18
+ dsl.server 'example5.com', roles: %w{db}, no_release: true, active:true
19
19
  end
20
20
 
21
21
  describe 'fetching all servers' do
@@ -241,23 +241,43 @@ describe Capistrano::DSL do
241
241
  end
242
242
 
243
243
  describe 'fetching all servers' do
244
- subject { dsl.roles(:all).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" } }
245
-
246
- it 'creates a server instance for each unique user@host:port combination' do
247
- expect(subject).to eq %w{db@example1.com:1234 root@example1.com:1234 @example1.com:5678 deployer@example1.com:1234}
244
+ it 'creates one server per hostname, ignoring user and port combinations' do
245
+ expect(dsl.roles(:all).size).to eq(1)
248
246
  end
249
247
  end
250
248
 
251
249
  describe 'fetching servers for a role' do
252
250
  it 'roles defined using the `server` syntax are included' do
253
- expect(dsl.roles(:web).size).to eq(2)
251
+ as = dsl.roles(:web).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }
252
+ expect(as.size).to eq(1)
253
+ expect(as[0]).to eq("deployer@example1.com:5678")
254
254
  end
255
255
 
256
256
  it 'roles defined using the `role` syntax are included' do
257
- expect(dsl.roles(:app).size).to eq(2)
257
+ as = dsl.roles(:app).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }
258
+ expect(as.size).to eq(1)
259
+ expect(as[0]).to eq("deployer@example1.com:5678")
260
+ end
261
+ end
262
+
263
+ end
264
+
265
+ describe 'when setting user and port' do
266
+ subject { dsl.roles(:all).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }.first }
267
+
268
+ describe "using the :user property" do
269
+ it "takes precedence over in the host string" do
270
+ dsl.server 'db@example1.com:1234', roles: %w{db}, active: true, user: 'brian'
271
+ expect(subject).to eq("brian@example1.com:1234")
258
272
  end
259
273
  end
260
274
 
275
+ describe "using the :port property" do
276
+ it "takes precedence over in the host string" do
277
+ dsl.server 'db@example1.com:9090', roles: %w{db}, active: true, port: 1234
278
+ expect(subject).to eq("db@example1.com:1234")
279
+ end
280
+ end
261
281
  end
262
282
 
263
283
  end
@@ -415,112 +435,6 @@ describe Capistrano::DSL do
415
435
 
416
436
  end
417
437
 
418
- describe 'release path' do
419
-
420
- before do
421
- dsl.set(:deploy_to, '/var/www')
422
- end
423
-
424
- describe 'fetching release path' do
425
- subject { dsl.release_path }
426
-
427
- context 'where no release path has been set' do
428
- before do
429
- dsl.delete(:release_path)
430
- end
431
-
432
- it 'returns the `current_path` value' do
433
- expect(subject.to_s).to eq '/var/www/current'
434
- end
435
- end
436
-
437
- context 'where the release path has been set' do
438
- before do
439
- dsl.set(:release_path, '/var/www/release_path')
440
- end
441
-
442
- it 'returns the set `release_path` value' do
443
- expect(subject.to_s).to eq '/var/www/release_path'
444
- end
445
- end
446
- end
447
-
448
- describe 'setting release path' do
449
- let(:now) { Time.parse("Oct 21 16:29:00 2015") }
450
- subject { dsl.release_path }
451
-
452
- context 'without a timestamp' do
453
- before do
454
- dsl.env.expects(:timestamp).returns(now)
455
- dsl.set_release_path
456
- end
457
-
458
- it 'returns the release path with the current env timestamp' do
459
- expect(subject.to_s).to eq '/var/www/releases/20151021162900'
460
- end
461
- end
462
-
463
- context 'with a timestamp' do
464
- before do
465
- dsl.set_release_path('timestamp')
466
- end
467
-
468
- it 'returns the release path with the timestamp' do
469
- expect(subject.to_s).to eq '/var/www/releases/timestamp'
470
- end
471
- end
472
- end
473
-
474
- describe 'setting deploy configuration path' do
475
- subject { dsl.deploy_config_path.to_s }
476
-
477
- context 'where no config path is set' do
478
- before do
479
- dsl.delete(:deploy_config_path)
480
- end
481
-
482
- it 'returns "config/deploy.rb"' do
483
- expect(subject).to eq 'config/deploy.rb'
484
- end
485
- end
486
-
487
- context 'where a custom path is set' do
488
- before do
489
- dsl.set(:deploy_config_path, 'my/custom/path.rb')
490
- end
491
-
492
- it 'returns the custom path' do
493
- expect(subject).to eq 'my/custom/path.rb'
494
- end
495
- end
496
- end
497
-
498
- describe 'setting stage configuration path' do
499
- subject { dsl.stage_config_path.to_s }
500
-
501
- context 'where no config path is set' do
502
-
503
- before do
504
- dsl.delete(:stage_config_path)
505
- end
506
-
507
- it 'returns "config/deploy"' do
508
- expect(subject).to eq 'config/deploy'
509
- end
510
- end
511
-
512
- context 'where a custom path is set' do
513
- before do
514
- dsl.set(:stage_config_path, 'my/custom/path')
515
- end
516
-
517
- it 'returns the custom path' do
518
- expect(subject).to eq 'my/custom/path'
519
- end
520
- end
521
- end
522
- end
523
-
524
438
  describe 'local_user' do
525
439
  before do
526
440
  dsl.set :local_user, -> { Etc.getlogin }
@@ -607,4 +521,63 @@ describe Capistrano::DSL do
607
521
 
608
522
  end
609
523
 
524
+ describe 'role_properties()' do
525
+
526
+ before do
527
+ dsl.role :redis, %w[example1.com example2.com], redis: { port: 6379, type: :slave }
528
+ dsl.server 'example1.com', roles: %w{web}, active: true, web: { port: 80 }
529
+ dsl.server 'example2.com', roles: %w{web redis}, web: { port: 81 }, redis: { type: :master }
530
+ dsl.server 'example3.com', roles: %w{app}, primary: true
531
+ end
532
+
533
+ it 'retrieves properties for a single role as a set' do
534
+ rps = dsl.role_properties(:app)
535
+ expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app}])
536
+ end
537
+
538
+ it 'retrieves properties for multiple roles as a set' do
539
+ rps = dsl.role_properties(:app, :web)
540
+ expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app},{ hostname: 'example1.com', role: :web, port: 80},{ hostname: 'example2.com', role: :web, port: 81}])
541
+ end
542
+
543
+ it 'yields the properties for a single role' do
544
+ recipient = mock('recipient')
545
+ recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
546
+ recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
547
+ dsl.role_properties(:redis) do |host, role, props|
548
+ recipient.doit(host, role, props)
549
+ end
550
+ end
551
+
552
+ it 'yields the properties for multiple roles' do
553
+ recipient = mock('recipient')
554
+ recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
555
+ recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
556
+ recipient.expects(:doit).with('example3.com', :app, nil)
557
+ dsl.role_properties(:redis, :app) do |host, role, props|
558
+ recipient.doit(host, role, props)
559
+ end
560
+ end
561
+
562
+ it 'yields the merged properties for multiple roles' do
563
+ recipient = mock('recipient')
564
+ recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
565
+ recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
566
+ recipient.expects(:doit).with('example1.com', :web, { port: 80 })
567
+ recipient.expects(:doit).with('example2.com', :web, { port: 81 })
568
+ dsl.role_properties(:redis, :web) do |host, role, props|
569
+ recipient.doit(host, role, props)
570
+ end
571
+ end
572
+
573
+ it 'honours a property filter before yielding' do
574
+ recipient = mock('recipient')
575
+ recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
576
+ recipient.expects(:doit).with('example1.com', :web, { port: 80 })
577
+ dsl.role_properties(:redis, :web, select: :active) do |host, role, props|
578
+ recipient.doit(host, role, props)
579
+ end
580
+ end
581
+ end
582
+
610
583
  end