capistrano 2.1.0 → 2.2.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.
- 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
|
+
|