You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by as...@apache.org on 2008/05/03 02:07:41 UTC

svn commit: r652955 - in /incubator/buildr/trunk: CHANGELOG buildr.gemspec lib/buildr/core/common.rb lib/buildr/core/transports.rb spec/transport_spec.rb

Author: assaf
Date: Fri May  2 17:07:41 2008
New Revision: 652955

URL: http://svn.apache.org/viewvc?rev=652955&view=rev
Log:
Added:  Reading files from SFTP server.
Changed: Upgraded to Net::SSH 2.0 and Net::SFTP 2.0.

Modified:
    incubator/buildr/trunk/CHANGELOG
    incubator/buildr/trunk/buildr.gemspec
    incubator/buildr/trunk/lib/buildr/core/common.rb
    incubator/buildr/trunk/lib/buildr/core/transports.rb
    incubator/buildr/trunk/spec/transport_spec.rb

Modified: incubator/buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/CHANGELOG?rev=652955&r1=652954&r2=652955&view=diff
==============================================================================
--- incubator/buildr/trunk/CHANGELOG (original)
+++ incubator/buildr/trunk/CHANGELOG Fri May  2 17:07:41 2008
@@ -1,4 +1,6 @@
 1.3.1 (Pending)
+* Added:  reading files from SFTP server.
+* Changed: Upgraded to Net::SSH 2.0 and Net::SFTP 2.0.
 
 1.3.0 (2008-04-25)
 * Added: Testing with EasyB (Nicolas Modrzyk).

Modified: incubator/buildr/trunk/buildr.gemspec
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/buildr.gemspec?rev=652955&r1=652954&r2=652955&view=diff
==============================================================================
--- incubator/buildr/trunk/buildr.gemspec (original)
+++ incubator/buildr/trunk/buildr.gemspec Fri May  2 17:07:41 2008
@@ -38,8 +38,8 @@
   # Tested against these dependencies.
   spec.add_dependency 'rake',                 '~> 0.8'
   spec.add_dependency 'builder',              '~> 2.1'
-  spec.add_dependency 'net-ssh',              '~> 1.1'
-  spec.add_dependency 'net-sftp',             '~> 1.1'
+  spec.add_dependency 'net-ssh',              '~> 2.0'
+  spec.add_dependency 'net-sftp',             '~> 2.0'
   spec.add_dependency 'rubyzip',              '~> 0.9'
   spec.add_dependency 'highline',             '~> 1.4'
   spec.add_dependency 'Antwrap',              '~> 0.7'

Modified: incubator/buildr/trunk/lib/buildr/core/common.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/core/common.rb?rev=652955&r1=652954&r2=652955&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/core/common.rb (original)
+++ incubator/buildr/trunk/lib/buildr/core/common.rb Fri May  2 17:07:41 2008
@@ -18,7 +18,6 @@
 require 'tempfile'
 require 'open-uri'
 $LOADED_FEATURES << 'rubygems/open-uri.rb' # avoid loading rubygems' open-uri
-require 'uri/open-sftp'
 require 'buildr/core/util'
 require 'buildr/core/transports'
 

Modified: incubator/buildr/trunk/lib/buildr/core/transports.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/core/transports.rb?rev=652955&r1=652954&r2=652955&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/core/transports.rb (original)
+++ incubator/buildr/trunk/lib/buildr/core/transports.rb Fri May  2 17:07:41 2008
@@ -20,7 +20,6 @@
 require 'net/ssh'
 require 'net/sftp'
 require 'uri'
-require 'uri/sftp'
 require 'digest/md5'
 require 'digest/sha1'
 require 'tempfile'
@@ -342,7 +341,10 @@
   end
 
 
-  class SFTP #:nodoc:
+  class SFTP < Generic #:nodoc:
+
+    DEFAULT_PORT = 22
+    COMPONENT = [ :scheme, :userinfo, :host, :port, :path ].freeze
 
     class << self
       # Caching of passwords, so we only need to ask once.
