summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Rakefile14
-rw-r--r--lib/bundler/cli.rb24
-rw-r--r--lib/bundler/dsl.rb2
-rw-r--r--lib/bundler/fetcher.rb6
-rw-r--r--lib/bundler/vendor/net/http/persistent.rb174
-rw-r--r--lib/bundler/vendor/thor.rb2
-rw-r--r--lib/bundler/vendor/thor/base.rb19
-rw-r--r--lib/bundler/vendor/thor/error.rb4
-rw-r--r--spec/bundler/dsl_spec.rb4
-rw-r--r--spec/other/binstubs_spec.rb24
10 files changed, 199 insertions, 74 deletions
diff --git a/Rakefile b/Rakefile
index 34e26e6022..e15adced54 100644
--- a/Rakefile
+++ b/Rakefile
@@ -220,18 +220,4 @@ end
task :build => ["man:clean", "man:build"]
task :release => ["man:clean", "man:build"]
-namespace :vendor do
- desc "Build the vendor dir"
- task :build => :clean do
- sh "git clone git://github.com/wycats/thor.git lib/bundler/vendor/tmp"
- sh "mv lib/bundler/vendor/tmp/lib/* lib/bundler/vendor/"
- rm_rf "lib/bundler/vendor/tmp"
- end
-
- desc "Clean the vendor dir"
- task :clean do
- rm_rf "lib/bundler/vendor"
- end
-end
-
task :default => :spec
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 137a3e13f7..5953f5a025 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -379,18 +379,26 @@ module Bundler
"binstub destination directory (default bin)"
method_option "force", :type => :boolean, :default => false, :banner =>
"overwrite existing binstubs if they exist"
- def binstubs(gem_name)
+ def binstubs(*gems)
Bundler.definition.validate_ruby!
Bundler.settings[:bin] = options["path"] if options["path"]
Bundler.settings[:bin] = nil if options["path"] && options["path"].empty?
installer = Installer.new(Bundler.root, Bundler.definition)
- spec = installer.specs.find{|s| s.name == gem_name }
- raise GemNotFound, not_found_message(gem_name, Bundler.definition.specs) unless spec
- if spec.name == "bundler"
- Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
- else
- installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
+ if gems.empty?
+ Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
+ exit 1
+ end
+
+ gems.each do |gem_name|
+ spec = installer.specs.find{|s| s.name == gem_name }
+ raise GemNotFound, not_found_message(gem_name, Bundler.definition.specs) unless spec
+
+ if spec.name == "bundler"
+ Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
+ else
+ installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
+ end
end
end
@@ -485,6 +493,7 @@ module Bundler
desc "package", "Locks and then caches all of the gems into vendor/cache"
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
+ method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
long_desc <<-D
The package command will copy the .gem files for every gem in the bundle into the
directory ./vendor/cache. If you then check that directory into your source
@@ -492,6 +501,7 @@ module Bundler
bundle without having to download any additional gems.
D
def package
+ Bundler.ui.level = "warn" if options[:quiet]
setup_cache_all
install
# TODO: move cache contents here now that all bundles are locked
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 947aca7ba8..7f88a1bc5d 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -230,7 +230,7 @@ module Bundler
if github = opts.delete("github")
github = "#{github}/#{github}" unless github.include?("/")
- opts["git"] = "git://github.com/#{github}.git"
+ opts["git"] = "https://github.com/#{github}.git"
end
if gist = opts.delete("gist")
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 9c8e155ce1..9f6c99a137 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -209,11 +209,11 @@ module Bundler
begin
Bundler.ui.debug "Fetching from: #{uri}"
+ req = Net::HTTP::Get.new uri.request_uri
+ req.basic_auth(uri.user, uri.password) if uri.user && uri.password
if defined?(Net::HTTP::Persistent)
- response = @connection.request(uri)
+ response = @connection.request(uri, req)
else
- req = Net::HTTP::Get.new uri.request_uri
- req.basic_auth(uri.user, uri.password) if uri.user && uri.password
response = @connection.request(req)
end
rescue OpenSSL::SSL::SSLError
diff --git a/lib/bundler/vendor/net/http/persistent.rb b/lib/bundler/vendor/net/http/persistent.rb
index dcdc5b53cb..f99f0625ed 100644
--- a/lib/bundler/vendor/net/http/persistent.rb
+++ b/lib/bundler/vendor/net/http/persistent.rb
@@ -1,5 +1,9 @@
require 'net/http'
-require 'net/https'
+begin
+ require 'net/https'
+rescue LoadError
+ # net/https or openssl
+end if RUBY_VERSION < '1.9' # but only for 1.8
require 'net/http/faster'
require 'uri'
require 'cgi' # for escaping
@@ -9,6 +13,8 @@ begin
rescue LoadError
end
+autoload :OpenSSL, 'openssl'
+
##
# Persistent connections for Net::HTTP
#
@@ -37,6 +43,11 @@ end
# # perform a GET
# response = http.request uri
#
+# # or
+#
+# get = Net::HTTP::Get.new uri.request_uri
+# response = http.request get
+#
# # create a POST
# post_uri = uri + 'create'
# post = Net::HTTP::Post.new post_uri.path
@@ -45,6 +56,10 @@ end
# # perform the POST, the URI is always required
# response http.request post_uri, post
#
+# Note that for GET, HEAD and other requests that do not have a body you want
+# to use URI#request_uri not URI#path. The request_uri contains the query
+# params which are sent in the body for other requests.
+#
# == SSL
#
# SSL connections are automatically created depending upon the scheme of the
@@ -105,6 +120,13 @@ end
# The amount of time allowed between reading two chunks from the socket. Set
# through #read_timeout
#
+# === Max Requests
+#
+# The number of requests that should be made before opening a new connection.
+# Typically many keep-alive capable servers tune this to 100 or less, so the
+# 101st request will fail with ECONNRESET. If unset (default), this value has no
+# effect, if set, connections will be reset on the request after max_requests.
+#
# === Open Timeout
#
# The amount of time to wait for a connection to be opened. Set through
@@ -174,9 +196,29 @@ class Net::HTTP::Persistent
EPOCH = Time.at 0 # :nodoc:
##
+ # Is OpenSSL available? This test works with autoload
+
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
+
+ ##
# The version of Net::HTTP::Persistent you are using
- VERSION = '2.8'
+ VERSION = '2.9'
+
+ ##
+ # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
+ # the exception list for ruby 1.x.
+
+ RETRIED_EXCEPTIONS = [ # :nodoc:
+ (Net::ReadTimeout if Net.const_defined? :ReadTimeout),
+ IOError,
+ EOFError,
+ Errno::ECONNRESET,
+ Errno::ECONNABORTED,
+ Errno::EPIPE,
+ (OpenSSL::SSL::SSLError if HAVE_OPENSSL),
+ Timeout::Error,
+ ].compact
##
# Error class for errors raised by Net::HTTP::Persistent. Various
@@ -226,6 +268,8 @@ class Net::HTTP::Persistent
$stderr.puts "sleeping #{sleep_time}" if $DEBUG
sleep sleep_time
end
+ rescue
+ # ignore StandardErrors, we've probably found the idle timeout.
ensure
http.shutdown
@@ -288,6 +332,12 @@ class Net::HTTP::Persistent
attr_accessor :idle_timeout
##
+ # Maximum number of requests on a connection before it is considered expired
+ # and automatically closed.
+
+ attr_accessor :max_requests
+
+ ##
# The value sent in the Keep-Alive header. Defaults to 30. Not needed for
# HTTP/1.1 servers.
#
@@ -442,6 +492,7 @@ class Net::HTTP::Persistent
@open_timeout = nil
@read_timeout = nil
@idle_timeout = 5
+ @max_requests = nil
@socket_options = []
@socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
@@ -458,15 +509,22 @@ class Net::HTTP::Persistent
@private_key = nil
@ssl_version = nil
@verify_callback = nil
- @verify_mode = OpenSSL::SSL::VERIFY_PEER
+ @verify_mode = nil
@cert_store = nil
@generation = 0 # incremented when proxy URI changes
@ssl_generation = 0 # incremented when SSL session variables change
- @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
+
+ if HAVE_OPENSSL then
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
+ end
@retry_change_requests = false
+ @ruby_1 = RUBY_VERSION < '2'
+ @retried_on_ruby_2 = !@ruby_1
+
self.proxy = proxy if proxy
end
@@ -536,6 +594,9 @@ class Net::HTTP::Persistent
use_ssl = uri.scheme.downcase == 'https'
if use_ssl then
+ raise Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless
+ HAVE_OPENSSL
+
ssl_generation = @ssl_generation
ssl_cleanup ssl_generation
@@ -606,10 +667,12 @@ class Net::HTTP::Persistent
end
##
- # Returns true if the connection should be reset due to an idle timeout,
- # false otherwise.
+ # Returns true if the connection should be reset due to an idle timeout, or
+ # maximum request count, false otherwise.
def expired? connection
+ requests = Thread.current[@request_key][connection.object_id]
+ return true if @max_requests && requests >= @max_requests
return false unless @idle_timeout
return true if @idle_timeout.zero?
@@ -679,10 +742,15 @@ class Net::HTTP::Persistent
end
##
- # Is the request idempotent or is retry_change_requests allowed
+ # Is the request +req+ idempotent or is retry_change_requests allowed.
+ #
+ # If +retried_on_ruby_2+ is true, true will be returned if we are on ruby,
+ # retry_change_requests is allowed and the request is not idempotent.
- def can_retry? req
- retry_change_requests or idempotent?(req)
+ def can_retry? req, retried_on_ruby_2 = false
+ return @retry_change_requests && !idempotent?(req) if retried_on_ruby_2
+
+ @retry_change_requests || idempotent?(req)
end
if RUBY_VERSION > '1.9' then
@@ -901,31 +969,14 @@ class Net::HTTP::Persistent
#
# +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
#
- # If there is an error and the request is idempontent according to RFC 2616
+ # If there is an error and the request is idempotent according to RFC 2616
# it will be retried automatically.
def request uri, req = nil, &block
retried = false
bad_response = false
- req = Net::HTTP::Get.new uri.request_uri unless req
-
- @headers.each do |pair|
- req.add_field(*pair)
- end
-
- if uri.user or uri.password
- req.basic_auth uri.user, uri.password
- end
-
- @override_headers.each do |name, value|
- req[name] = value
- end
-
- unless req['Connection'] then
- req.add_field 'Connection', 'keep-alive'
- req.add_field 'Keep-Alive', @keep_alive
- end
+ req = request_setup req || uri
connection = connection_for uri
connection_id = connection.object_id
@@ -950,23 +1001,25 @@ class Net::HTTP::Persistent
bad_response = true
retry
- rescue IOError, EOFError, Timeout::Error,
- Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
- Errno::EINVAL, OpenSSL::SSL::SSLError => e
-
- if retried or not can_retry? req
- due_to = "(due to #{e.message} - #{e.class})"
- message = error_message connection
+ rescue *RETRIED_EXCEPTIONS => e # retried on ruby 2
+ request_failed e, req, connection if
+ retried or not can_retry? req, @retried_on_ruby_2
- finish connection
+ reset connection
- raise Error, "too many connection resets #{due_to} #{message}"
- end
+ retried = true
+ retry
+ rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2
+ request_failed e, req, connection if retried or not can_retry? req
reset connection
retried = true
retry
+ rescue Exception => e
+ finish connection
+
+ raise
ensure
Thread.current[@timeout_key][connection_id] = Time.now
end
@@ -977,6 +1030,51 @@ class Net::HTTP::Persistent
end
##
+ # Raises an Error for +exception+ which resulted from attempting the request
+ # +req+ on the +connection+.
+ #
+ # Finishes the +connection+.
+
+ def request_failed exception, req, connection # :nodoc:
+ due_to = "(due to #{exception.message} - #{exception.class})"
+ message = "too many connection resets #{due_to} #{error_message connection}"
+
+ finish connection
+
+
+ raise Error, message, exception.backtrace
+ end
+
+ ##
+ # Creates a GET request if +req_or_uri+ is a URI and adds headers to the
+ # request.
+ #
+ # Returns the request.
+
+ def request_setup req_or_uri # :nodoc:
+ req = if URI === req_or_uri then
+ Net::HTTP::Get.new req_or_uri.request_uri
+ else
+ req_or_uri
+ end
+
+ @headers.each do |pair|
+ req.add_field(*pair)
+ end
+
+ @override_headers.each do |name, value|
+ req[name] = value
+ end
+
+ unless req['Connection'] then
+ req.add_field 'Connection', 'keep-alive'
+ req.add_field 'Keep-Alive', @keep_alive
+ end
+
+ req
+ end
+
+ ##
# Shuts down all connections for +thread+.
#
# Uses the current thread by default.
diff --git a/lib/bundler/vendor/thor.rb b/lib/bundler/vendor/thor.rb
index 9eeab586aa..6880b6d7ae 100644
--- a/lib/bundler/vendor/thor.rb
+++ b/lib/bundler/vendor/thor.rb
@@ -421,7 +421,7 @@ class Thor
possibilities = find_command_possibilities(meth)
if possibilities.size > 1
- raise ArgumentError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
+ raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
elsif possibilities.size < 1
meth = meth || default_command
elsif map[meth]
diff --git a/lib/bundler/vendor/thor/base.rb b/lib/bundler/vendor/thor/base.rb
index e33d852e2f..272dae417b 100644
--- a/lib/bundler/vendor/thor/base.rb
+++ b/lib/bundler/vendor/thor/base.rb
@@ -474,10 +474,10 @@ class Thor
alias handle_no_task_error handle_no_command_error
def handle_argument_error(command, error, args, arity) #:nodoc:
- msg = "ERROR: #{basename} #{command.name} was called with "
+ msg = "ERROR: \"#{basename} #{command.name}\" was called with "
msg << 'no arguments' if args.empty?
msg << 'arguments ' << args.inspect if !args.empty?
- msg << "\nUsage: #{self.banner(command).inspect}."
+ msg << "\nUsage: #{self.banner(command).inspect}"
raise InvocationError, msg
end
@@ -603,13 +603,16 @@ class Thor
else
value = superclass.send(method)
- if value
- if value.is_a?(TrueClass) || value.is_a?(Symbol)
- value
- else
- value.dup
- end
+ # Ruby implements `dup` on Object, but raises a `TypeError`
+ # if the method is called on immediates. As a result, we
+ # don't have a good way to check whether dup will succeed
+ # without calling it and rescuing the TypeError.
+ begin
+ value.dup
+ rescue TypeError
+ value
end
+
end
end
diff --git a/lib/bundler/vendor/thor/error.rb b/lib/bundler/vendor/thor/error.rb
index 31e0c4bc5c..3174c57eac 100644
--- a/lib/bundler/vendor/thor/error.rb
+++ b/lib/bundler/vendor/thor/error.rb
@@ -13,6 +13,10 @@ class Thor
end
UndefinedTaskError = UndefinedCommandError
+ class AmbiguousCommandError < Error
+ end
+ AmbiguousTaskError = AmbiguousCommandError
+
# Raised when a command was found, but not invoked properly.
class InvocationError < Error
end
diff --git a/spec/bundler/dsl_spec.rb b/spec/bundler/dsl_spec.rb
index 610ff0b55e..1b05eb48b4 100644
--- a/spec/bundler/dsl_spec.rb
+++ b/spec/bundler/dsl_spec.rb
@@ -9,7 +9,7 @@ describe Bundler::Dsl do
describe "#_normalize_options" do
it "converts :github to :git" do
subject.gem("sparks", :github => "indirect/sparks")
- github_uri = "git://github.com/indirect/sparks.git"
+ github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
@@ -27,7 +27,7 @@ describe Bundler::Dsl do
it "converts 'rails' to 'rails/rails'" do
subject.gem("rails", :github => "rails")
- github_uri = "git://github.com/rails/rails.git"
+ github_uri = "https://github.com/rails/rails.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
end
diff --git a/spec/other/binstubs_spec.rb b/spec/other/binstubs_spec.rb
index 9237a8d3d5..3d3ed0fc58 100644
--- a/spec/other/binstubs_spec.rb
+++ b/spec/other/binstubs_spec.rb
@@ -26,6 +26,30 @@ describe "bundle binstubs <gem>" do
expect(bundled_app("bin/rails")).to exist
end
+ it "does install multiple binstubs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rails"
+ G
+
+ bundle "binstubs rails rack"
+
+ expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ end
+
+ it "displays an error when used without any gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs", :exitstatus => true
+ expect(exitstatus).to eq(1)
+ expect(out).to eq("`bundle binstubs` needs at least one gem to run.")
+ end
+
it "does not bundle the bundler binary" do
install_gemfile <<-G
source "file://#{gem_repo1}"