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 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]
@@ -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
- lines = line.strip.gsub(/(.{1,#{output_columns - indentation_size}})(?:\s+|\Z)/, "\\1\n").split(/\n/)
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
- # establish connections to those servers, as necessary
124
- begin
125
- establish_connections_to(servers)
126
- rescue ConnectionError => error
127
- raise error unless task && task.continue_on_error?
128
- error.hosts.each do |h|
129
- servers.delete(h)
130
- failed!(h)
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
- begin
135
- yield servers
136
- rescue RemoteError => error
137
- raise error unless task && task.continue_on_error?
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
 
@@ -116,7 +116,8 @@ module Capistrano
116
116
 
117
117
  ns = self
118
118
  until parts.empty?
119
- ns = ns.namespaces[parts.shift.to_sym]
119
+ next_part = parts.shift
120
+ ns = next_part.empty? ? nil : ns.namespaces[next_part.to_sym]
120
121
  return nil if ns.nil?
121
122
  end
122
123
 
@@ -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 :branch, revswitch(revision), repository, destination
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' command would work fine, except it doesn't let you
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
- [checkout(revision, destination) && "rm -rf #{destination}/.bzr"].join(" && ")
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
- if revision == head
67
- yield(scm(:revno, repository)).chomp
68
- else
69
- revision
70
- end
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, "origin/master"
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
- # and Scott Chacon http://jointheconversation.org
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 = command
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
- execute = "#{git} clone --depth #{depth} #{configuration[:repository]} #{destination} && "
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 = "#{git} clone #{configuration[:repository]} #{destination} && "
143
+ execute << "#{git} clone #{args.join(' ')} #{configuration[:repository]} #{destination}"
118
144
  end
119
145
 
120
- execute += "cd #{destination} && #{git} checkout -b deploy #{branch}"
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
- execute = "cd #{destination} && git fetch origin && "
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
- if head == 'HEAD'
131
- execute += "git merge origin/HEAD"
132
- else
133
- execute += "git merge #{head}"
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
- if variable(:scm_user)
74
- "#{variable(:scm_user)}\n"
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 :scm_user specified and Mercurial asked!\n" +
77
+ raise "No variable :scm_username specified and Mercurial asked!\n" +
77
78
  "Prompt was: #{text}"
78
79
  end
79
- when /^password:/mi
80
- if variable(:scm_password)
81
- "#{variable(:scm_password)}\n"
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, "-f")
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, "-f")
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, "-f")
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
- "#{variable(:scm_password) || variable(:password)}\n"
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 Capisrano::Error, "subversion can't update because directory '#{$1}' was replaced. Please add it to svn:ignore."
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
@@ -1,3 +1,9 @@
1
+ begin
2
+ require 'rubygems'
3
+ gem 'net-ssh', "< 1.99.0"
4
+ rescue LoadError, NameError
5
+ end
6
+
1
7
  require 'net/ssh'
2
8
 
3
9
  module Capistrano
@@ -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
@@ -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 0660.
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)
@@ -11,7 +11,7 @@ module Capistrano
11
11
  end
12
12
 
13
13
  MAJOR = 2
14
- MINOR = 1
14
+ MINOR = 2
15
15
  TINY = 0
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY].join(".")
@@ -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", :fully_qualified_name => "name", :options => options, :continue_on_error? => continue_on_error)
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
- assert_equal %w(app1.capistrano.test), @config.roles[:app].map { |s| s.host }
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
- assert_equal %w(app1.capistrano.test app2.capistrano.test), @config.roles[:app].map { |s| s.host }
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
- end
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
@@ -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
- assert_equal "git clone git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy HEAD", @source.checkout('Not used', dest)
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 branch
28
- @config[:branch] = "origin/foo"
29
- assert_equal "git clone git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy origin/foo", @source.checkout('Not used', dest)
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
- assert_equal "cd #{dest} && git fetch origin && git merge origin/HEAD", @source.sync('Not used', dest)
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
- # With branch
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
- assert_equal "git clone --depth 1 git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy HEAD", @source.checkout('Not used', dest)
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
- # With branch
68
- @config[:branch] = "origin/foo"
69
- assert_equal "git clone --depth 1 git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy origin/foo", @source.checkout('Not used', dest)
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.1.0
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
- test_files: []
148
-
175
+ has_rdoc: true
176
+ homepage: http://www.capify.org
177
+ post_install_message:
149
178
  rdoc_options: []
150
179
 
151
- extra_rdoc_files: []
152
-
153
- executables:
154
- - cap
155
- - capify
156
- extensions: []
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
- dependencies:
161
- - !ruby/object:Gem::Dependency
162
- name: net-ssh
163
- version_requirement:
164
- version_requirements: !ruby/object:Gem::Version::Requirement
165
- requirements:
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
+