capistrano 3.0.0.pre2 → 3.0.0.pre3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38d0a3ff8b874c2e4d430c269c18f76d93964753
4
- data.tar.gz: 52b27720df0d2b9ae3908dcdaef8e1f5eca195af
3
+ metadata.gz: ec0cef7495327f81efcf4e79622e2e27af857334
4
+ data.tar.gz: 4657cf63f889a01ea11a16f4fb28208297d2ece3
5
5
  SHA512:
6
- metadata.gz: 0dd61efc447d0b8169d354a126bbfb81845d739426e8c3d60752f3038f4e2c2b0f4fc9b94b87a7df41511ea49859193e04a525ef7fcd2ceb5b8d2bf6c7318b96
7
- data.tar.gz: cf0d253ec7463e3e6e3f106d2f7ceaf30996b61156a59e42852de237f2556b786cc00b1781f016d734b77a995a848677a85023f04b1e687ebd5b6e3fbf8adc55
6
+ metadata.gz: d136e348c49b419e709851111825f264a6d8d94ba8038b8ca13d1fd21ffbe8308acdc49a83db1700700bc1f6f0beb64fad9a3c248bb2c17980a803e5a3dfe619
7
+ data.tar.gz: 81a2f3f73e250668a807a8f2abfd248dba05247e4c5cab8b73fd971a5691e842193800f7a58f63c43e661d76d74e0a039c19524fcf22a4f6992293d895667cf2
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  .rspec-local
19
19
  .ruby-version
