capistrano 3.0.0.pre2 → 3.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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