=== modified file 'vendor/gems/capistrano-1.99.1/lib/capistrano/configuration/connections.rb'
--- vendor/gems/capistrano-1.99.1/lib/capistrano/configuration/connections.rb	2007-06-04 19:52:30 +0000
+++ vendor/gems/capistrano-1.99.1/lib/capistrano/configuration/connections.rb	2007-06-08 13:44:38 +0000
@@ -57,6 +57,7 @@
       # Ensures that there are active sessions for each server in the list.
       def establish_connections_to(servers)
         servers = Array(servers)
+        failed_servers = []
 
         # because Net::SSH uses lazy loading for things, we need to make sure
         # that at least one connection has been made successfully, to kind of
@@ -65,12 +66,29 @@
         # and causing Net::SSH to die of confusion.
         # TODO investigate Net::SSH and see if this can't be solved there
 
-        if sessions.empty?
-          server, servers = servers.first, servers[1..-1]
-          sessions[server] = connection_factory.connect_to(server)
+        begin
+          if sessions.empty? and not servers.empty?
+            server, servers = servers.first, servers[1..-1]
+            sessions[server] = connection_factory.connect_to(server)
+          end
+        rescue Exception => error
+          failed_servers << server
+          retry
         end
 
-        servers.map { |server| establish_connection_to(server) }.each { |t| t.join }
+        servers.map { |server|
+          begin
+            establish_connection_to(server)
+          rescue Exception => error
+            failed_servers << server
+          end
+        }.each { |t| t.join }
+        
+        if not failed_servers.empty?
+          error = ConnectionError.new("connection failed for: #{servers.join(',')}")
+          error.hosts = failed_servers
+          raise error
+        end
       end
 
       # Determines the set of servers within the current task's scope and
@@ -78,8 +96,12 @@
       # servers.
       def execute_on_servers(options={})
         raise ArgumentError, "expected a block" unless block_given?
-
+            
         if task = current_task
+          if task.options[:best_efforts]
+            (options[:except] ||= {}).merge!({ :failed_best_efforts => true }) 
+          end
+
           servers = find_servers_for_task(task, options)
 
           if servers.empty? 
@@ -96,7 +118,18 @@
         logger.trace "servers: #{servers.map { |s| s.host }.inspect}"
 
         # establish connections to those servers, as necessary
-        establish_connections_to(servers)
+        begin
+          establish_connections_to(servers)
+        rescue ConnectionError => error
+          raise error unless task.options[:best_efforts]
+          # In best efforts mode we remove any failed servers from the list, and carry on
+          error.hosts.each { |server|
+            logger.important "could not connect", server if logger
+            server.options[:failed_best_efforts] = true
+            servers.delete(server)
+          }
+        end
+        
         yield servers
       end
 

=== modified file 'vendor/gems/capistrano-1.99.1/lib/capistrano/errors.rb'
--- vendor/gems/capistrano-1.99.1/lib/capistrano/errors.rb	2007-06-04 16:30:49 +0000
+++ vendor/gems/capistrano-1.99.1/lib/capistrano/errors.rb	2007-06-08 12:48:41 +0000
@@ -2,8 +2,11 @@
   class Error < RuntimeError; end
 
   class CaptureError < Error; end
-  class ConnectionError < Error; end
   class NoSuchTaskError < Error; end
+  
+  class ConnectionError < Error
+    attr_accessor :hosts
+  end
 
   class UploadError < Error
     attr_accessor :hosts

=== modified file 'vendor/gems/capistrano-1.99.1/test/configuration/connections_test.rb'
--- vendor/gems/capistrano-1.99.1/test/configuration/connections_test.rb	2007-06-04 19:52:30 +0000
+++ vendor/gems/capistrano-1.99.1/test/configuration/connections_test.rb	2007-06-08 13:38:06 +0000
@@ -123,7 +123,7 @@
 
   def test_execute_on_servers_should_not_raise_an_error_if_the_current_task_has_no_matching_servers_but_is_marked_best_efforts
     @config.current_task = stub("task", :fully_qualified_name => "name", :options => { :best_efforts => true })
-    @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
+    @config.expects(:find_servers_for_task).with(@config.current_task, { :except => { :failed_best_efforts => true } }).returns([])
     assert_nothing_raised do
       @config.execute_on_servers do
         flunk "should not get here"