@@ -351,16 +353,40 @@
       end
     end
 
-  protected
+    def initialize(*arg)
+      super
+    end
 
-    def write_internal(options, &block) #:nodoc:
+    def read(options = {}, &block)
       # SSH options are based on the username/password from the URI.
-      ssh_options = { :port=>port, :username=>user, :password=>password }.merge(options[:ssh_options] || {})
+      ssh_options = { :port=>port, :password=>password }.merge(options[:ssh_options] || {})
       ssh_options[:password] ||= SFTP.passwords[host]
       begin
         puts "Connecting to #{host}" if Buildr.application.options.trace
-        session = Net::SSH.start(host, ssh_options)
-        SFTP.passwords[host] = ssh_options[:password]
+        result = nil
+        Net::SFTP.start(host, user, ssh_options) do |sftp|
+          SFTP.passwords[host] = ssh_options[:password]
+          puts 'connected' if Buildr.application.options.trace
+
+          with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
+            puts "Downloading to #{path}" if Buildr.application.options.trace
+            sftp.file.open(path, 'r') do |file|
+              if block
+                while chunk = file.read(32 * 4096)
+                  block.call chunk
+                  progress << chunk
+                end
+              else
+                result = ''
+                while chunk = file.read(32 * 4096)
+                  result << chunk
+                  progress << chunk
+                end
+              end
+            end
+          end
+        end
+        return result
       rescue Net::SSH::AuthenticationFailed=>ex
         # Only if running with console, prompt for password.
         if !ssh_options[:password] && $stdout.isatty
@@ -370,39 +396,55 @@
         end
         raise
       end
+    end
 
-      session.sftp.connect do |sftp|
-        puts 'connected' if Buildr.application.options.trace
+  protected
 
-        # To create a path, we need to create all its parent. We use realpath to determine if
-        # the path already exists, otherwise mkdir fails.
-        puts "Creating path #{path}" if Buildr.application.options.trace
-        File.dirname(path).split('/').inject('') do |base, part|
-          combined = base + part
-          sftp.realpath combined rescue sftp.mkdir combined, {}
-          "#{combined}/"
-        end
+    def write_internal(options, &block) #:nodoc:
+      # SSH options are based on the username/password from the URI.
+      ssh_options = { :port=>port, :password=>password }.merge(options[:ssh_options] || {})
+      ssh_options[:password] ||= SFTP.passwords[host]
+      begin
+        puts "Connecting to #{host}" if Buildr.application.options.trace
+        Net::SFTP.start(host, user, ssh_options) do |sftp|
+          SFTP.passwords[host] = ssh_options[:password]
+          puts 'connected' if Buildr.application.options.trace
+
+          # To create a path, we need to create all its parent. We use realpath to determine if
+          # the path already exists, otherwise mkdir fails.
+          puts "Creating path #{path}" if Buildr.application.options.trace
+          File.dirname(path).split('/').inject('') do |base, part|
+            combined = base + part
+            sftp.realpath combined rescue sftp.mkdir combined, {}
+            "#{combined}/"
+          end
 
-        with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
-          puts "Uploading to #{path}" if Buildr.application.options.trace
-          sftp.open_handle(path, 'w') do |handle|
-            # Writing in chunks gives us the benefit of a progress bar,
-            # but also require that we maintain a position in the file,
-            # since write() with two arguments always writes at position 0.
-            pos = 0
-            while chunk = yield(32 * 4096)
-              sftp.write(handle, chunk, pos)
-              pos += chunk.size
-              progress << chunk
+          with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
+            puts "Uploading to #{path}" if Buildr.application.options.trace
+            sftp.file.open(path, 'w') do |file|
+              while chunk = yield(32 * 4096)
+                file.write chunk
+                progress << chunk
+              end
+              sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
             end
-            sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
           end
         end
+      rescue Net::SSH::AuthenticationFailed=>ex
+        # Only if running with console, prompt for password.
+        if !ssh_options[:password] && $stdout.isatty
+          password = ask("Password for #{host}:") { |q| q.echo = '*' }
+          ssh_options[:password] = password
+          retry
+        end
+        raise
       end
     end
 
   end
 