20
+ _site
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 2.0.0
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - rbx-19mode
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Capistrano [![Build Status](https://travis-ci.org/capistrano/capistrano.png?branch=v3)](https://travis-ci.org/capistrano/capistrano)
2
2
 
3
+ ## Requirements
4
+
5
+ * Ruby >= 1.9 (JRuby and C-Ruby/MRI are supported)
6
+
3
7
  ## Installation
4
8
 
5
9
  Add this line to your application's Gemfile:
@@ -55,7 +59,16 @@ $ cap production deploy --trace
55
59
 
56
60
  ## Tasks
57
61
 
58
-
62
+ ``` ruby
63
+ server 'example.com', roles: [:web, :app]
64
+ server 'example.org', roles: [:db, :workers]
65
+ desc "Report Uptimes"
66
+ task :uptime do
67
+ on roles(:all) do |host|
68
+ info "Host #{host} (#{host.roles.join(', ')}):\t#{capture(:uptime)}"
69
+ end
70
+ end
71
+ ```
59
72
 
60
73
  ## Before / After
61
74
 
@@ -78,17 +91,108 @@ after :finishing, :notify do
78
91
  end
79
92
  ```
80
93
 
94
+ If it makes sense for your use-case (often, that means *generating a file*)
95
+ the Rake prerequisite mechanism can be used:
96
+
97
+ ``` ruby
98
+ desc "Create Important File"
99
+ file 'important.txt' do |t|
100
+ sh "touch #{t.name}"
101
+ end
102
+ desc "Upload Important File"
103
+ task :upload => 'important.txt' do |t|
104
+ on roles(:all) do
105
+ upload!(t.prerequisites.first, '/tmp')
106
+ end
107
+ end
108
+ ```
109
+
110
+ The final way to call out to other tasks is to simply `invoke()` them:
111
+
112
+ ``` ruby
113
+ task :one do
114
+ on roles(:all) { info "One" }
115
+ end
116
+ task :two do
117
+ invoke :one
118
+ on roles(:all) { info "Two" }
119
+ end
120
+ ```
121
+
122
+ This method is widely used.
123
+
124
+ ## Getting User Input
125
+
126
+ ``` ruby
127
+ desc "Ask about breakfast"
128
+ task :breakfast do
129
+ breakfast = ask(:breakfast, "What would you like your colleagues to you for breakfast?")
130
+ on roles(:all) do |h|
131
+ execute "echo \"$(whoami) wants #{breakfast} for breakfast!\" | wall"
132
+ end
133
+ end
134
+ ```
135
+
136
+ Perfect, who needs telephones.
137
+
81
138
  ## Console
82
139
 
83
- Execute arbitrary remote commands, to use this simply add
84
- `require 'capistrano/console'` which will add the necessary tasks to your
140
+ **Note:** Here be dragons. The console is very immature, but it's much more
141
+ cleanly architected than previous incarnations and it'll only get better from
142
+ here on in.
143
+
144
+ Execute arbitrary remote commands, to use this simply add
145
+ `require 'capistrano/console'` which will add the necessary tasks to your
85
146
  environment:
86
147
 
87
148
  ``` shell
88
149
  $ cap staging console
89
150
  ```
90
151
 
152
+ Then, after setting up the server connections, this is how that might look:
153
+
154
+ ```
155
+ $ cap production console
156
+ capistrano console - enter command to execute on production
157
+ production> uptime
158
+ INFO [94db8027] Running /usr/bin/env uptime on leehambley@example.com:22
159
+ DEBUG [94db8027] Command: /usr/bin/env uptime
160
+ DEBUG [94db8027] 17:11:17 up 50 days, 22:31, 1 user, load average: 0.02, 0.02, 0.05
161
+ INFO [94db8027] Finished in 0.435 seconds command successful.
162
+ production> who
163
+ INFO [9ce34809] Running /usr/bin/env who on leehambley@example.com:22
164
+ DEBUG [9ce34809] Command: /usr/bin/env who
165
+ DEBUG [9ce34809] leehambley pts/0 2013-06-13 17:11 (port-11262.pppoe.wtnet.de)
166
+ INFO [9ce34809] Finished in 0.420 seconds command successful.
167
+ ```
168
+
169
+ ## A word about PTYs
170
+
171
+ There is a configuration option which asks the backend driver to as the remote host
172
+ to assign the connection a *pty*. A *pty* is a pseudo-terminal, which in effect means
173
+ *tell the backend that this is an **interactive** session*. This is normally a bad idea.
174
+
175
+ Most of the differences are best explained by [this page](https://github.com/sstephenson/rbenv/wiki/Unix-shell-initialization) from the author of *rbenv*.
176
+
177
+ **When Capistrano makes a connection it is a *non-login*, *non-interactive* shell.
178
+ This was not an accident!**
179
+
180
+ It's often used as a band aid to cure issues related to RVM and rbenv not loading login
181
+ and shell initialisation scripts. In these scenarios RVM and rbenv are the tools at fault,
182
+ or at least they are being used incorrectly.
183
+
184
+ Whilst, especially in the case of language runtimes (Ruby, Node, Python and friends in
185
+ particular) there is a temptation to run multiple versions in parallel on a single server
186
+ and to switch between them using environmental variables, this is an anti-pattern, and
187
+ sympotamtic of bad design (i.e. you're testing a second version of Ruby in production because
188
+ your company lacks the infrastructure to test this in a staging environment)
189
+
91
190
  ## Configuration
92
191
 
93
192
 
94
- ## SSHKit
193
+ ## SSHKit
194
+
195
+ [SSHKit][https://github.com/capistrano/sshkit] is the driver for SSH
196
+ connections behind the scenes in Capistrano, depending how deep you dig, you
197
+ might run into interfaces that come directly from SSHKit (the configuration is
198
+ a good example).
@@ -1,9 +1,9 @@
1
1
  module Capistrano
2
2
  class Application < Rake::Application
3
- include Rake::DSL
4
3
 
5
4
  def initialize
6
5
  super
6
+ @name = "cap"
7
7
  @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb} << capfile
8
8
  end
9
9
 
@@ -12,6 +12,11 @@ module Capistrano
12
12
  super
13
13
  end
14
14
 
15
+ def sort_options(options)
16
+ options.push(version)
17
+ super
18
+ end
19
+
15
20
  def load_rakefile
16
21
  @name = 'cap'
17
22
  super
@@ -23,6 +28,16 @@ module Capistrano
23
28
  def capfile
24
29
  File.expand_path(File.join(File.dirname(__FILE__),'..','Capfile'))
25
30
  end
31
+
32
+ def version
33
+ ['--version', '-V',
34
+ "Display the program version.",
35
+ lambda { |value|
36
+ puts "Capistrano Version: #{Capistrano::VERSION} (Rake Version: #{RAKEVERSION})"
37
+ exit
38
+ }
39
+ ]
40
+ end
26
41
  end
27
42
 
28
43
  end
@@ -29,35 +29,30 @@ module Capistrano
29
29
  end
30
30
  end
31
31
 
32
- def role(name, hosts)
33
- servers.add_role(name, hosts)
32
+ def role(name, hosts, options={})
33
+ servers.add_role(name, hosts, options)
34
34
  end
35
35
 
36
- def server(name, properties = {})
36
+ def server(name, properties={})
37
37
  servers.add_host(name, properties)
38
38
  end
39
39
 
40
- def roles_for(names, options = {})
41
- servers.fetch_roles(names).tap do |list|
42
- if filter = options.delete(:filter) || options.delete(:select)
43
- if filter.respond_to?(:call)
44
- list.select!(&filter)
45
- else
46
- list.select! { |s| s.properties.send(filter) }
47
- end
48
- if list.empty?
49
- raise "Your filter #{filter} would remove all matching servers!"
50
- end
51
- end
52
- end
40
+ def roles_for(names)
41
+ servers.roles_for(names)
53
42
  end
54
43
 
55
44
  def primary(role)
56
45
  servers.fetch_primary(role)
57
46
  end
58
47
 
48
+ def backend
49
+ @backend ||= SSHKit
50
+ end
51
+
52
+ attr_writer :backend
53
+
59
54
  def configure_backend
60
- SSHKit.configure do |sshkit|
55
+ backend.configure do |sshkit|
61
56
  sshkit.format = fetch(:format)
62
57
  sshkit.output_verbosity = fetch(:log_level)
63
58
  sshkit.default_env = fetch(:default_env)
@@ -2,9 +2,16 @@ require 'set'
2
2
  module Capistrano
3
3
  class Configuration
4
4
  class Server < SSHKit::Host
5
+ extend Forwardable
6
+ def_delegators :properties, :roles, :fetch, :set
7
+
8
+ def add_roles(roles)
9
+ Array(roles).each { |role| add_role(role) }
10
+ end
11
+ alias roles= add_roles
5
12
 
6
13
  def add_role(role)
7
- roles << role.to_sym
14
+ roles.add role.to_sym
8
15
  end
9
16
 
10
17
  def has_role?(role)
@@ -15,8 +22,66 @@ module Capistrano
15
22
  hostname == Server.new(host).hostname
16
23
  end
17
24
 
18
- def roles
19
- properties.roles ||= Set.new
25
+ def primary
26
+ self if fetch(:primary)
27
+ end
28
+
29
+ def with(properties)
30
+ properties.each { |key, value| add_property(key, value) }
31
+ self
32
+ end
33
+
34
+ def properties
35
+ @properties ||= Properties.new
36
+ end
37
+
38
+ class Properties
39
+
40
+ def initialize
41
+ @properties = {}
42
+ end
43
+
44
+ def set(key, value)
45
+ @properties[key] = value
46
+ end
47
+
48
+ def fetch(key)
49
+ @properties[key]
50
+ end
51
+
52
+ def respond_to?(method)
53
+ @properties.has_key?(method)
54
+ end
55
+
56
+ def roles
57
+ @roles ||= Set.new
58
+ end
59
+
60
+ def method_missing(key, value=nil)
61
+ if value
62
+ set(lvalue(key), value)
63
+ else
64
+ fetch(key)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def lvalue(key)
71
+ key.to_s.chomp('=').to_sym
72
+ end
73
+
74
+ end
75
+
76
+
77
+ private
78
+
79
+ def add_property(key, value)
80
+ if respond_to?("#{key}=")
81
+ send("#{key}=", value)
82
+ else
83
+ set(key, value)
84
+ end
20
85
  end
21
86
 
22
87
  end
@@ -4,34 +4,22 @@ module Capistrano
4
4
  class Servers
5
5
  include Enumerable
6
6
 
7
- def add_host(host, properties = {})
8
- find_or_create_server(host).tap do |host|
9
- Array(properties.delete(:roles) || properties.delete("roles")).each do |role|
10
- host.add_role(role)
11
- end
12
- properties.each do |key, value|
13
- unless host.properties.respond_to?(key)
14
- host.properties.send(:"#{key}=", value)
15
- end
16
- end
17
- servers.add host
18
- end
7
+ def add_host(host, properties={})
8
+ servers.add server(host).with(properties)
19
9
  end
20
10
 
21
- def add_role(role, hosts)
22
- Array(hosts).each do |host|
23
- server = find_or_create_server(host)
24
- server.add_role(role)
25
- servers.add server
26
- end
11
+ def add_role(role, hosts, options={})
12
+ Array(hosts).each { |host| add_host(host, options.merge(roles: role)) }
27
13
  end
28
14
 
29
- def fetch_roles(names)
30
- roles_for(names)
15
+ def roles_for(names)
16
+ options = extract_options(names)
17
+ fetch_roles(names, options)
31
18
  end
32
19
 
33
20
  def fetch_primary(role)
34
- fetch(role).select { |h| h.properties.primary }.first || fetch(role).first
21
+ hosts = fetch(role)
22
+ hosts.find(&:primary) || hosts.first
35
23
  end
36
24
 
37
25
  def each
@@ -40,25 +28,80 @@ module Capistrano
40
28
 
41
29
  private
42
30
 
43
- def find_or_create_server(host)
44
- servers.find { |server| server.matches?(host) } || Server.new(host)
31
+ def server(host)
32
+ if host.is_a? Server
33
+ host
34
+ else
35
+ servers.find { |server| server.matches?(host) } || Server.new(host)
36
+ end
45
37
  end
46
38
 
47
- def fetch(name)
48
- servers.find_all { |server| server.has_role? name }
39
+ def fetch(role)
40
+ servers.find_all { |server| server.has_role? role}
49
41
  end
50
42
 
51
- def roles_for(names)
43
+ def fetch_roles(names, options)
52
44
  if Array(names).map(&:to_sym).include?(:all)
53
- servers
45
+ filter(servers, options)
54
46
  else
55
- Array(names).flat_map { |name| fetch name }.uniq
47
+ role_servers = Array(names).flat_map { |name| fetch name }.uniq
48
+ filter(role_servers, options)
56
49
  end
57
50
  end
58
51
 
52
+ def filter(servers, options)
53
+ Filter.new(servers, options).filtered_servers
54
+ end
55
+
59
56
  def servers
60
57
  @servers ||= Set.new
61
58
  end
59
+
60
+ def extract_options(array)
61
+ array.last.is_a?(::Hash) ? array.pop : {}
62
+ end
63
+
64
+ class Filter
65
+ def initialize(servers, options)
66
+ @servers, @options = servers, options
67
+ end
68
+
69
+ def filtered_servers
70
+ if servers_with_filter.any?
71
+ servers_with_filter
72
+ else
73
+ fail I18n.t(:filter_removes_all_servers, scope: :capistrano, filter: key )
74
+ end
75
+ end
76
+
77
+ private
78
+ attr_reader :options, :servers
79
+
80
+ def servers_with_filter
81
+ @servers_with_filter ||= servers.select(&filter)
82
+ end
83
+
84
+ def key
85
+ options[:filter] || options[:select]
86
+ end
87
+
88
+ def filter_option
89
+ key || all
90
+ end
91
+
92
+ def filter
93
+ if filter_option.respond_to?(:call)
94
+ filter_option
95
+ else
96
+ lambda { |server| server.fetch(filter_option) }
97
+ end
98
+ end
99
+
100
+ def all
101
+ lambda { |server| :all }
102
+ end
103
+
104
+ end
62
105
  end
63
106
  end
64
107
  end