capistrano 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +47 -0
- data/lib/capistrano/cli/help.rb +3 -1
- data/lib/capistrano/configuration/connections.rb +34 -14
- data/lib/capistrano/configuration/namespaces.rb +2 -1
- data/lib/capistrano/configuration/roles.rb +16 -2
- data/lib/capistrano/recipes/deploy.rb +3 -3
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +10 -13
- data/lib/capistrano/recipes/deploy/scm/git.rb +68 -19
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +15 -8
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +11 -4
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +8 -2
- data/lib/capistrano/role.rb +112 -0
- data/lib/capistrano/ssh.rb +6 -0
- data/lib/capistrano/task_definition.rb +2 -1
- data/lib/capistrano/upload.rb +7 -1
- data/lib/capistrano/version.rb +1 -1
- data/test/configuration/connections_test.rb +44 -4
- data/test/configuration/namespace_dsl_test.rb +8 -0
- data/test/configuration/roles_test.rb +99 -3
- data/test/deploy/scm/git_test.rb +41 -12
- data/test/deploy/scm/mercurial_test.rb +123 -0
- metadata +76 -61
data/CHANGELOG
CHANGED
@@ -1,3 +1,50 @@
|
|
1
|
+
*2.2.0* February 27, 2008
|
2
|
+
|
3
|
+
* Fix git submodule support to init on sync [halorgium]
|
4
|
+
|
5
|
+
* Add alternative server-centric role definition method [James Duncan Davidson]
|
6
|
+
|
7
|
+
* Add support for password prompts from the Mercurial SCM [ches]
|
8
|
+
|
9
|
+
* Add support for :max_hosts option in task definition or run() [Rob Holland <rob@inversepath.com>]
|
10
|
+
|
11
|
+
* Distributed git support for better operability with remote_cache strategy [voidlock]
|
12
|
+
|
13
|
+
* Use a default line length in help text if line length is otherwise too small [Jamis Buck]
|
14
|
+
|
15
|
+
* Fix incorrect reference to the 'setup' task in task documentation [rajeshduggal]
|
16
|
+
|
17
|
+
* Don't try to kill the spawner process on deploy:stop if no spawner process exists [Jamis Buck]
|
18
|
+
|
19
|
+
* Dynamic roles (e.g. role(:app) { "host.name" }) [dmasover]
|
20
|
+
|
21
|
+
* Implement Bzr#next_revision so that pending changes can be reported correctly [casret]
|
22
|
+
|
23
|
+
* Use a proper export command for bzr SCM [drudru]
|
24
|
+
|
25
|
+
* Use checkout instead of merge for git SCM [nuttycom]
|
26
|
+
|
27
|
+
* Fix typo in Subversion SCM module, encountered when an update fails [kemiller]
|
28
|
+
|
29
|
+
* Fix documentation typo in upload.rb [evolving_jerk]
|
30
|
+
|
31
|
+
* Added test case to show that the :scm_command is honored by the git SCM module [grempe]
|
32
|
+
|
33
|
+
* Fail gracefully when double-colons are used to delimit namespaces [richie]
|
34
|
+
|
35
|
+
* Add support for :git_enable_submodules variable, to enable submodules with the git SCM [halorgium]
|
36
|
+
|
37
|
+
* If subversion asks for a password, prompt as a last resort [Jamis Buck]
|
38
|
+
|
39
|
+
* Use checkout --lightweight for bzr checkout, instead of branch [michiels]
|
40
|
+
|
41
|
+
* Make sure bzr SCM works when revision is head (or unspecified) [michiels]
|
42
|
+
|
43
|
+
* Support p4sync_flags and p4client_root variables for Perforce [gseidman]
|
44
|
+
|
45
|
+
* Prepare for Net::SSH v2 by making sure Capistrano only tries to load Net::SSH versions less than 1.99.0 [Jamis Buck]
|
46
|
+
|
47
|
+
|
1
48
|
*2.1.0* October 14, 2007
|
2
49
|
|
3
50
|
* Default to 0664 instead of 0660 on upload [Jamis Buck]
|
data/lib/capistrano/cli/help.rb
CHANGED
@@ -84,7 +84,9 @@ module Capistrano
|
|
84
84
|
text.each_line do |line|
|
85
85
|
indentation = line[/^\s+/] || ""
|
86
86
|
indentation_size = indentation.split(//).inject(0) { |c,s| c + (s[0] == ?\t ? 8 : 1) }
|
87
|
-
|
87
|
+
line_length = output_columns - indentation_size
|
88
|
+
line_length = MIN_MAX_LEN if line_length < MIN_MAX_LEN
|
89
|
+
lines = line.strip.gsub(/(.{1,#{line_length}})(?:\s+|\Z)/, "\\1\n").split(/\n/)
|
88
90
|
if lines.empty?
|
89
91
|
formatted << "\n"
|
90
92
|
else
|
@@ -1,3 +1,5 @@
|
|
1
|
+
|
2
|
+
require 'enumerator'
|
1
3
|
require 'capistrano/gateway'
|
2
4
|
require 'capistrano/ssh'
|
3
5
|
|
@@ -95,6 +97,14 @@ module Capistrano
|
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
100
|
+
# Destroys sessions for each server in the list.
|
101
|
+
def teardown_connections_to(servers)
|
102
|
+
servers.each do |server|
|
103
|
+
@sessions[server].close
|
104
|
+
@sessions.delete(server)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
98
108
|
# Determines the set of servers within the current task's scope and
|
99
109
|
# establishes connections to them, and then yields that list of
|
100
110
|
# servers.
|
@@ -120,22 +130,32 @@ module Capistrano
|
|
120
130
|
servers = [servers.first] if options[:once]
|
121
131
|
logger.trace "servers: #{servers.map { |s| s.host }.inspect}"
|
122
132
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
133
|
+
max_hosts = (options[:max_hosts] || (task && task.max_hosts) || servers.size).to_i
|
134
|
+
is_subset = max_hosts < servers.size
|
135
|
+
|
136
|
+
# establish connections to those servers in groups of max_hosts, as necessary
|
137
|
+
servers.each_slice(max_hosts) do |servers_slice|
|
138
|
+
begin
|
139
|
+
establish_connections_to(servers_slice)
|
140
|
+
rescue ConnectionError => error
|
141
|
+
raise error unless task && task.continue_on_error?
|
142
|
+
error.hosts.each do |h|
|
143
|
+
servers_slice.delete(h)
|
144
|
+
failed!(h)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
begin
|
149
|
+
yield servers_slice
|
150
|
+
rescue RemoteError => error
|
151
|
+
raise error unless task && task.continue_on_error?
|
152
|
+
error.hosts.each { |h| failed!(h) }
|
131
153
|
end
|
132
|
-
end
|
133
154
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
error.hosts.each { |h| failed!(h) }
|
155
|
+
# if dealing with a subset (e.g., :max_hosts is less than the
|
156
|
+
# number of servers available) teardown the subset of connections
|
157
|
+
# that were just made, so that we can make room for the next subset.
|
158
|
+
teardown_connections_to(servers_slice) if is_subset
|
139
159
|
end
|
140
160
|
end
|
141
161
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'capistrano/server_definition'
|
2
|
+
require 'capistrano/role'
|
2
3
|
|
3
4
|
module Capistrano
|
4
5
|
class Configuration
|
@@ -15,7 +16,7 @@ module Capistrano
|
|
15
16
|
|
16
17
|
def initialize_with_roles(*args) #:nodoc:
|
17
18
|
initialize_without_roles(*args)
|
18
|
-
@roles = Hash.new { |h,k| h[k] =
|
19
|
+
@roles = Hash.new { |h,k| h[k] = Role.new }
|
19
20
|
end
|
20
21
|
|
21
22
|
# Define a new role and its associated servers. You must specify at least
|
@@ -41,11 +42,24 @@ module Capistrano
|
|
41
42
|
# that call to "role":
|
42
43
|
#
|
43
44
|
# role :web, "web2", "web3", :user => "www", :port => 2345
|
44
|
-
def role(which, *args)
|
45
|
+
def role(which, *args, &block)
|
45
46
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
46
47
|
which = which.to_sym
|
48
|
+
roles[which].push(block, options) if block_given?
|
47
49
|
args.each { |host| roles[which] << ServerDefinition.new(host, options) }
|
48
50
|
end
|
51
|
+
|
52
|
+
# An alternative way to associate servers with roles. If you have a server
|
53
|
+
# that participates in multiple roles, this can be a DRYer way to describe
|
54
|
+
# the relationships. Pass the host definition as the first parameter, and
|
55
|
+
# the roles as the remaining parameters:
|
56
|
+
#
|
57
|
+
# server "master.example.com", :web, :app
|
58
|
+
def server(host, *roles)
|
59
|
+
options = roles.last.is_a?(Hash) ? roles.pop : {}
|
60
|
+
raise ArgumentError, "you must associate a server with at least one role" if roles.empty?
|
61
|
+
roles.each { |name| role(name, host, options) }
|
62
|
+
end
|
49
63
|
end
|
50
64
|
end
|
51
65
|
end
|
@@ -110,11 +110,11 @@ namespace :deploy do
|
|
110
110
|
desc <<-DESC
|
111
111
|
Prepares one or more servers for deployment. Before you can use any \
|
112
112
|
of the Capistrano deployment tasks with your project, you will need to \
|
113
|
-
make sure all of your servers have been prepared with `cap setup'. When \
|
113
|
+
make sure all of your servers have been prepared with `cap deploy:setup'. When \
|
114
114
|
you add a new server to your cluster, you can easily run the setup task \
|
115
115
|
on just that server by specifying the HOSTS environment variable:
|
116
116
|
|
117
|
-
$ cap HOSTS=new.server.com setup
|
117
|
+
$ cap HOSTS=new.server.com deploy:setup
|
118
118
|
|
119
119
|
It is safe to run this task on servers that have already been set up; it \
|
120
120
|
will not destroy any deployed revisions or data.
|
@@ -426,7 +426,7 @@ namespace :deploy do
|
|
426
426
|
as = fetch(:runner, "app")
|
427
427
|
via = fetch(:run_method, :sudo)
|
428
428
|
|
429
|
-
invoke_command "#{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid", :via => via, :as => as
|
429
|
+
invoke_command "if [ -f #{current_path}/tmp/pids/dispatch.spawner.pid ]; then #{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid; fi", :via => via, :as => as
|
430
430
|
invoke_command "#{current_path}/script/process/reaper -a kill", :via => via, :as => as
|
431
431
|
end
|
432
432
|
|
@@ -21,7 +21,7 @@ module Capistrano
|
|
21
21
|
# Returns the command that will check out the given revision to the
|
22
22
|
# given destination.
|
23
23
|
def checkout(revision, destination)
|
24
|
-
scm :
|
24
|
+
scm :checkout, "--lightweight", revswitch(revision), repository, destination
|
25
25
|
end
|
26
26
|
|
27
27
|
# The bzr 'update' command does not support updating to a specific
|
@@ -33,13 +33,9 @@ module Capistrano
|
|
33
33
|
commands.join(" && ")
|
34
34
|
end
|
35
35
|
|
36
|
-
# The bzr 'export'
|
37
|
-
# specify the repository itself, so it only works if there is a working
|
38
|
-
# tree handy, locally. Since this needs to work even on a remote host
|
39
|
-
# where there is no working tree, we'll just do a checkout, followed
|
40
|
-
# by a deletion of the ".bzr" metadata directory.
|
36
|
+
# The bzr 'export' does an export similar to other SCM systems
|
41
37
|
def export(revision, destination)
|
42
|
-
|
38
|
+
scm :export, revswitch(revision), destination, repository
|
43
39
|
end
|
44
40
|
|
45
41
|
# The bzr "diff" command doesn't accept a repository argument, so it
|
@@ -63,17 +59,18 @@ module Capistrano
|
|
63
59
|
# If the 'revision' argument, on the other hand, is not :head, it is
|
64
60
|
# simply returned.
|
65
61
|
def query_revision(revision)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
62
|
+
revision
|
63
|
+
end
|
64
|
+
|
65
|
+
# Increments the given revision number and returns it.
|
66
|
+
def next_revision(revision)
|
67
|
+
revision.to_i + 1
|
71
68
|
end
|
72
69
|
|
73
70
|
private
|
74
71
|
|
75
72
|
def revswitch(revision)
|
76
|
-
if revision == :head
|
73
|
+
if revision == :head || revision.nil?
|
77
74
|
nil
|
78
75
|
else
|
79
76
|
"-r #{revision}"
|
@@ -49,11 +49,28 @@ module Capistrano
|
|
49
49
|
# You may set <tt>:branch</tt>, which is the reference to the branch, tag,
|
50
50
|
# or any SHA1 you are deploying, for example:
|
51
51
|
#
|
52
|
-
# set :branch, "
|
52
|
+
# set :branch, "master"
|
53
53
|
#
|
54
54
|
# Otherwise, HEAD is assumed. I strongly suggest you set this. HEAD is
|
55
55
|
# not always the best assumption.
|
56
56
|
#
|
57
|
+
# You may also set <tt>:remote</tt>, which will be used as a name for remote
|
58
|
+
# tracking of repositories. This option is intended for use with the
|
59
|
+
# <tt>:remote_cache</tt> strategy in a distributed git environment.
|
60
|
+
#
|
61
|
+
# For example in the projects <tt>config/deploy.rb</tt>:
|
62
|
+
#
|
63
|
+
# set :repository, "#{scm_user}@somehost:~/projects/project.git"
|
64
|
+
# set :remote, "#{scm_user}"
|
65
|
+
#
|
66
|
+
# Then each person with deploy priveledges can add the following to their
|
67
|
+
# local <tt>~/.caprc</tt> file:
|
68
|
+
#
|
69
|
+
# set :scm_user, 'someuser'
|
70
|
+
#
|
71
|
+
# Now any time a person deploys the project, their repository will be
|
72
|
+
# setup as a remote git repository within the cached repository.
|
73
|
+
#
|
57
74
|
# The <tt>:scm_command</tt> configuration variable, if specified, will
|
58
75
|
# be used as the full path to the git executable on the *remote* machine:
|
59
76
|
#
|
@@ -78,7 +95,7 @@ module Capistrano
|
|
78
95
|
# set :deploy_via, :remote_cache
|
79
96
|
#
|
80
97
|
# For faster clone, you can also use shallow cloning. This will set the
|
81
|
-
# '--depth' flag using the depth specified. This *cannot* be used
|
98
|
+
# '--depth' flag using the depth specified. This *cannot* be used
|
82
99
|
# together with the :remote_cache strategy
|
83
100
|
#
|
84
101
|
# set :git_shallow_clone, 1
|
@@ -88,8 +105,9 @@ module Capistrano
|
|
88
105
|
#
|
89
106
|
# Garry Dolley http://scie.nti.st
|
90
107
|
# Contributions by Geoffrey Grosenbach http://topfunky.com
|
91
|
-
#
|
92
|
-
|
108
|
+
# Scott Chacon http://jointheconversation.org
|
109
|
+
# and Alex Arnell http://twologic.com
|
110
|
+
|
93
111
|
class Git < Base
|
94
112
|
# Sets the default command name for this SCM on your *local* machine.
|
95
113
|
# Users may override this by setting the :scm_command variable.
|
@@ -102,38 +120,69 @@ module Capistrano
|
|
102
120
|
configuration[:branch] || 'HEAD'
|
103
121
|
end
|
104
122
|
|
123
|
+
def origin
|
124
|
+
configuration[:remote] || 'origin'
|
125
|
+
end
|
126
|
+
|
105
127
|
# Performs a clone on the remote machine, then checkout on the branch
|
106
128
|
# you want to deploy.
|
107
129
|
def checkout(revision, destination)
|
108
|
-
git
|
109
|
-
|
110
|
-
branch = head
|
111
|
-
|
112
|
-
fail "No branch specified, use for example 'set :branch, \"origin/master\"' in your deploy.rb" unless branch
|
130
|
+
git = command
|
131
|
+
remote = origin
|
113
132
|
|
133
|
+
args = []
|
134
|
+
args << "-o #{remote}" unless remote == 'origin'
|
114
135
|
if depth = configuration[:git_shallow_clone]
|
115
|
-
|
136
|
+
args << "--depth #{depth}"
|
137
|
+
end
|
138
|
+
|
139
|
+
execute = []
|
140
|
+
if args.empty?
|
141
|
+
execute << "#{git} clone #{configuration[:repository]} #{destination}"
|
116
142
|
else
|
117
|
-
execute
|
143
|
+
execute << "#{git} clone #{args.join(' ')} #{configuration[:repository]} #{destination}"
|
118
144
|
end
|
119
145
|
|
120
|
-
|
146
|
+
# checkout into a local branch rather than a detached HEAD
|
147
|
+
execute << "cd #{destination} && #{git} checkout -b deploy #{revision}"
|
148
|
+
|
149
|
+
if configuration[:git_enable_submodules]
|
150
|
+
execute << "#{git} submodule init"
|
151
|
+
execute << "#{git} submodule update"
|
152
|
+
end
|
121
153
|
|
122
|
-
execute
|
154
|
+
execute.join(" && ")
|
123
155
|
end
|
124
156
|
|
125
157
|
# Merges the changes to 'head' since the last fetch, for remote_cache
|
126
158
|
# deployment strategy
|
127
159
|
def sync(revision, destination)
|
128
|
-
|
160
|
+
git = command
|
161
|
+
remote = origin
|
162
|
+
|
163
|
+
execute = []
|
164
|
+
execute << "cd #{destination}"
|
165
|
+
|
166
|
+
# Use git-config to setup a remote tracking branches. Could use
|
167
|
+
# git-remote but it complains when a remote of the same name already
|
168
|
+
# exists, git-config will just silenty overwrite the setting every
|
169
|
+
# time. This could cause wierd-ness in the remote cache if the url
|
170
|
+
# changes between calls, but as long as the repositories are all
|
171
|
+
# based from each other it should still work fine.
|
172
|
+
if remote != 'origin'
|
173
|
+
execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
|
174
|
+
execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
|
175
|
+
end
|
129
176
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
177
|
+
# since we're in a local branch already, just reset to specified revision rather than merge
|
178
|
+
execute << "#{git} fetch #{remote} && #{git} reset --hard #{revision}"
|
179
|
+
|
180
|
+
if configuration[:git_enable_submodules]
|
181
|
+
execute << "#{git} submodule init"
|
182
|
+
execute << "#{git} submodule update"
|
134
183
|
end
|
135
184
|
|
136
|
-
execute
|
185
|
+
execute.join(" && ")
|
137
186
|
end
|
138
187
|
|
139
188
|
# Returns a string of diffs between two revisions
|
@@ -70,24 +70,25 @@ module Capistrano
|
|
70
70
|
logger.info "[#{stream}] #{text}"
|
71
71
|
case text
|
72
72
|
when /^user:/mi
|
73
|
-
|
74
|
-
|
73
|
+
# support :scm_user for backwards compatibility of this module
|
74
|
+
if user = variable(:scm_username) || variable(:scm_user)
|
75
|
+
"#{user}\n"
|
75
76
|
else
|
76
|
-
raise "No variable :
|
77
|
+
raise "No variable :scm_username specified and Mercurial asked!\n" +
|
77
78
|
"Prompt was: #{text}"
|
78
79
|
end
|
79
|
-
when
|
80
|
-
|
81
|
-
|
82
|
-
else
|
80
|
+
when /\bpassword:/mi
|
81
|
+
unless pass = scm_password_or_prompt
|
82
|
+
# fall back on old behavior of erroring out with msg
|
83
83
|
raise "No variable :scm_password specified and Mercurial asked!\n" +
|
84
84
|
"Prompt was: #{text}"
|
85
85
|
end
|
86
|
+
"#{pass}\n"
|
86
87
|
when /yes\/no/i
|
87
88
|
"yes\n"
|
88
89
|
end
|
89
90
|
end
|
90
|
-
|
91
|
+
|
91
92
|
private
|
92
93
|
|
93
94
|
# Fine grained mercurial commands
|
@@ -122,6 +123,12 @@ module Capistrano
|
|
122
123
|
else "--verbose"
|
123
124
|
end
|
124
125
|
end
|
126
|
+
|
127
|
+
# honor Cap 2.1+'s :scm_prefer_prompt if present
|
128
|
+
def scm_password_or_prompt
|
129
|
+
@scm_password_or_prompt ||= variable(:scm_password) ||
|
130
|
+
(Capistrano::CLI.password_prompt("hg password: ") if variable(:scm_prefer_prompt))
|
131
|
+
end
|
125
132
|
|
126
133
|
end
|
127
134
|
end
|
@@ -25,21 +25,21 @@ module Capistrano
|
|
25
25
|
# destination directory. The perforce client has a fixed destination so
|
26
26
|
# the files must be copied from there to their intended resting place.
|
27
27
|
def checkout(revision, destination)
|
28
|
-
p4_sync(revision, destination,
|
28
|
+
p4_sync(revision, destination, p4sync_flags)
|
29
29
|
end
|
30
30
|
|
31
31
|
# Returns the command that will sync the given revision to the given
|
32
32
|
# destination directory. The perforce client has a fixed destination so
|
33
33
|
# the files must be copied from there to their intended resting place.
|
34
34
|
def sync(revision, destination)
|
35
|
-
p4_sync(revision, destination,
|
35
|
+
p4_sync(revision, destination, p4sync_flags)
|
36
36
|
end
|
37
37
|
|
38
38
|
# Returns the command that will sync the given revision to the given
|
39
39
|
# destination directory. The perforce client has a fixed destination so
|
40
40
|
# the files must be copied from there to their intended resting place.
|
41
41
|
def export(revision, destination)
|
42
|
-
p4_sync(revision, destination,
|
42
|
+
p4_sync(revision, destination, p4sync_flags)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Returns the command that will do an "p4 diff2" for the two revisions.
|
@@ -89,7 +89,6 @@ module Capistrano
|
|
89
89
|
# a fixed destination so the files must be copied from there to their
|
90
90
|
# intended resting place.
|
91
91
|
def p4_sync(revision, destination, options="")
|
92
|
-
p4client_root = "`#{command} #{authentication} client -o | grep ^Root | cut -f2`"
|
93
92
|
scm authentication, :sync, options, "#{rev_no(revision)}", "&& cp -rf #{p4client_root} #{destination}"
|
94
93
|
end
|
95
94
|
|
@@ -108,6 +107,14 @@ module Capistrano
|
|
108
107
|
def p4passwd
|
109
108
|
variable(:p4passwd) || variable(:scm_password)
|
110
109
|
end
|
110
|
+
|
111
|
+
def p4sync_flags
|
112
|
+
variable(:p4sync_flags) || "-f"
|
113
|
+
end
|
114
|
+
|
115
|
+
def p4client_root
|
116
|
+
variable(:p4client_root) || "`#{command} #{authentication} client -o | grep ^Root | cut -f2`"
|
117
|
+
end
|
111
118
|
|
112
119
|
def rev_no(revision)
|
113
120
|
case revision.to_s
|
@@ -72,7 +72,7 @@ module Capistrano
|
|
72
72
|
case text
|
73
73
|
when /\bpassword.*:/i
|
74
74
|
# subversion is prompting for a password
|
75
|
-
"#{
|
75
|
+
"#{scm_password_prompt}\n"
|
76
76
|
when %r{\(yes/no\)}
|
77
77
|
# subversion is asking whether or not to connect
|
78
78
|
"yes\n"
|
@@ -80,7 +80,7 @@ module Capistrano
|
|
80
80
|
# subversion is asking for the passphrase for the user's key
|
81
81
|
"#{variable(:scm_passphrase)}\n"
|
82
82
|
when /The entry \'(.+?)\' is no longer a directory/
|
83
|
-
raise
|
83
|
+
raise Capistrano::Error, "subversion can't update because directory '#{$1}' was replaced. Please add it to svn:ignore."
|
84
84
|
when /accept \(t\)emporarily/
|
85
85
|
# subversion is asking whether to accept the certificate
|
86
86
|
"t\n"
|
@@ -107,6 +107,12 @@ module Capistrano
|
|
107
107
|
def verbose
|
108
108
|
variable(:scm_verbose) ? nil : "-q"
|
109
109
|
end
|
110
|
+
|
111
|
+
def scm_password_prompt
|
112
|
+
@scm_password_prompt ||= variable(:scm_password) ||
|
113
|
+
variable(:password) ||
|
114
|
+
Capistrano::CLI.password_prompt("Subversion password: ")
|
115
|
+
end
|
110
116
|
end
|
111
117
|
|
112
118
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
|
2
|
+
module Capistrano
|
3
|
+
class Role
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(*list)
|
7
|
+
@static_servers = []
|
8
|
+
@dynamic_servers = []
|
9
|
+
push(*list)
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
servers.each &block
|
14
|
+
end
|
15
|
+
|
16
|
+
def push(*list)
|
17
|
+
options = list.last.is_a?(Hash) ? list.pop : {}
|
18
|
+
list.each do |item|
|
19
|
+
if item.respond_to?(:call)
|
20
|
+
@dynamic_servers << DynamicServerList.new(item, options)
|
21
|
+
else
|
22
|
+
@static_servers << self.class.wrap_server(item, options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method :<<, :push
|
27
|
+
|
28
|
+
def servers
|
29
|
+
@static_servers + dynamic_servers
|
30
|
+
end
|
31
|
+
alias_method :to_ary, :servers
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
servers.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Resets the cache, so that proc values may be recalculated.
|
38
|
+
# There should be a command in Configuration::Roles to do this,
|
39
|
+
# but I haven't needed it yet, and I'm not sure yet
|
40
|
+
# what to call that command. Suggestions?
|
41
|
+
def reset!
|
42
|
+
@dynamic_servers.each { |item| item.reset! }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Clears everything. I still thing this should be 'clear!', but that's not
|
46
|
+
# the way Array does it.
|
47
|
+
def clear
|
48
|
+
@dynamic_servers.clear
|
49
|
+
@static_servers.clear
|
50
|
+
end
|
51
|
+
|
52
|
+
# Mostly for documentation purposes. Doesn't seem to do anything.
|
53
|
+
protected
|
54
|
+
|
55
|
+
# This is the combination of a block, a hash of options, and a cached value.
|
56
|
+
# It is protected because it is an implementation detail -- the original
|
57
|
+
# implementation was two lists (blocks and cached results of calling them).
|
58
|
+
class DynamicServerList
|
59
|
+
def initialize (block, options)
|
60
|
+
@block = block
|
61
|
+
@options = options
|
62
|
+
@cached = []
|
63
|
+
@is_cached = false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convert to a list of ServerDefinitions
|
67
|
+
def to_ary
|
68
|
+
unless @is_cached
|
69
|
+
@cached = Role::wrap_list(@block.call(@options), @options)
|
70
|
+
@is_cached = true
|
71
|
+
end
|
72
|
+
@cached
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clear the cached value
|
76
|
+
def reset!
|
77
|
+
@cached.clear
|
78
|
+
@is_cached = false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Attribute reader for the cached results of executing the blocks in turn
|
83
|
+
def dynamic_servers
|
84
|
+
@dynamic_servers.inject([]) { |list, item| list.concat item }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Wraps a string in a ServerDefinition, if it isn't already.
|
88
|
+
# This and wrap_list should probably go in ServerDefinition in some form.
|
89
|
+
def self.wrap_server (item, options)
|
90
|
+
item.is_a?(ServerDefinition) ? item : ServerDefinition.new(item, options)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Turns a list, or something resembling a list, into a properly-formatted
|
94
|
+
# ServerDefinition list. Keep an eye on this one -- it's entirely too
|
95
|
+
# magical for its own good. In particular, if ServerDefinition ever inherits
|
96
|
+
# from Array, this will break.
|
97
|
+
def self.wrap_list (*list)
|
98
|
+
options = list.last.is_a?(Hash) ? list.pop : {}
|
99
|
+
if list.length == 1
|
100
|
+
if list.first.nil?
|
101
|
+
return []
|
102
|
+
elsif list.first.is_a?(Array)
|
103
|
+
list = list.first
|
104
|
+
end
|
105
|
+
end
|
106
|
+
options.merge! list.pop if list.last.is_a?(Hash)
|
107
|
+
list.map do |item|
|
108
|
+
self.wrap_server item, options
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/capistrano/ssh.rb
CHANGED
@@ -3,12 +3,13 @@ require 'capistrano/server_definition'
|
|
3
3
|
module Capistrano
|
4
4
|
# Represents the definition of a single task.
|
5
5
|
class TaskDefinition
|
6
|
-
attr_reader :name, :namespace, :options, :body, :desc, :on_error
|
6
|
+
attr_reader :name, :namespace, :options, :body, :desc, :on_error, :max_hosts
|
7
7
|
|
8
8
|
def initialize(name, namespace, options={}, &block)
|
9
9
|
@name, @namespace, @options = name, namespace, options
|
10
10
|
@desc = @options.delete(:desc)
|
11
11
|
@on_error = options.delete(:on_error)
|
12
|
+
@max_hosts = options[:max_hosts] && options[:max_hosts].to_i
|
12
13
|
@body = block or raise ArgumentError, "a task requires a block"
|
13
14
|
@servers = nil
|
14
15
|
end
|
data/lib/capistrano/upload.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'net-sftp', "< 1.99.0"
|
4
|
+
rescue LoadError, NameError
|
5
|
+
end
|
6
|
+
|
1
7
|
require 'net/sftp'
|
2
8
|
require 'net/sftp/operations/errors'
|
3
9
|
require 'capistrano/errors'
|
@@ -42,7 +48,7 @@ module Capistrano
|
|
42
48
|
#
|
43
49
|
# * data: required. Should refer to a String containing the contents of
|
44
50
|
# the file to upload.
|
45
|
-
# * mode: optional. The "mode" of the destination file. Defaults to
|
51
|
+
# * mode: optional. The "mode" of the destination file. Defaults to 0664.
|
46
52
|
# * logger: optional. Should point to a Capistrano::Logger instance, if
|
47
53
|
# given.
|
48
54
|
def initialize(sessions, filename, options)
|
data/lib/capistrano/version.rb
CHANGED
@@ -217,7 +217,6 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
|
|
217
217
|
@config.current_task = mock_task(:on_error => :continue)
|
218
218
|
@config.expects(:find_servers_for_task).with(@config.current_task, {}).returns(list)
|
219
219
|
Capistrano::SSH.expects(:connect).times(2).raises(Exception).then.returns(:success)
|
220
|
-
@config.expects(:failed!).with(server("cap1"))
|
221
220
|
@config.execute_on_servers do |servers|
|
222
221
|
assert_equal %w(cap2), servers.map { |s| s.host }
|
223
222
|
end
|
@@ -260,7 +259,7 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
|
|
260
259
|
assert_equal %w(cap2), servers.map { |s| s.host }
|
261
260
|
end
|
262
261
|
end
|
263
|
-
|
262
|
+
|
264
263
|
def test_connect_should_establish_connections_to_all_servers_in_scope
|
265
264
|
assert @config.sessions.empty?
|
266
265
|
@config.current_task = mock_task
|
@@ -269,7 +268,43 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
|
|
269
268
|
@config.connect!
|
270
269
|
assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
|
271
270
|
end
|
272
|
-
|
271
|
+
|
272
|
+
def test_execute_on_servers_should_only_run_on_tasks_max_hosts_hosts_at_once
|
273
|
+
cap1 = server("cap1")
|
274
|
+
cap2 = server("cap2")
|
275
|
+
connection1 = mock()
|
276
|
+
connection2 = mock()
|
277
|
+
connection1.expects(:close)
|
278
|
+
connection2.expects(:close)
|
279
|
+
@config.current_task = mock_task(:max_hosts => 1)
|
280
|
+
@config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
|
281
|
+
Capistrano::SSH.expects(:connect).times(2).returns(connection1).then.returns(connection2)
|
282
|
+
block_called = 0
|
283
|
+
@config.execute_on_servers do |servers|
|
284
|
+
block_called += 1
|
285
|
+
assert_equal 1, servers.size
|
286
|
+
end
|
287
|
+
assert_equal 2, block_called
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_execute_on_servers_should_only_run_on_max_hosts_hosts_at_once
|
291
|
+
cap1 = server("cap1")
|
292
|
+
cap2 = server("cap2")
|
293
|
+
connection1 = mock()
|
294
|
+
connection2 = mock()
|
295
|
+
connection1.expects(:close)
|
296
|
+
connection2.expects(:close)
|
297
|
+
@config.current_task = mock_task(:max_hosts => 1)
|
298
|
+
@config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
|
299
|
+
Capistrano::SSH.expects(:connect).times(2).returns(connection1).then.returns(connection2)
|
300
|
+
block_called = 0
|
301
|
+
@config.execute_on_servers do |servers|
|
302
|
+
block_called += 1
|
303
|
+
assert_equal 1, servers.size
|
304
|
+
end
|
305
|
+
assert_equal 2, block_called
|
306
|
+
end
|
307
|
+
|
273
308
|
def test_connect_should_honor_once_option
|
274
309
|
assert @config.sessions.empty?
|
275
310
|
@config.current_task = mock_task
|
@@ -283,6 +318,11 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
|
|
283
318
|
|
284
319
|
def mock_task(options={})
|
285
320
|
continue_on_error = options[:on_error] == :continue
|
286
|
-
stub("task",
|
321
|
+
stub("task",
|
322
|
+
:fully_qualified_name => "name",
|
323
|
+
:options => options,
|
324
|
+
:continue_on_error? => continue_on_error,
|
325
|
+
:max_hosts => options[:max_hosts]
|
326
|
+
)
|
287
327
|
end
|
288
328
|
end
|
@@ -294,4 +294,12 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
|
|
294
294
|
@config.namespace(:outer) { namespace(:middle) { namespace(:inner) {} } }
|
295
295
|
assert_equal @config, @config.namespaces[:outer].namespaces[:middle].namespaces[:inner].top
|
296
296
|
end
|
297
|
+
|
298
|
+
def test_find_task_should_return_nil_when_empty_inner_task
|
299
|
+
@config.namespace :outer do
|
300
|
+
namespace :inner do
|
301
|
+
end
|
302
|
+
end
|
303
|
+
assert_nil @config.find_task("outer::inner")
|
304
|
+
end
|
297
305
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "#{File.dirname(__FILE__)}/../utils"
|
2
2
|
require 'capistrano/configuration/roles'
|
3
|
+
require 'capistrano/server_definition'
|
3
4
|
|
4
5
|
class ConfigurationRolesTest < Test::Unit::TestCase
|
5
6
|
class MockConfig
|
@@ -29,13 +30,34 @@ class ConfigurationRolesTest < Test::Unit::TestCase
|
|
29
30
|
def test_role_with_one_argument_should_add_to_roles_collection
|
30
31
|
@config.role :app, "app1.capistrano.test"
|
31
32
|
assert_equal [:app], @config.roles.keys
|
32
|
-
|
33
|
+
assert_role_equals %w(app1.capistrano.test)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_role_block_returning_single_string_is_added_to_roles_collection
|
37
|
+
@config.role :app do
|
38
|
+
'app1.capistrano.test'
|
39
|
+
end
|
40
|
+
assert_role_equals %w(app1.capistrano.test)
|
33
41
|
end
|
34
42
|
|
35
43
|
def test_role_with_multiple_arguments_should_add_each_to_roles_collection
|
36
44
|
@config.role :app, "app1.capistrano.test", "app2.capistrano.test"
|
37
45
|
assert_equal [:app], @config.roles.keys
|
38
|
-
|
46
|
+
assert_role_equals %w(app1.capistrano.test app2.capistrano.test)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_role_with_block_and_strings_should_add_both_to_roles_collection
|
50
|
+
@config.role :app, 'app1.capistrano.test' do
|
51
|
+
'app2.capistrano.test'
|
52
|
+
end
|
53
|
+
assert_role_equals %w(app1.capistrano.test app2.capistrano.test)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_role_block_returning_array_should_add_each_to_roles_collection
|
57
|
+
@config.role :app do
|
58
|
+
['app1.capistrano.test', 'app2.capistrano.test']
|
59
|
+
end
|
60
|
+
assert_role_equals %w(app1.capistrano.test app2.capistrano.test)
|
39
61
|
end
|
40
62
|
|
41
63
|
def test_role_with_options_should_apply_options_to_each_argument
|
@@ -44,4 +66,78 @@ class ConfigurationRolesTest < Test::Unit::TestCase
|
|
44
66
|
assert_equal({:extra => :value}, server.options)
|
45
67
|
end
|
46
68
|
end
|
47
|
-
|
69
|
+
|
70
|
+
def test_role_with_options_should_apply_options_to_block_results
|
71
|
+
@config.role :app, :extra => :value do
|
72
|
+
['app1.capistrano.test', 'app2.capistrano.test']
|
73
|
+
end
|
74
|
+
@config.roles[:app].each do |server|
|
75
|
+
assert_equal({:extra => :value}, server.options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_options_should_apply_only_to_this_argument_set
|
80
|
+
@config.role :app, 'app1.capistrano.test', 'app2.capistrano.test' do
|
81
|
+
['app3.capistrano.test', 'app4.capistrano.test']
|
82
|
+
end
|
83
|
+
@config.role :app, 'app5.capistrano.test', 'app6.capistrano.test', :extra => :value do
|
84
|
+
['app7.capistrano.test', 'app8.capistrano.test']
|
85
|
+
end
|
86
|
+
@config.role :app, 'app9.capistrano.test'
|
87
|
+
|
88
|
+
option_hosts = ['app5.capistrano.test', 'app6.capistrano.test', 'app7.capistrano.test', 'app8.capistrano.test']
|
89
|
+
@config.roles[:app].each do |server|
|
90
|
+
if (option_hosts.include? server.host)
|
91
|
+
assert_equal({:extra => :value}, server.options)
|
92
|
+
else
|
93
|
+
assert_not_equal({:extra => :value}, server.options)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Here, the source should be more readable than the method name
|
99
|
+
def test_role_block_returns_options_hash_is_merged_with_role_options_argument
|
100
|
+
@config.role :app, :first => :one, :second => :two do
|
101
|
+
['app1.capistrano.test', 'app2.capistrano.test', {:second => :please, :third => :three}]
|
102
|
+
end
|
103
|
+
@config.roles[:app].each do |server|
|
104
|
+
assert_equal({:first => :one, :second => :please, :third => :three}, server.options)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_role_block_can_override_role_options_argument
|
109
|
+
@config.role :app, :value => :wrong do
|
110
|
+
Capistrano::ServerDefinition.new('app.capistrano.test')
|
111
|
+
end
|
112
|
+
@config.roles[:app].servers
|
113
|
+
@config.roles[:app].servers.each do |server|
|
114
|
+
assert_not_equal({:value => :wrong}, server.options)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_role_block_can_return_nil
|
119
|
+
@config.role :app do
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
assert_role_equals ([])
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_role_block_can_return_empty_array
|
126
|
+
@config.role :app do
|
127
|
+
[]
|
128
|
+
end
|
129
|
+
assert_role_equals ([])
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_role_definitions_via_server_should_associate_server_with_roles
|
133
|
+
@config.server "www.capistrano.test", :web, :app
|
134
|
+
assert_equal %w(www.capistrano.test), @config.roles[:app].map { |s| s.host }
|
135
|
+
assert_equal %w(www.capistrano.test), @config.roles[:web].map { |s| s.host }
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def assert_role_equals(list)
|
141
|
+
assert_equal list, @config.roles[:app].map { |s| s.host }
|
142
|
+
end
|
143
|
+
end
|
data/test/deploy/scm/git_test.rb
CHANGED
@@ -15,18 +15,28 @@ class DeploySCMGitTest < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
def test_head
|
17
17
|
assert_equal "HEAD", @source.head
|
18
|
+
|
19
|
+
# With :branch
|
18
20
|
@config[:branch] = "master"
|
19
21
|
assert_equal "master", @source.head
|
20
22
|
end
|
21
23
|
|
24
|
+
def test_origin
|
25
|
+
assert_equal "origin", @source.origin
|
26
|
+
@config[:remote] = "username"
|
27
|
+
assert_equal "username", @source.origin
|
28
|
+
end
|
29
|
+
|
22
30
|
def test_checkout
|
23
31
|
@config[:repository] = "git@somehost.com:project.git"
|
24
32
|
dest = "/var/www"
|
25
|
-
|
33
|
+
rev = 'c2d9e79'
|
34
|
+
assert_equal "git clone git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy #{rev}", @source.checkout(rev, dest)
|
26
35
|
|
27
|
-
# With
|
28
|
-
|
29
|
-
|
36
|
+
# With :scm_command
|
37
|
+
git = "/opt/local/bin/git"
|
38
|
+
@config[:scm_command] = git
|
39
|
+
assert_equal "#{git} clone git@somehost.com:project.git /var/www && cd /var/www && #{git} checkout -b deploy #{rev}", @source.checkout(rev, dest)
|
30
40
|
end
|
31
41
|
|
32
42
|
def test_diff
|
@@ -51,22 +61,41 @@ class DeploySCMGitTest < Test::Unit::TestCase
|
|
51
61
|
|
52
62
|
def test_sync
|
53
63
|
dest = "/var/www"
|
54
|
-
|
64
|
+
rev = 'c2d9e79'
|
65
|
+
assert_equal "cd #{dest} && git fetch origin && git reset --hard #{rev}", @source.sync(rev, dest)
|
66
|
+
|
67
|
+
# With :scm_command
|
68
|
+
git = "/opt/local/bin/git"
|
69
|
+
@config[:scm_command] = git
|
70
|
+
assert_equal "cd #{dest} && #{git} fetch origin && #{git} reset --hard #{rev}", @source.sync(rev, dest)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_sync_with_remote
|
74
|
+
dest = "/var/www"
|
75
|
+
rev = 'c2d9e79'
|
76
|
+
remote = "username"
|
77
|
+
repository = "git@somehost.com:project.git"
|
78
|
+
|
79
|
+
@config[:repository] = repository
|
80
|
+
@config[:remote] = remote
|
55
81
|
|
56
|
-
#
|
57
|
-
@config[:branch] = "origin/foo"
|
58
|
-
assert_equal "cd #{dest} && git fetch origin && git merge origin/foo", @source.sync('Not used', dest)
|
82
|
+
assert_equal "cd #{dest} && git config remote.#{remote}.url #{repository} && git config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/* && git fetch #{remote} && git reset --hard #{rev}", @source.sync(rev, dest)
|
59
83
|
end
|
60
84
|
|
61
85
|
def test_shallow_clone
|
62
86
|
@config[:repository] = "git@somehost.com:project.git"
|
63
87
|
@config[:git_shallow_clone] = 1
|
64
88
|
dest = "/var/www"
|
65
|
-
|
89
|
+
rev = 'c2d9e79'
|
90
|
+
assert_equal "git clone --depth 1 git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy #{rev}", @source.checkout(rev, dest)
|
91
|
+
end
|
66
92
|
|
67
|
-
|
68
|
-
@config[:
|
69
|
-
|
93
|
+
def test_remote_clone
|
94
|
+
@config[:repository] = "git@somehost.com:project.git"
|
95
|
+
@config[:remote] = "username"
|
96
|
+
dest = "/var/www"
|
97
|
+
rev = 'c2d9e79'
|
98
|
+
assert_equal "git clone -o username git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy #{rev}", @source.checkout(rev, dest)
|
70
99
|
end
|
71
100
|
|
72
101
|
# Tests from base_test.rb, makin' sure we didn't break anything up there!
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../../utils"
|
2
|
+
require 'capistrano/recipes/deploy/scm/mercurial'
|
3
|
+
|
4
|
+
class DeploySCMMercurialTest < Test::Unit::TestCase
|
5
|
+
class TestSCM < Capistrano::Deploy::SCM::Mercurial
|
6
|
+
default_command "hg"
|
7
|
+
end
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@config = { }
|
11
|
+
def @config.exists?(name); key?(name); end
|
12
|
+
|
13
|
+
@source = TestSCM.new(@config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_head
|
17
|
+
assert_equal "tip", @source.head
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_checkout
|
21
|
+
@config[:repository] = "http://example.com/project-hg"
|
22
|
+
dest = "/var/www"
|
23
|
+
assert_equal "hg clone --noupdate http://example.com/project-hg /var/www && hg update --repository /var/www --clean 8a8e00b8f11b", @source.checkout('8a8e00b8f11b', dest)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_diff
|
27
|
+
assert_equal "hg diff --rev tip", @source.diff('tip')
|
28
|
+
assert_equal "hg diff --rev 1 --rev 2", @source.diff('1', '2')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_log
|
32
|
+
assert_equal "hg log --rev 8a8e00b8f11b", @source.log('8a8e00b8f11b')
|
33
|
+
assert_equal "hg log --rev 0:3", @source.log('0', '3')
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_query_revision
|
37
|
+
assert_equal "hg log -r 8a8e00b8f11b --template '{node|short}'", @source.query_revision('8a8e00b8f11b') { |o| o }
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_username_should_be_backwards_compatible
|
41
|
+
# older versions of this module required :scm_user var instead
|
42
|
+
# of the currently preferred :scm_username
|
43
|
+
require 'capistrano/logger'
|
44
|
+
@config[:scm_user] = "fred"
|
45
|
+
text = "user:"
|
46
|
+
assert_equal "fred\n", @source.handle_data(:test_state, :test_stream, text)
|
47
|
+
# :scm_username takes priority
|
48
|
+
@config[:scm_username] = "wilma"
|
49
|
+
assert_equal "wilma\n", @source.handle_data(:test_state, :test_stream, text)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_sync
|
53
|
+
dest = "/var/www"
|
54
|
+
assert_equal "hg pull --repository /var/www && hg update --repository /var/www --clean 8a8e00b8f11b", @source.sync('8a8e00b8f11b', dest)
|
55
|
+
|
56
|
+
# With :scm_command
|
57
|
+
@config[:scm_command] = "/opt/local/bin/hg"
|
58
|
+
assert_equal "/opt/local/bin/hg pull --repository /var/www && /opt/local/bin/hg update --repository /var/www --clean 8a8e00b8f11b", @source.sync('8a8e00b8f11b', dest)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_export
|
62
|
+
dest = "/var/www"
|
63
|
+
assert_raise(NotImplementedError) { @source.export('8a8e00b8f11b', dest) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_sends_password_if_set
|
67
|
+
require 'capistrano/logger'
|
68
|
+
text = "password:"
|
69
|
+
@config[:scm_password] = "opensesame"
|
70
|
+
assert_equal "opensesame\n", @source.handle_data(:test_state, :test_stream, text)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_prompts_for_password_if_preferred
|
74
|
+
require 'capistrano/logger'
|
75
|
+
require 'capistrano/cli'
|
76
|
+
Capistrano::CLI.stubs(:password_prompt).with("hg password: ").returns("opensesame")
|
77
|
+
@config[:scm_prefer_prompt] = true
|
78
|
+
text = "password:"
|
79
|
+
assert_equal "opensesame\n", @source.handle_data(:test_state, :test_stream, text)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Tests from base_test.rb, makin' sure we didn't break anything up there!
|
84
|
+
def test_command_should_default_to_default_command
|
85
|
+
assert_equal "hg", @source.command
|
86
|
+
@source.local { assert_equal "hg", @source.command }
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_command_should_use_scm_command_if_available
|
90
|
+
@config[:scm_command] = "/opt/local/bin/hg"
|
91
|
+
assert_equal "/opt/local/bin/hg", @source.command
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_command_should_use_scm_command_in_local_mode_if_local_scm_command_not_set
|
95
|
+
@config[:scm_command] = "/opt/local/bin/hg"
|
96
|
+
@source.local { assert_equal "/opt/local/bin/hg", @source.command }
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_command_should_use_local_scm_command_in_local_mode_if_local_scm_command_is_set
|
100
|
+
@config[:scm_command] = "/opt/local/bin/hg"
|
101
|
+
@config[:local_scm_command] = "/usr/local/bin/hg"
|
102
|
+
assert_equal "/opt/local/bin/hg", @source.command
|
103
|
+
@source.local { assert_equal "/usr/local/bin/hg", @source.command }
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_command_should_use_default_if_scm_command_is_default
|
107
|
+
@config[:scm_command] = :default
|
108
|
+
assert_equal "hg", @source.command
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_command_should_use_default_in_local_mode_if_local_scm_command_is_default
|
112
|
+
@config[:scm_command] = "/foo/bar/hg"
|
113
|
+
@config[:local_scm_command] = :default
|
114
|
+
@source.local { assert_equal "hg", @source.command }
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_local_mode_proxy_should_treat_messages_as_being_in_local_mode
|
118
|
+
@config[:scm_command] = "/foo/bar/hg"
|
119
|
+
@config[:local_scm_command] = :default
|
120
|
+
assert_equal "hg", @source.local.command
|
121
|
+
assert_equal "/foo/bar/hg", @source.command
|
122
|
+
end
|
123
|
+
end
|
metadata
CHANGED
@@ -1,33 +1,59 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: capistrano
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 2.
|
7
|
-
date: 2007-10-14 00:00:00 -06:00
|
8
|
-
summary: Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: jamis@37signals.com
|
12
|
-
homepage: http://www.capify.org
|
13
|
-
rubyforge_project:
|
14
|
-
description:
|
15
|
-
autorequire: capistrano
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: false
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 2.2.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Jamis Buck
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-27 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: net-ssh
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.10
|
23
|
+
- - <
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 1.99.0
|
26
|
+
version:
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-sftp
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.1.0
|
35
|
+
- - <
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.99.0
|
38
|
+
version:
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: highline
|
41
|
+
version_requirement:
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
description:
|
49
|
+
email: jamis@37signals.com
|
50
|
+
executables:
|
51
|
+
- cap
|
52
|
+
- capify
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
31
57
|
files:
|
32
58
|
- bin/cap
|
33
59
|
- bin/capify
|
@@ -91,6 +117,7 @@ files:
|
|
91
117
|
- lib/capistrano/recipes/templates
|
92
118
|
- lib/capistrano/recipes/templates/maintenance.rhtml
|
93
119
|
- lib/capistrano/recipes/upgrade.rb
|
120
|
+
- lib/capistrano/role.rb
|
94
121
|
- lib/capistrano/server_definition.rb
|
95
122
|
- lib/capistrano/shell.rb
|
96
123
|
- lib/capistrano/ssh.rb
|
@@ -125,6 +152,7 @@ files:
|
|
125
152
|
- test/deploy/scm/accurev_test.rb
|
126
153
|
- test/deploy/scm/base_test.rb
|
127
154
|
- test/deploy/scm/git_test.rb
|
155
|
+
- test/deploy/scm/mercurial_test.rb
|
128
156
|
- test/deploy/strategy
|
129
157
|
- test/deploy/strategy/copy_test.rb
|
130
158
|
- test/extensions_test.rb
|
@@ -144,44 +172,31 @@ files:
|
|
144
172
|
- README
|
145
173
|
- MIT-LICENSE
|
146
174
|
- CHANGELOG
|
147
|
-
|
148
|
-
|
175
|
+
has_rdoc: true
|
176
|
+
homepage: http://www.capify.org
|
177
|
+
post_install_message:
|
149
178
|
rdoc_options: []
|
150
179
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
-
|
156
|
-
|
157
|
-
|
180
|
+
require_paths:
|
181
|
+
- lib
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: "0"
|
187
|
+
version:
|
188
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: "0"
|
193
|
+
version:
|
158
194
|
requirements: []
|
159
195
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
- !ruby/object:Gem::Version
|
168
|
-
version: 1.0.10
|
169
|
-
version:
|
170
|
-
- !ruby/object:Gem::Dependency
|
171
|
-
name: net-sftp
|
172
|
-
version_requirement:
|
173
|
-
version_requirements: !ruby/object:Gem::Version::Requirement
|
174
|
-
requirements:
|
175
|
-
- - ">="
|
176
|
-
- !ruby/object:Gem::Version
|
177
|
-
version: 1.1.0
|
178
|
-
version:
|
179
|
-
- !ruby/object:Gem::Dependency
|
180
|
-
name: highline
|
181
|
-
version_requirement:
|
182
|
-
version_requirements: !ruby/object:Gem::Version::Requirement
|
183
|
-
requirements:
|
184
|
-
- - ">"
|
185
|
-
- !ruby/object:Gem::Version
|
186
|
-
version: 0.0.0
|
187
|
-
version:
|
196
|
+
rubyforge_project: capistrano
|
197
|
+
rubygems_version: 1.0.0
|
198
|
+
signing_key:
|
199
|
+
specification_version: 2
|
200
|
+
summary: Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.
|
201
|
+
test_files: []
|
202
|
+
|