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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/README.md +108 -4
- data/lib/capistrano/application.rb +16 -1
- data/lib/capistrano/configuration.rb +12 -17
- data/lib/capistrano/configuration/server.rb +68 -3
- data/lib/capistrano/configuration/servers.rb +71 -28
- data/lib/capistrano/dsl.rb +2 -0
- data/lib/capistrano/dsl/env.rb +7 -4
- data/lib/capistrano/i18n.rb +2 -1
- data/lib/capistrano/tasks/deploy.rake +3 -6
- data/lib/capistrano/templates/Capfile +4 -0
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/deploy_finalize_spec.rb +34 -0
- data/spec/integration/deploy_finished_spec.rb +36 -0
- data/spec/integration/deploy_started_spec.rb +74 -0
- data/spec/integration/deploy_update_spec.rb +45 -0
- data/spec/integration/dsl_spec.rb +254 -0
- data/spec/integration/installation_spec.rb +76 -0
- data/spec/integration_spec_helper.rb +7 -0
- data/spec/lib/capistrano/application_spec.rb +61 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +91 -0
- data/spec/lib/capistrano/configuration/servers_spec.rb +79 -11
- data/spec/lib/capistrano/configuration_spec.rb +12 -2
- data/spec/lib/capistrano/dsl/env_spec.rb +0 -73
- data/spec/spec_helper.rb +2 -0
- data/spec/support/matchers.rb +5 -0
- data/spec/support/test_app.rb +89 -0
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec0cef7495327f81efcf4e79622e2e27af857334
|
4
|
+
data.tar.gz: 4657cf63f889a01ea11a16f4fb28208297d2ece3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d136e348c49b419e709851111825f264a6d8d94ba8038b8ca13d1fd21ffbe8308acdc49a83db1700700bc1f6f0beb64fad9a3c248bb2c17980a803e5a3dfe619
|
7
|
+
data.tar.gz: 81a2f3f73e250668a807a8f2abfd248dba05247e4c5cab8b73fd971a5691e842193800f7a58f63c43e661d76d74e0a039c19524fcf22a4f6992293d895667cf2
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Capistrano [](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
|
-
|
84
|
-
|
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
|
41
|
-
servers.
|
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
|
-
|
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
|
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
|
19
|
-
|
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
|
-
|
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
|
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
|
30
|
-
|
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
|
-
|
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
|
44
|
-
|
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(
|
48
|
-
servers.find_all { |server| server.has_role?
|
39
|
+
def fetch(role)
|
40
|
+
servers.find_all { |server| server.has_role? role}
|
49
41
|
end
|
50
42
|
|
51
|
-
def
|
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
|