+  @@schemes['SFTP'] = SFTP
+
 
   # File URL. Keep in mind that file URLs take the form of <code>file://host/path</code>, although the host
   # is not used, so typically all you will see are three backslashes. This methods accept common variants,

Modified: incubator/buildr/trunk/spec/transport_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/transport_spec.rb?rev=652955&r1=652954&r2=652955&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/transport_spec.rb (original)
+++ incubator/buildr/trunk/spec/transport_spec.rb Fri May  2 17:07:41 2008
@@ -298,3 +298,106 @@
     URI("http://john:secret@#{@host_domain}").read
   end
 end
+
+
+describe URI::HTTP, '#write' do
+end
+
+
+describe URI::SFTP, '#write' do
+  before do
+    @uri = URI('sftp://john:secret@localhost/path/readme')
+    @content = 'Readme. Please!'
+
+    @ssh_session = mock('Net::SSH::Session')
+    @sftp_session = mock('Net::SFTP::Session')
+    @file_factory = mock('Net::SFTP::Operations::FileFactory')
+    Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
+      Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
+      @sftp_session.should_receive(:connect!).and_return(@sftp_session)
+      @sftp_session.should_receive(:loop)
+      @sftp_session.stub!(:mkdir)
+      @sftp_session.should_receive(:file).with.and_return(@file_factory)
+      @file_factory.stub!(:open)
+      @ssh_session.should_receive(:close)
+      @ssh_session
+    end
+  end
+
+  it 'should open connection to SFTP server' do
+    @uri.write @content
+  end
+
+  it 'should create path recursively on SFTP server' do
+    @sftp_session.should_receive(:mkdir).ordered.with('', {})
+    @sftp_session.should_receive(:mkdir).ordered.with('/path', {})
+    @uri.write @content
+  end
+
+  it 'should only create paths that don\'t exist' do
+    @sftp_session.should_receive(:realpath).any_number_of_times
+    @sftp_session.should_not_receive(:mkdir)
+    @uri.write @content
+  end
+
+  it 'should open file for writing' do
+    @file_factory.should_receive(:open).with('/path/readme', 'w')
+    @uri.write @content
+  end
+
+  it 'should write contents to file' do
+    file = mock('Net::SFTP::Operations::File')
+    file.should_receive(:write).with(@content)
+    @file_factory.should_receive(:open).with('/path/readme', 'w').and_yield(file)
+    @uri.write @content
+  end
+
+end
+
+
+describe URI::SFTP, '#read' do
+  before do
+    @uri = URI('sftp://john:secret@localhost/path/readme')
+    @content = 'Readme. Please!'
+
+    @ssh_session = mock('Net::SSH::Session')
+    @sftp_session = mock('Net::SFTP::Session')
+    @file_factory = mock('Net::SFTP::Operations::FileFactory')
+    Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
+      Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
+      @sftp_session.should_receive(:connect!).and_return(@sftp_session)
+      @sftp_session.should_receive(:loop)
+      @sftp_session.should_receive(:file).with.and_return(@file_factory)
+      @file_factory.stub!(:open)
+      @ssh_session.should_receive(:close)
+      @ssh_session
+    end
+  end
+
+  it 'should open connection to SFTP server' do
+    @uri.read
+  end
+
+  it 'should open file for reading' do
+    @file_factory.should_receive(:open).with('/path/readme', 'r')
+    @uri.read
+  end
+
+  it 'should read contents of file and return it' do
+    file = mock('Net::SFTP::Operations::File')
+    file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
+    @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
+    @uri.read.should eql(@content)
+  end
+
+  it 'should read contents of file and pass it to block' do
+    file = mock('Net::SFTP::Operations::File')
+    file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
+    @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
+    content = ''
+    @uri.read do |chunk|
+      content << chunk
+    end
+    content.should eql(@content)
+  end
+end