capistrano 3.3.5 → 3.4.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.
@@ -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