lib/core/transports.rb in buildr-0.18.0 vs lib/core/transports.rb in buildr-0.19.0
- old
+ new
@@ -10,50 +10,85 @@
# Monkeypatching: SFTP never defines the mkdir method on its session or the underlying
# driver, it just redirect calls through method_missing. Rake, on the other hand, decides
# to define mkdir on Object, and so routes our calls to FileUtils.
-class Net::SFTP::Session
- def mkdir(path, attrs = {})
- method_missing :mkdir, path, attrs
+module Net #:nodoc:all
+ class Session
+ def mkdir(path, attrs = {})
+ method_missing :mkdir, path, attrs
+ end
end
-end
-class Net::SFTP::Protocol::Driver
- def mkdir(first, path, attrs = {})
- method_missing :mkdir, first, path, attrs
+ class SFTP::Protocol::Driver
+ def mkdir(first, path, attrs = {})
+ method_missing :mkdir, first, path, attrs
+ end
end
end
module Buildr
+
+ # Transports are used for downloading artifacts from remote repositories, uploading
+ # artifacts to deployment repositories, and anything else you need to move around.
+ #
+ # The HTTP transport is used for all URLs with the scheme http or https. You can only
+ # use the HTTP transport to download artifacts.
+ #
+ # The SFTP transport is used for all URLs with the schema sftp. You can only use the
+ # SFTP transport to upload artifacts.
+ #
+ # The SFTP transport supports the following options:
+ # * :username -- The username.
+ # * :password -- A password. If unspecified, you will be prompted to enter a password.
+ # * :permissions -- Permissions to set on the uploaded file.
+ # You can also pass the username/password in the URL.
+ #
+ # The SFTP transport will automatically create MD5 and SHA1 digest files for each file
+ # it uploads.
module Transports
+ # Indicates the requested resource was not found.
class NotFound < Exception
end
- # Perform one or more operations using an open connection to the
- # specified URL. For examples, see Transport#download and Transport#upload.
- def self.perform(url, options = nil, &block)
- uri = URI.parse(url.to_s)
- const_get(uri.scheme.upcase).perform(uri, options, &block)
- end
+ class << self
- # Convenience method for downloading a single file from the specified
- # URL to the target file.
- def self.download(url, target, options = nil)
- uri = URI.parse(url.to_s)
- path, uri.path = uri.path, ""
- const_get(uri.scheme.upcase).perform(uri, options) do |transport|
- transport.download(path, target)
+ # :call-seq:
+ # perform(url, options?) { |transport| ... }
+ #
+ # Perform one or more operations using an open connection to the
+ # specified URL. For examples, see Transport#download and Transport#upload.
+ def perform(url, options = nil, &block)
+ uri = URI.parse(url.to_s)
+ const_get(uri.scheme.upcase).perform(uri, options, &block)
end
+
+ # :call-seq:
+ # download(url, target, options?)
+ #
+ # Convenience method for downloading a single file from the specified
+ # URL to the target file.
+ def download(url, target, options = nil)
+ uri = URI.parse(url.to_s)
+ path, uri.path = uri.path, ""
+ const_get(uri.scheme.upcase).perform(uri, options) do |transport|
+ transport.download(path, target)
+ end
+ end
+
end
+ # Extend this class if you are implementing a new transport.
class Transport
class << self
+ # :call-seq:
+ # perform(url, options?) { |transport| ... }
+ #
# Perform one or more operations using an open connection to the
# specified URL. For examples, see #download and #upload.
def perform(url, options = nil)
instance = new(url, options)
begin
@@ -69,10 +104,11 @@
# The base path on the server, always ending with a slash.
attr_reader :base_path
# Options passed during construction.
attr_reader :options
+ # Initialize the transport with the specified URL and options.
def initialize(url, options)
@uri = URI.parse(url.to_s)
@base_path = @uri.path || "/"
@base_path += "/" unless @base_path[-1] == ?/
@options = options || {}
@@ -136,14 +172,14 @@
else
truncated = file_name
end
progress_bar.format = "#{truncated}: %3d%% %s %s/%s %s"
progress_bar.format = "%3d%% %s %s/%s %s"
- progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
+ progress_bar.format_arguments = [:percentage, :bar, :bytes, :total, :stat]
progress_bar.bar_mark = "."
-
+
begin
class << progress_bar
def <<(bytes)
inc bytes.respond_to?(:size) ? bytes.size : bytes
end
@@ -192,11 +228,11 @@
digester = Digester.new(types)
yield digester
digester.to_hash
end
- class Digester
+ class Digester #:nodoc:
def initialize(types)
types ||= [ "md5", "sha1" ]
@digests = types.inject({}) do |hash, type|
hash[type.to_s.downcase] = Digest.const_get(type.to_s.upcase).new
@@ -208,11 +244,11 @@
def <<(bytes)
@digests.each { |type, digest| digest << bytes }
end
# Iterate over all the digests calling the block with two arguments:
- # the digest type (e.g. "md5") and the hexadecimal digest value.
+ # the digest type (e.g. "md5") and the hexadecimal digest value.
def each()
@digests.each { |type, digest| yield type, digest.hexdigest }
end
# Returns a hash that maps each digest type to its hexadecimal digest value.
@@ -226,11 +262,11 @@
end
end
- class HTTP < Transport
+ class HTTP < Transport #:nodoc:
def initialize(url, options)
super
@http = Net::HTTP.start(@uri.host, @uri.port)
end
@@ -252,11 +288,11 @@
download = proc do |write|
# Read the body of the page and write it out.
response.read_body do |chunk|
write[chunk]
digester << chunk
- progress << chunk
+ progress << chunk
end
# Check server digests before approving the download.
digester.each do |type, hexdigest|
@http.request_get("#{@base_path}#{path}.#{type.to_s.downcase}") do |response|
if Net::HTTPOK === response
@@ -265,11 +301,11 @@
response.read_body.split.first == hexdigest
end
end
end
end
-
+
if target
# If download breaks we end up with a partial file which is
# worse than not having a file at all, so download to temporary
# file and then move over.
temp = Tempfile.new(File.basename(target))
@@ -295,23 +331,27 @@
@http = nil
end
end
+ # Use the HTTP transport for HTTPS connections.
+ HTTPS = HTTP #:nodoc:
- class SFTP < Transport
+ class SFTP < Transport #:nodoc:
+
class << self
def passwords()
@passwords ||= {}
end
end
attr_reader :sftp
def initialize(url, options)
super
+ @permissions = options.delete :permissions
# SSH options are based on the username/password from the URI.
ssh_options = { :port=>@uri.port, :username=>@uri.user }.merge(options || {})
ssh_options[:password] ||= SFTP.passwords[@uri.host]
begin
puts "Connecting to #{@uri.host}" if Rake.application.options.trace
@@ -332,44 +372,49 @@
def upload(source, path)
File.open(source) do |file|
with_progress_bar path.split("/").last, File.size(source) do |progress|
with_digests(@options[:digests]) do |digester|
- puts "Uploading to #{@base_path}#{path}" if Rake.application.options.trace
- @sftp.open_handle(@base_path + path, "w") do |handle|
+ target_path = "#{@base_path}#{path}"
+ puts "Uploading to #{target_path}" if Rake.application.options.trace
+ @sftp.open_handle(target_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 = file.read(32 * 4096)
+ while chunk = file.read(32 * 4096)
@sftp.write(handle, chunk, pos)
pos += chunk.size
digester << chunk
progress << chunk
end
end
+ @sftp.setstat(target_path, :permissions => @permissions) if @permissions
# Upload all the digests.
digester.each do |type, hexdigest|
- puts "Uploading signature to #{@base_path}#{path}.#{type}" if Rake.application.options.trace
- @sftp.open_handle("#{@base_path}#{path}.#{type}", "w") do |handle|
+ digest_file = "#{@base_path}#{path}.#{type}"
+ puts "Uploading signature to #{digest_file}" if Rake.application.options.trace
+ @sftp.open_handle(digest_file, "w") do |handle|
@sftp.write(handle, "#{hexdigest} #{path}")
end
+ @sftp.setstat(digest_file, :permissions => @permissions) if @permissions
end
end
- end
+ end
end
end
def mkpath(path)
# 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(@base_path) do |base, part|
- @sftp.realpath(base+part) rescue @sftp.mkdir base + part
- "#{base}#{part}/"
+ combined = base + part
+ @sftp.realpath combined rescue @sftp.mkdir combined, {}
+ "#{combined}/"
end
end
def close()
@sftp.close