@@ -133,7 +133,7 @@
 
   def test_execute_on_servers_should_determine_server_list_from_active_task
     assert @config.sessions.empty?
-    @config.current_task = stub("task")
+    @config.current_task = stub("task", :options => {})
     @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
     Capistrano::SSH.expects(:connect).times(3).returns(:success)
     @config.execute_on_servers {}
@@ -142,7 +142,7 @@
 
   def test_execute_on_servers_should_yield_server_list_to_block
     assert @config.sessions.empty?
-    @config.current_task = stub("task")
+    @config.current_task = stub("task", :options => {})
     @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
     Capistrano::SSH.expects(:connect).times(3).returns(:success)
     block_called = false
@@ -158,7 +158,7 @@
 
   def test_execute_on_servers_with_once_option_should_establish_connection_to_and_yield_only_the_first_server
     assert @config.sessions.empty?
-    @config.current_task = stub("task")
+    @config.current_task = stub("task", :options => {})
     @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
     Capistrano::SSH.expects(:connect).returns(:success)
     block_called = false
@@ -172,7 +172,7 @@
 
   def test_connect_should_establish_connections_to_all_servers_in_scope
     assert @config.sessions.empty?
-    @config.current_task = stub("task")
+    @config.current_task = stub("task", :options => {})
     @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
     Capistrano::SSH.expects(:connect).times(3).returns(:success)
     @config.connect!
@@ -181,10 +181,54 @@
 
   def test_connect_should_honor_once_option
     assert @config.sessions.empty?
-    @config.current_task = stub("task")
+    @config.current_task = stub("task", :options => {})
     @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
     Capistrano::SSH.expects(:connect).returns(:success)
     @config.connect! :once => true
     assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
   end
+  
+  def test_connect_should_raise_an_error_if_any_connection_fails
+    assert @config.sessions.empty?
+    @config.current_task = stub("task", :options => {})
+    @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
+    Capistrano::SSH.expects(:connect).times(3).raises(Exception)
+    assert_raises (Capistrano::ConnectionError) do
+      @config.connect!
+    end
+  end
+  
+  def test_connect_should_not_raise_if_a_connection_fails_if_task_is_marked_best_efforts
+    assert @config.sessions.empty?
+    @config.current_task = stub("task", :options => { :best_efforts => true })
+    @config.expects(:find_servers_for_task).with(@config.current_task, { :except => { :failed_best_efforts => true } }).returns([server("cap1"), server("cap2"), server("cap3")])
+    Capistrano::SSH.expects(:connect).times(3).raises(Exception)
+    assert_nothing_raised do
+      @config.connect!
+    end
+  end
+  
+  def test_connection_should_try_subsequent_hosts_if_one_host_fails_if_task_is_marked_best_efforts
+    assert @config.sessions.empty?
+    @config.current_task = stub("task", :options => { :best_efforts => true })
+    @config.expects(:find_servers_for_task).with(@config.current_task, { :except => { :failed_best_efforts => true }}).returns([server("cap1"), server("cap2")])
+    Capistrano::SSH.expects(:connect).with { |s,| s.host == "cap1" }.raises(Exception)
+    Capistrano::SSH.expects(:connect).with { |s,| s.host == "cap2" }.returns(:success)
+    @config.connect!
+  end
+
+  def test_execute_on_servers_with_best_efforts_option_should_establish_connection_to_and_yield_only_the_successfully_connected_servers
+    assert @config.sessions.empty?
+    @config.current_task = stub("task", :options => { :best_efforts => true })
+    @config.expects(:find_servers_for_task).with(@config.current_task, { :except => { :failed_best_efforts => true } } ).returns([server("cap1"), server("cap2"), server("cap3")])
+    Capistrano::SSH.expects(:connect).with { |s,| s.host == "cap1" }.returns(:success)
+    Capistrano::SSH.expects(:connect).with { |s,| s.host == "cap2" or s.host == "cap3" }.times(2).raises(Exception)
+    block_called = false
+    @config.execute_on_servers do |servers|
+      block_called = true
+      assert_equal %w(cap1), servers.map { |s| s.host }
+    end
+    assert block_called
+    assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
+  end
 end

