lib/core/transports.rb in buildr-1.2.0 vs lib/core/transports.rb in buildr-1.2.1

- old
+ new

@@ -54,11 +54,10 @@ # end # Shorter version: # File.open("image.jpg", "w") { |file| file.write URI.read("http://example.com/image.jpg") } # # Supported options: - # * :proxy -- Collection of proxy settings, accessed by scheme. # * :modified -- Only download if file modified since this timestamp. Returns nil if not modified. # * :progress -- Show the progress bar while reading. def read(uri, options = nil, &block) uri = URI.parse(uri.to_s) unless URI === uri uri.read options, &block @@ -92,11 +91,10 @@ # end # Or: # write "sftp://localhost/jars/killer-app.jar", File.read("killer-app.jar") # # Supported options: - # * :proxy -- Collection of proxy settings, accessed by scheme. # * :progress -- Show the progress bar while reading. def write(uri, *args, &block) uri = URI.parse(uri.to_s) unless URI === uri uri.write *args, &block end @@ -174,11 +172,23 @@ # that responds to +read+ and optionally +size+. The second form writes the content by yielding to the # block. Each yield should return up to the specified number of bytes, the last yield returns nil. # # For options, see URI::write. def write(*args, &block) - fail "This protocol doesn't support writing (yet, how about helping by implementing it?)" + options = args.pop if Hash === args.last + options ||= {} + if String === args.first + ios = StringIO.new(args.first, "r") + write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) } + elsif args.first.respond_to?(:read) + size = args.first.size rescue nil + write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) } + elsif args.empty? && block + write_internal options, &block + else + raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?" + end end # :call-seq: # upload(source, options?) # @@ -272,12 +282,30 @@ end yield progress_bar end end + # :call-seq: + # proxy_uri() => URI? + # + # Returns the proxy server to use. Obtains the proxy from the relevant environment variable (e.g. HTTP_PROXY). + # Supports exclusions based on host name and port number from environment variable NO_PROXY. + def proxy_uri() + proxy = ENV["#{scheme.upcase}_PROXY"] + proxy = URI.parse(proxy) if String === proxy + excludes = (ENV["NO_PROXY"] || "").split(/\s*,\s*/).compact + excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" } + return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") } + end + + def write_internal(options, &block) #:nodoc: + fail "This protocol doesn't support writing (yet, how about helping by implementing it?)" + end + end + class HTTP #:nodoc: # See URI::Generic#read def read(options = nil, &block) options ||= {} @@ -320,90 +348,78 @@ raise RuntimeError, "Failed to download #{self}: #{response.message}" end end end - proxy = options[:proxy] && options[:proxy].http - if proxy + if proxy = proxy_uri proxy = URI.parse(proxy) if String === proxy Net::HTTP.start(host, port, proxy.host, proxy.port, proxy.user, proxy.password) { |http| request[http] } else Net::HTTP.start(host, port) { |http| request[http] } end result end end + class SFTP #:nodoc: class << self # Caching of passwords, so we only need to ask once. def passwords() @passwords ||= {} end end - # See URI::Generic#write - def write(*args, &block) - options = args.pop if Hash === args.last - options ||= {} - if String === args.first - ios = StringIO.new(args.first, "r") - write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) } - elsif args.first.respond_to?(:read) - size = args.first.size rescue nil - write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) } - elsif args.empty? && block + protected - # SSH options are based on the username/password from the URI. - ssh_options = { :port=>port, :username=>user }.merge(options[:ssh_options] || {}) - ssh_options[:password] ||= SFTP.passwords[host] - begin - puts "Connecting to #{host}" if Rake.application.options.trace - session = Net::SSH.start(host, ssh_options) - SFTP.passwords[host] = ssh_options[:password] - rescue Net::SSH::AuthenticationFailed=>ex - # Only if running with console, prompt for password. - if !ssh_options[:password] && $stdout.isatty - password = HighLine.new.ask("Password for #{host}:") { |q| q.echo = "*" } - ssh_options[:password] = password - retry - end - raise + def write_internal(options, &block) #:nodoc: + # SSH options are based on the username/password from the URI. + ssh_options = { :port=>port, :username=>user }.merge(options[:ssh_options] || {}) + ssh_options[:password] ||= SFTP.passwords[host] + begin + puts "Connecting to #{host}" if Rake.application.options.trace + session = Net::SSH.start(host, ssh_options) + SFTP.passwords[host] = ssh_options[:password] + rescue Net::SSH::AuthenticationFailed=>ex + # Only if running with console, prompt for password. + if !ssh_options[:password] && $stdout.isatty + password = HighLine.new.ask("Password for #{host}:") { |q| q.echo = "*" } + ssh_options[:password] = password + retry end + raise + end - session.sftp.connect do |sftp| - puts "connected" if Rake.application.options.trace + session.sftp.connect do |sftp| + puts "connected" if Rake.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 #{@base_path}" if Rake.application.options.trace - path.split("/").inject("") do |base, part| - combined = base + part - sftp.realpath combined rescue sftp.mkdir combined, {} - "#{combined}/" - end + # 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 #{@base_path}" if Rake.application.options.trace + 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 Rake.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 - end - sftp.setstat(target_path, :permissions => options[:permissions]) if options[:permissions] + with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress| + puts "Uploading to #{path}" if Rake.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 end + sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions] end end - else - raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?" end end end @@ -450,50 +466,38 @@ block ? block.call(input.read) : input.read end end end - # See URI::Generic#write - def write(*args, &block) - options = args.pop if Hash === args.last - options ||= {} - raise ArgumentError, "Either you're attempting to write a file to another host (which we don't support), or you used two slashes by mistake, where you should have file:///<path>." unless host.blank? - - if String === args.first - ios = StringIO.new(args.first, "r") - write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) } - elsif args.first.respond_to?(:read) - size = args.first.size rescue nil - write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) } - elsif args.empty? && block - temp = nil - Tempfile.open File.basename(path) do |temp| - temp.binmode - with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress| - while chunk = yield(32 * 4096) - temp.write chunk - progress << chunk - end - end - end - real_path.tap do |path| - mkpath File.dirname(path) - File.move temp.path, path - end - else - raise ArgumentError, "Either give me the content, or pass me a block, otherwise what would I upload?" - end - end - def to_s() "file://#{host}#{path}" end # The URL path always starts with a backslash. On most operating systems (Linux, Darwin, BSD) it points # to the absolute path on the file system. But on Windows, it comes before the drive letter, creating an # unusable path, so real_path fixes that. Ugly but necessary hack. def real_path() #:nodoc: RUBY_PLATFORM =~ /win32/ && path =~ /^\/[a-zA-Z]:\// ? path[1..-1] : path + end + + protected + + def write_internal(options, &block) #:nodoc: + raise ArgumentError, "Either you're attempting to write a file to another host (which we don't support), or you used two slashes by mistake, where you should have file:///<path>." unless host.blank? + temp = nil + Tempfile.open File.basename(path) do |temp| + temp.binmode + with_progress_bar options[:progress] && options[:size], path.split("/"), options[:size] || 0 do |progress| + while chunk = yield(32 * 4096) + temp.write chunk + progress << chunk + end + end + end + real_path.tap do |path| + mkpath File.dirname(path) + File.move temp.path, path + end end @@schemes["FILE"] = FILE end