summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSmit Shah <who828@gmail.com>2014-01-18 08:53:19 +0530
committerSmit Shah <who828@gmail.com>2014-01-18 08:53:43 +0530
commite19548c4eb6e1bb79261b8ac648448d9904b376f (patch)
tree45ab4e4f80d491ca26f52970815d9177c18f914b
parent318aab8149c6d9d4740291be23eff98dfc3cca4c (diff)
parent1f86e5acbad68fe5acd959d01a605b321b4f9d2c (diff)
downloadbundler-e19548c4eb6e1bb79261b8ac648448d9904b376f.tar.gz
Merged with master
-rw-r--r--CHANGELOG.md64
-rw-r--r--Rakefile6
-rw-r--r--bundler.gemspec2
-rw-r--r--lib/bundler.rb2
-rw-r--r--lib/bundler/cli.rb3
-rw-r--r--lib/bundler/fetcher.rb25
-rw-r--r--lib/bundler/gem_installer.rb9
-rw-r--r--lib/bundler/installer.rb50
-rw-r--r--lib/bundler/resolver.rb31
-rw-r--r--lib/bundler/rubygems_ext.rb14
-rw-r--r--lib/bundler/rubygems_integration.rb17
-rw-r--r--lib/bundler/settings.rb4
-rw-r--r--lib/bundler/source/git.rb8
-rw-r--r--lib/bundler/source/rubygems.rb16
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt2
-rw-r--r--lib/bundler/ui.rb145
-rw-r--r--lib/bundler/ui/rg_proxy.rb21
-rw-r--r--lib/bundler/ui/shell.rb98
-rw-r--r--lib/bundler/ui/silent.rb44
-rw-r--r--lib/bundler/version.rb2
-rw-r--r--man/bundle-install.ronn2
-rw-r--r--spec/commands/binstubs_spec.rb13
-rw-r--r--spec/commands/config_spec.rb18
-rw-r--r--spec/commands/package_spec.rb6
-rw-r--r--spec/install/gemfile/git_spec.rb2
-rw-r--r--spec/install/gems/dependency_api_spec.rb34
-rw-r--r--spec/quality_spec.rb4
-rw-r--r--spec/realworld/parallel_install_spec.rb23
-rw-r--r--spec/realworld/parallel_spec.rb69
-rw-r--r--spec/realworld/parallel_update_spec.rb31
-rw-r--r--spec/runtime/setup_spec.rb5
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/artifice/endpoint_host_redirect.rb15
-rw-r--r--spec/support/permissions.rb10
34 files changed, 495 insertions, 301 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73224b2614..5460aa15d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,55 @@
## 1.6.0
+Bugfixes:
+
+ - many Gemfiles that had incorrect errors now resolve correctly (@Who828)
+
Features:
+ - resolver rewritten to avoid recursion (@Who828)
+ - some complex Gemfiles are resolved up to 10x faster (@Who828)
- add support for IRB alternatives such as Pry and Ripl (@joallard, @postmodern)
- - highlight installing logs (#2722, @yaotti)
- - highlight updating logs (#2741, @simi)
+ - highlight installed or updated gems (#2722, #2741, @yaotti, @simi)
- display post_install_message's for gems installed via :git (@phallstrom)
+## 1.5.2 (2014-01-10)
+
+Bugfixes:
+
+ - fix integration with Rubygems 1.8.0-1.8.19
+ - handle ENETDOWN exception during network requests
+ - gracefully shut down after interrupt during parallel install (@Who828)
+ - allow Rails to run Thor without debug mode (@rafaelfranca)
+ - set git binstub permissions by umask (@v-yarotsky)
+ - remove parallel install debug log
+
+## 1.5.1 (2013-12-28)
+
+Bugfixes:
+
+ - correctly find gems installed with Ruby by default
+
+## 1.5.0 (2013-12-26)
+
+Features:
+
+ - install missing gems if their specs are present (@hone)
+
+Bugfixes:
+
+ - use print for "Installing…" so messages are thread-safe (@TimMoore)
+
+## 1.5.0.rc.2 (2013-12-18)
+
+"Features":
+
+ - Support threaded installation on Rubygems 2.0.7+
+ - Debug installation logs in .bundle/install.log
+
+"Bugfixes":
+
+ - Try to catch gem installation race conditions
+
## 1.5.0.rc.1 (2013-11-09)
Features:
@@ -78,12 +121,25 @@ Bugfixes:
- allow the same options hash to be passed to multiple gems (#2447)
- handle missing binaries without an exception (#2019, @luismreis)
-## 1.3.6
+## 1.3.6 (8 January 2014)
Bugfixes:
- - set --no-cache when bundle install --local is called (@TimMoore)
- make gemspec path option preserve relative paths in lock file (@bwillis)
+ - use umask when creating binstubs (#1618, @v-yarotsky)
+ - warn if graphviz is not installed (#2435, @Agis-)
+ - show git errors while loading gemspecs
+ - don't mutate gem method options hash (#2447)
+ - print Thor errors (#2478, @pjvds)
+ - print Rubygems system exit errors (James Cook)
+ - more Pathnames into Strings for MacRuby (@kml)
+ - preserve original gemspec path (@bwillis)
+ - remove warning about deps with :git (#1651, @ixti)
+ - split git files on null (#2634, @jasonmp85)
+ - handle cross-host redirects without SSL (#2686, @grddev)
+ - handle Rubygems 2 security exception (@zzak)
+ - reinstall gems if they are missing with spec present
+ - set binstub permissions using umask (#1618, @v-yarotsky)
## 1.3.5 (3 April 2013)
diff --git a/Rakefile b/Rakefile
index 11935394ac..ef8f779bda 100644
--- a/Rakefile
+++ b/Rakefile
@@ -44,6 +44,10 @@ namespace :spec do
system("sudo sed -i '/secure_path/d' /etc/sudoers")
# Install groff for the ronn gem
system("sudo apt-get install groff -y")
+ # Downgrade Rubygems on 1.8 to avoid https://github.com/rubygems/rubygems/issues/784
+ if RUBY_VERSION < '1.9'
+ system("gem update --system 2.1.11")
+ end
# Install the other gem deps, etc.
Rake::Task["spec:deps"].invoke
end
@@ -126,7 +130,7 @@ begin
puts "RUBYOPT=#{ENV['RUBYOPT']}"
end
- task rg => ["clone_rubygems_#{rg}", "man:build"]
+ task rg => ["man:build", "clone_rubygems_#{rg}"]
task "rubygems:all" => rg
end
diff --git a/bundler.gemspec b/bundler.gemspec
index 37c320e77e..17ed42f617 100644
--- a/bundler.gemspec
+++ b/bundler.gemspec
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'ronn', '~> 0.7.3'
spec.add_development_dependency 'rspec', '~> 2.99.0.beta1'
- spec.files = `git ls-files`.split($/)
+ spec.files = `git ls-files -z`.split("\x0")
spec.files += Dir.glob('lib/bundler/man/**/*') # man/ is ignored by git
spec.test_files = spec.files.grep(%r{^spec/})
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 4176d3a8b4..59878c03ea 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -90,7 +90,7 @@ module Bundler
end
def ui
- @ui ||= UI.new
+ @ui ||= UI::Silent.new
end
# Returns absolute path of where gems are installed on the filesystem.
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index b6201cba3c..fc129bcc31 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -1,5 +1,4 @@
require 'bundler'
-require 'bundler/similarity_detector'
require 'bundler/vendored_thor'
module Bundler
@@ -21,7 +20,7 @@ module Bundler
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
- options ||= {}
+ self.options ||= {}
Bundler.ui = UI::Shell.new(options)
Bundler.ui.level = "debug" if options["verbose"]
end
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 548002fb9b..940eb4fd24 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -226,7 +226,7 @@ module Bundler
private
HTTP_ERRORS = [
- Timeout::Error, EOFError, SocketError,
+ Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
Net::HTTP::Persistent::Error
@@ -235,17 +235,7 @@ module Bundler
def fetch(uri, counter = 0)
raise HTTPError, "Too many redirects" if counter >= @redirect_limit
- 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
- response = connection.request(uri, req)
- rescue OpenSSL::SSL::SSLError
- raise CertificateFailureError.new(uri)
- rescue *HTTP_ERRORS
- raise HTTPError, "Network error while fetching #{uri}"
- end
-
+ response = request(uri)
case response
when Net::HTTPRedirection
Bundler.ui.debug("HTTP Redirection")
@@ -265,6 +255,17 @@ module Bundler
end
end
+ def request(uri)
+ Bundler.ui.debug "Fetching from: #{uri}"
+ req = Net::HTTP::Get.new uri.request_uri
+ req.basic_auth(uri.user, uri.password) if uri.user
+ response = connection.request(uri, req)
+ rescue OpenSSL::SSL::SSLError
+ raise CertificateFailureError.new(uri)
+ rescue *HTTP_ERRORS
+ raise HTTPError, "Network error while fetching #{uri}"
+ end
+
def dependency_api_uri(gem_names = [])
url = "#{@remote_uri}api/v1/dependencies"
url << "?gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
diff --git a/lib/bundler/gem_installer.rb b/lib/bundler/gem_installer.rb
index 749ff53653..7d84939853 100644
--- a/lib/bundler/gem_installer.rb
+++ b/lib/bundler/gem_installer.rb
@@ -5,14 +5,5 @@ module Bundler
def check_executable_overwrite(filename)
# Bundler needs to install gems regardless of binstub overwriting
end
-
- if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
- def build_extensions
- # Gain the lock because rubygems use Dir.chdir
- SharedHelpers.chdir('.') do
- super
- end
- end
- end
end
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index b6a2a06a73..fa5b10fd1e 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -177,7 +177,7 @@ module Bundler
next
end
- File.open(binstub_path, 'w', 0755) do |f|
+ File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
f.puts ERB.new(template, nil, '-').result(binding)
end
end
@@ -198,13 +198,15 @@ module Bundler
end
private
+
def can_install_parallely?
- if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= 2.1.0.rc")
+ min_rubygems = "2.0.7"
+ if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= #{min_rubygems}")
true
else
Bundler.ui.warn "Rubygems #{Gem::VERSION} is not threadsafe, so your "\
- "gems must be installed one at a time. Upgrade to Rubygems 2.1 or "\
- "higher to enable parallel gem installation."
+ "gems must be installed one at a time. Upgrade to Rubygems " \
+ "#{min_rubygems} or higher to enable parallel gem installation."
false
end
end
@@ -284,13 +286,23 @@ module Bundler
message = install_gem_from_spec spec, standalone, worker
{ :name => spec.name, :post_install => message }
}
- specs.each do |spec|
- deps = spec.dependencies.select { |dep| dep.type != :development }
- if deps.empty?
- worker_pool.enq spec.name
- enqueued[spec.name] = true
+
+ # Keys in the remains hash represent uninstalled gems specs.
+ # We enqueue all gem specs that do not have any dependencies.
+ # Later we call this lambda again to install specs that depended on
+ # previously installed specifications. We continue until all specs
+ # are installed.
+ enqueue_remaining_specs = lambda do
+ remains.keys.each do |name|
+ next if enqueued[name]
+ spec = name2spec[name]
+ if ready_to_install?(spec, remains)
+ worker_pool.enq name
+ enqueued[name] = true
+ end
end
end
+ enqueue_remaining_specs.call
until remains.empty?
message = worker_pool.deq
@@ -298,19 +310,21 @@ module Bundler
if message[:post_install]
Installer.post_install_messages[message[:name]] = message[:post_install]
end
- remains.keys.each do |name|
- next if enqueued[name]
- spec = name2spec[name]
- deps = spec.dependencies.select { |dep| remains[dep.name] and dep.type != :development }
- if deps.empty?
- worker_pool.enq name
- enqueued[name] = true
- end
- end
+ enqueue_remaining_specs.call
end
message
ensure
worker_pool && worker_pool.stop
end
+
+ # We only want to install a gem spec if all its dependencies are met.
+ # If the dependency is no longer in the `remains` hash then it has been met.
+ # If a dependency is only development or is self referential it can be ignored.
+ def ready_to_install?(spec, remains)
+ spec.dependencies.none? do |dep|
+ next if dep.type == :development || dep.name == spec.name
+ remains[dep.name]
+ end
+ end
end
end
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index 118cd7b8d5..ffe7cb4ab1 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -172,35 +172,27 @@ module Bundler
end
def handle_conflict(current, states)
+ return unless current
parent = current
state = states.detect { |i| i.name == parent.name }
until (state && state.possibles.any?) || parent.required_by.empty?
parent = parent.required_by.last
state = states.detect { |i| i.name == parent.name }
end
- parent
+ return parent, state
end
def other_possible?(conflict, states)
- state = states.detect { |i| i.name == conflict }
+ return unless conflict
+ state = states.detect { |i| i.name == conflict.name }
state && state.possibles.any?
end
def find_conflict_state(conflict, states)
- rejected = []
-
until states.empty? do
state = states.pop
-
- return state if conflict == state.name
-
- rejected << state
+ return state if conflict.name == state.name
end
-
- return rejected.shift
- ensure
- rejected = rejected.concat(states)
- states.replace(rejected)
end
def activate_gem(reqs, activated, requirement, current)
@@ -224,7 +216,7 @@ module Bundler
end
def resolve_for_conflict(state)
- raise version_conflict if state.possibles.empty?
+ raise version_conflict if state.nil? || state.possibles.empty?
reqs, activated, depth = state.reqs.dup, state.activated.dup, state.depth
requirement = state.requirement
possible = state.possibles.pop
@@ -237,12 +229,12 @@ module Bundler
def resolve_conflict(current, states)
# Return a requirment/gem which has other possibles states
# Given the set of constraints placed by requrired_by
- parent = handle_conflict(current, states)
+ parent, _ = handle_conflict(current, states)
debug { " -> Going to: #{parent.name} state" }
# Find the state where the conlict has occured
- state = find_conflict_state(parent.name, states)
+ state = find_conflict_state(parent, states)
# Resolve the conflicts by rewinding the state
# when the conflicted gem was activated
@@ -311,8 +303,11 @@ module Bundler
@errors[existing.name] = [existing, current]
parent = current.required_by.last
- parent = current unless parent && other_possible?(parent.name, states)
- raise version_conflict if parent.name == 'bundler'
+ parent, state = handle_conflict(existing.required_by[-2], states) if !other_possible?(parent, states) && existing.respond_to?(:required_by)
+ parent = current unless state && state.possibles.any?
+
+ raise version_conflict if parent.nil? || parent.name == 'bundler'
+
reqs, activated, depth = resolve_conflict(parent, states)
end
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index 6661d2039c..ea018ac972 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -56,12 +56,8 @@ module Gem
end
def git_version
- if @loaded_from && File.exist?(File.join(full_gem_path, ".git"))
- sha = Bundler::SharedHelpers.chdir(full_gem_path) do
- null_command("git rev-parse HEAD").strip
- end
- " #{sha[0..6]}"
- end
+ return unless loaded_from && source.is_a?(Bundler::Source::Git)
+ " #{source.revision[0..6]}"
end
def to_gemfile(path = nil)
@@ -96,12 +92,6 @@ module Gem
gemfile
end
- # TODO: Do not rely on /dev/null.
- # see https://github.com/bundler/bundler/blob/98f79a1d/lib/bundler/source/git/git_proxy.rb#L97-101
- def null_command(command)
- `#{command} 2>#{Bundler::NULL}`.tap {|out| return `#{command}` if out.empty? }
- end
-
end
class Dependency
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index 16ccc7988c..5ce49e77cf 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -1,6 +1,4 @@
require 'rubygems'
-# rubygems master requires UI for ConfigFile but doesn't require it
-require 'rubygems/user_interaction'
require 'rubygems/config_file'
module Bundler
@@ -188,7 +186,8 @@ module Bundler
def download_gem(spec, uri, path)
uri = Bundler::Source.mirror_for(uri)
- Gem::RemoteFetcher.fetcher.download(spec, uri, path)
+ fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
+ fetcher.download(spec, uri, path)
end
def security_policies
@@ -476,9 +475,9 @@ module Bundler
def stub_rubygems(specs)
Gem::Specification.all = specs
- Gem.post_reset {
+ Gem.post_reset do
Gem::Specification.all = specs
- }
+ end
end
def all_specs
@@ -514,6 +513,14 @@ module Bundler
hash
end
+ def download_gem(spec, uri, path)
+ require 'resolv'
+ uri = Bundler::Source.mirror_for(uri)
+ proxy, dns = configuration[:http_proxy], Resolv::DNS.new
+ fetcher = Gem::RemoteFetcher.new(proxy, dns)
+ fetcher.download(spec, uri, path)
+ end
+
def gem_from_path(path, policy = nil)
require 'rubygems/package'
p = Gem::Package.new(path)
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index 94fa76f5fc..ae659c139f 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -154,7 +154,9 @@ module Bundler
def load_config(config_file)
valid_file = config_file && config_file.exist? && !config_file.size.zero?
if !ignore_config? && valid_file
- Hash[config_file.read.scan(/^(BUNDLE_.+): ['"]?(.+?)['"]?$/)]
+ config_regex = /^(BUNDLE_.+): (?:['"](.*)['"]|(.+))$/
+ config_pairs = config_file.read.scan(config_regex).map{|m| m.compact }
+ Hash[config_pairs]
else
{}
end
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 43d09eb6a3..209def7a7d 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -203,6 +203,10 @@ module Bundler
"#{base_name}-#{shortref_for_path(cached_revision || revision)}"
end
+ def revision
+ git_proxy.revision
+ end
+
private
def serialize_gemspecs_in(destination)
@@ -267,10 +271,6 @@ module Bundler
options["revision"]
end
- def revision
- git_proxy.revision
- end
-
def cached?
cache_path.exist?
end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index ec8fba2dff..afa445578f 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -68,7 +68,9 @@ module Bundler
end
def install(spec)
- return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any? && gem_dir_exists?(spec)
+ if installed_specs[spec].any? && gem_dir_exists?(spec)
+ return ["Using #{version_message(spec)}", nil]
+ end
# Download the gem to get the spec, because some specs that are returned
# by rubygems.org are broken and wrong.
@@ -275,11 +277,13 @@ module Bundler
end
def gem_dir_exists?(spec)
- if spec.name == "bundler"
- true
- else
- File.directory?(spec.full_gem_path)
- end
+ return true if spec.name == "bundler"
+ # Ruby 2 default gems
+ return true if spec.loaded_from.include?("specifications/default/")
+ # Ruby 1.9 default gems
+ return true if spec.summary =~ /is bundled with Ruby/
+
+ File.directory?(spec.full_gem_path)
end
end
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index 5a3c312aca..34072c3083 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
spec.homepage = ""
spec.license = "MIT"
- spec.files = `git ls-files`.split($/)
+ spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
diff --git a/lib/bundler/ui.rb b/lib/bundler/ui.rb
index 7ab44eb40c..af325afba8 100644
--- a/lib/bundler/ui.rb
+++ b/lib/bundler/ui.rb
@@ -1,144 +1,7 @@
-require 'rubygems/user_interaction'
-
module Bundler
- class UI
- def warn(message, newline = nil)
- end
-
- def debug(message, newline = nil)
- end
-
- def trace(message, newline = nil)
- end
-
- def error(message, newline = nil)
- end
-
- def info(message, newline = nil)
- end
-
- def confirm(message, newline = nil)
- end
-
- def quiet?
- false
- end
-
- def debug?
- false
- end
-
- def ask(message)
- end
-
- class Shell < UI
- LEVELS = %w(silent error warn confirm info debug)
-
- attr_writer :shell
-
- def initialize(options = {})
- if options["no-color"] || !STDOUT.tty?
- Thor::Base.shell = Thor::Shell::Basic
- end
- @shell = Thor::Base.shell.new
- @level = ENV['DEBUG'] ? "debug" : "info"
- end
-
- def info(msg, newline = nil)
- tell_me(msg, nil, newline) if level("info")
- end
-
- def confirm(msg, newline = nil)
- tell_me(msg, :green, newline) if level("confirm")
- end
-
- def warn(msg, newline = nil)
- tell_me(msg, :yellow, newline) if level("warn")
- end
-
- def error(msg, newline = nil)
- tell_me(msg, :red, newline) if level("error")
- end
-
- def debug(msg, newline = nil)
- tell_me(msg, nil, newline) if level("debug")
- end
-
- def debug?
- # needs to be false instead of nil to be newline param to other methods
- level("debug")
- end
-
- def quiet?
- LEVELS.index(@level) <= LEVELS.index("warn")
- end
-
- def ask(msg)
- @shell.ask(msg)
- end
-
- def level=(level)
- raise ArgumentError unless LEVELS.include?(level.to_s)
- @level = level
- end
-
- def level(name = nil)
- name ? LEVELS.index(name) <= LEVELS.index(@level) : @level
- end
-
- def trace(e, newline = nil)
- msg = ["#{e.class}: #{e.message}", *e.backtrace].join("\n")
- if debug?
- tell_me(msg, nil, newline)
- elsif @trace
- STDERR.puts "#{msg}#{newline}"
- end
- end
-
- def silence
- old_level, @level = @level, "silent"
- yield
- ensure
- @level = old_level
- end
-
- private
-
- # valimism
- def tell_me(msg, color = nil, newline = nil)
- msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
- if newline.nil?
- @shell.say(msg, color)
- else
- @shell.say(msg, color, newline)
- end
- end
-
- def strip_leading_spaces(text)
- spaces = text[/\A\s+/, 0]
- spaces ? text.gsub(/#{spaces}/, '') : text
- end
-
- def word_wrap(text, line_width = @shell.terminal_width)
- strip_leading_spaces(text).split("\n").collect do |line|
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
- end * "\n"
- end
- end
-
- class RGProxy < ::Gem::SilentUI
- def initialize(ui)
- @ui = ui
- super()
- end
-
- def say(message)
- if message =~ /native extensions/
- @ui.info "with native extensions "
- else
- @ui.debug(message)
- end
- end
- end
+ module UI
+ autoload :RGProxy, 'bundler/ui/rg_proxy'
+ autoload :Shell, 'bundler/ui/shell'
+ autoload :Silent, 'bundler/ui/silent'
end
end
diff --git a/lib/bundler/ui/rg_proxy.rb b/lib/bundler/ui/rg_proxy.rb
new file mode 100644
index 0000000000..1b620019fc
--- /dev/null
+++ b/lib/bundler/ui/rg_proxy.rb
@@ -0,0 +1,21 @@
+require 'bundler/ui'
+require 'rubygems/user_interaction'
+
+module Bundler
+ module UI
+ class RGProxy < ::Gem::SilentUI
+ def initialize(ui)
+ @ui = ui
+ super()
+ end
+
+ def say(message)
+ if message =~ /native extensions/
+ @ui.info "with native extensions "
+ else
+ @ui.debug(message)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb
new file mode 100644
index 0000000000..d2c00529e1
--- /dev/null
+++ b/lib/bundler/ui/shell.rb
@@ -0,0 +1,98 @@
+module Bundler
+ module UI
+ class Shell
+ LEVELS = %w(silent error warn confirm info debug)
+
+ attr_writer :shell
+
+ def initialize(options = {})
+ if options["no-color"] || !STDOUT.tty?
+ Thor::Base.shell = Thor::Shell::Basic
+ end
+ @shell = Thor::Base.shell.new
+ @level = ENV['DEBUG'] ? "debug" : "info"
+ end
+
+ def info(msg, newline = nil)
+ tell_me(msg, nil, newline) if level("info")
+ end
+
+ def confirm(msg, newline = nil)
+ tell_me(msg, :green, newline) if level("confirm")
+ end
+
+ def warn(msg, newline = nil)
+ tell_me(msg, :yellow, newline) if level("warn")
+ end
+
+ def error(msg, newline = nil)
+ tell_me(msg, :red, newline) if level("error")
+ end
+
+ def debug(msg, newline = nil)
+ tell_me(msg, nil, newline) if level("debug")
+ end
+
+ def debug?
+ # needs to be false instead of nil to be newline param to other methods
+ level("debug")
+ end
+
+ def quiet?
+ LEVELS.index(@level) <= LEVELS.index("warn")
+ end
+
+ def ask(msg)
+ @shell.ask(msg)
+ end
+
+ def level=(level)
+ raise ArgumentError unless LEVELS.include?(level.to_s)
+ @level = level
+ end
+
+ def level(name = nil)
+ name ? LEVELS.index(name) <= LEVELS.index(@level) : @level
+ end
+
+ def trace(e, newline = nil)
+ msg = ["#{e.class}: #{e.message}", *e.backtrace].join("\n")
+ if debug?
+ tell_me(msg, nil, newline)
+ elsif @trace
+ STDERR.puts "#{msg}#{newline}"
+ end
+ end
+
+ def silence
+ old_level, @level = @level, "silent"
+ yield
+ ensure
+ @level = old_level
+ end
+
+ private
+
+ # valimism
+ def tell_me(msg, color = nil, newline = nil)
+ msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap]
+ if newline.nil?
+ @shell.say(msg, color)
+ else
+ @shell.say(msg, color, newline)
+ end
+ end
+
+ def strip_leading_spaces(text)
+ spaces = text[/\A\s+/, 0]
+ spaces ? text.gsub(/#{spaces}/, '') : text
+ end
+
+ def word_wrap(text, line_width = @shell.terminal_width)
+ strip_leading_spaces(text).split("\n").collect do |line|
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
+ end * "\n"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb
new file mode 100644
index 0000000000..3eb3199b5c
--- /dev/null
+++ b/lib/bundler/ui/silent.rb
@@ -0,0 +1,44 @@
+module Bundler
+ module UI
+ class Silent
+ def info(message, newline = nil)
+ end
+
+ def confirm(message, newline = nil)
+ end
+
+ def warn(message, newline = nil)
+ end
+
+ def error(message, newline = nil)
+ end
+
+ def debug(message, newline = nil)
+ end
+
+ def debug?
+ false
+ end
+
+ def quiet?
+ false
+ end
+
+ def ask(message)
+ end
+
+ def level=(name)
+ end
+
+ def level(name = nil)
+ end
+
+ def trace(message, newline = nil)
+ end
+
+ def silence
+ yield
+ end
+ end
+ end
+end
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index 5a571971bf..05eb3ba2ff 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -2,5 +2,5 @@ module Bundler
# We're doing this because we might write tests that deal
# with other versions of bundler and we are unsure how to
# handle this better.
- VERSION = "1.5.0.rc.1" unless defined?(::Bundler::VERSION)
+ VERSION = "1.6.0.pre.1" unless defined?(::Bundler::VERSION)
end
diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn
index 2af9c45d3a..92a1923e60 100644
--- a/man/bundle-install.ronn
+++ b/man/bundle-install.ronn
@@ -61,7 +61,7 @@ update process below under [CONSERVATIVE UPDATING][].
Do not attempt to connect to `rubygems.org`, instead using just
the gems already present in Rubygems' cache or in `vendor/cache`.
Note that if a more appropriate platform-specific gem exists on
- `rubygems.org`, it will not be found. This option implies `--no-cache`.
+ `rubygems.org`, it will not be found.
* `--deployment`:
Switches bundler's defaults into [deployment mode][DEPLOYMENT MODE].
diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb
index 3d3ed0fc58..0f04ad4dd9 100644
--- a/spec/commands/binstubs_spec.rb
+++ b/spec/commands/binstubs_spec.rb
@@ -90,6 +90,19 @@ describe "bundle binstubs <gem>" do
expect(bundled_app("bin/foo")).to exist
end
+
+ it "sets correct permissions for binstubs" do
+ with_umask(0002) do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "binstubs rack"
+ binary = bundled_app("bin/rackup")
+ expect(File.stat(binary).mode.to_s(8)).to eq("100775")
+ end
+ end
end
context "--path" do
diff --git a/spec/commands/config_spec.rb b/spec/commands/config_spec.rb
index 6c5a2c3707..612dbc2344 100644
--- a/spec/commands/config_spec.rb
+++ b/spec/commands/config_spec.rb
@@ -192,4 +192,22 @@ E
expect(out).to eq("http://gems.example.org/ => http://gem-mirror.example.org/")
end
end
+
+ describe "quoting" do
+ before(:each) { bundle :install }
+
+ it "saves quotes" do
+ bundle "config foo something\\'"
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("something'")
+ end
+
+ it "doesn't return quotes around values", :ruby => "1.9" do
+ bundle "config foo '1'"
+ run "puts Bundler.settings.send(:global_config_file).read"
+ expect(out).to include("'1'")
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("1")
+ end
+ end
end
diff --git a/spec/commands/package_spec.rb b/spec/commands/package_spec.rb
index 708107abd1..e433b92094 100644
--- a/spec/commands/package_spec.rb
+++ b/spec/commands/package_spec.rb
@@ -32,7 +32,7 @@ end
describe "bundle install with gem sources" do
describe "when cached and locked" do
- it "does not hit the remote at all if --local is passed" do
+ it "does not hit the remote at all" do
build_repo2
install_gemfile <<-G
source "file://#{gem_repo2}"
@@ -44,11 +44,10 @@ describe "bundle install with gem sources" do
FileUtils.rm_rf gem_repo2
bundle "install --local"
- expect(out).not_to include("Updating files in vendor/cache")
should_be_installed "rack 1.0.0"
end
- it "does not hit the remote at all if --deployment is passed" do
+ it "does not hit the remote at all" do
build_repo2
install_gemfile <<-G
source "file://#{gem_repo2}"
@@ -60,7 +59,6 @@ describe "bundle install with gem sources" do
FileUtils.rm_rf gem_repo2
bundle "install --deployment"
- expect(out).not_to include("Updating files in vendor/cache")
should_be_installed "rack 1.0.0"
end
diff --git a/spec/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb
index 2beddabcf2..4933727b36 100644
--- a/spec/install/gemfile/git_spec.rb
+++ b/spec/install/gemfile/git_spec.rb
@@ -34,7 +34,7 @@ describe "bundle install with git sources" do
git = update_git "foo" do |s|
s.executables = ["foobar"] # we added this the first time, so keep it now
s.files = ["bin/foobar"] # updating git nukes the files list
- foospec = s.to_ruby.gsub(/s\.files.*/, 's.files = `git ls-files`.split("\n")')
+ foospec = s.to_ruby.gsub(/s\.files.*/, 's.files = `git ls-files -z`.split("\x0")')
s.write "foo.gemspec", foospec
end
diff --git a/spec/install/gems/dependency_api_spec.rb b/spec/install/gems/dependency_api_spec.rb
index da9551392a..a88e459437 100644
--- a/spec/install/gems/dependency_api_spec.rb
+++ b/spec/install/gems/dependency_api_spec.rb
@@ -162,6 +162,40 @@ describe "gemcutter's dependency API" do
should_be_installed "rack 1.0.0"
end
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_host_redirect"
+ should_be_installed "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ should_be_installed "rack 1.0.0"
+ end
+
it "timeouts when Bundler::Fetcher redirects too much" do
gemfile <<-G
source "#{source_uri}"
diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb
index 840f2be97f..ca3dfc18a5 100644
--- a/spec/quality_spec.rb
+++ b/spec/quality_spec.rb
@@ -55,7 +55,7 @@ describe "The library itself" do
exempt = /\.gitmodules|\.marshal|fixtures|vendor|ssl_certs|LICENSE/
error_messages = []
Dir.chdir(File.expand_path("../..", __FILE__)) do
- `git ls-files`.split("\n").each do |filename|
+ `git ls-files -z`.split("\x0").each do |filename|
next if filename =~ exempt
error_messages << check_for_tab_characters(filename)
error_messages << check_for_extra_spaces(filename)
@@ -68,7 +68,7 @@ describe "The library itself" do
included = /spec/
error_messages = []
Dir.chdir(File.expand_path("../", __FILE__)) do
- `git ls-files`.split("\n").each do |filename|
+ `git ls-files -z`.split("\x0").each do |filename|
next unless filename =~ included
error_messages << check_for_spec_defs_with_single_quotes(filename)
end
diff --git a/spec/realworld/parallel_install_spec.rb b/spec/realworld/parallel_install_spec.rb
deleted file mode 100644
index ca92ca016b..0000000000
--- a/spec/realworld/parallel_install_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'spec_helper'
-
-describe "installing dependencies parallely", :realworld => true do
- it "installs gems parallely" do
- gemfile <<-G
- source "https://rubygems.org"
- gem 'activesupport', '~> 3.2.13'
- gem 'faker', '~> 1.1.2'
- G
-
- bundle :install, :jobs => 2, :env => {"DEBUG" => "1"}
- (0..1).each {|i| expect(out).to include("#{i}: ") }
-
- bundle "show activesupport"
- expect(out).to match(/activesupport/)
-
- bundle "show faker"
- expect(out).to match(/faker/)
-
- bundle "config jobs"
- expect(out).to match(/: "2"/)
- end
-end
diff --git a/spec/realworld/parallel_spec.rb b/spec/realworld/parallel_spec.rb
new file mode 100644
index 0000000000..457139b882
--- /dev/null
+++ b/spec/realworld/parallel_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe "parallel", :realworld => true do
+ it "installs", :ruby => "1.8" do
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.13'
+ gem 'faker', '~> 1.1.2'
+ G
+
+ bundle :install, :jobs => 4, :env => {"DEBUG" => "1"}
+ expect(out).to match(/[1-3]: /)
+
+ bundle "show activesupport"
+ expect(out).to match(/activesupport/)
+
+ bundle "show faker"
+ expect(out).to match(/faker/)
+
+ bundle "config jobs"
+ expect(out).to match(/: "4"/)
+ end
+
+ it "installs even with circular dependency", :ruby => "1.9" do
+ gemfile <<-G
+ source 'https://rubygems.org'
+ gem 'activesupport', '~> 3.2.13'
+ gem 'mongoid_auto_increment', "0.1.1"
+ G
+
+ bundle :install, :jobs => 4, :env => {"DEBUG" => "1"}
+ expect(out).to match(/[1-3]: /)
+
+ bundle "show activesupport"
+ expect(out).to match(/activesupport/)
+
+ bundle "show mongoid_auto_increment"
+ expect(out).to match(%r{gems/mongoid_auto_increment})
+
+ bundle "config jobs"
+ expect(out).to match(/: "4"/)
+ end
+
+ it "updates" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '3.2.12'
+ gem 'faker', '~> 1.1.2'
+ G
+
+ gemfile <<-G
+ source "https://rubygems.org"
+ gem 'activesupport', '~> 3.2.12'
+ gem 'faker', '~> 1.1.2'
+ G
+
+ bundle :update, :jobs => 4, :env => {"DEBUG" => "1"}
+ expect(out).to match(/[1-3]: /)
+
+ bundle "show activesupport"
+ expect(out).to match(/activesupport-3\.2\.1[3-9]/)
+
+ bundle "show faker"
+ expect(out).to match(/faker/)
+
+ bundle "config jobs"
+ expect(out).to match(/: "4"/)
+ end
+end
diff --git a/spec/realworld/parallel_update_spec.rb b/spec/realworld/parallel_update_spec.rb
deleted file mode 100644
index e6e71453d9..0000000000
--- a/spec/realworld/parallel_update_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'spec_helper'
-
-describe "updating dependencies parallely", :realworld => true do
- before :each do
- install_gemfile <<-G
- source "https://rubygems.org"
- gem 'activesupport', '~> 3.2.12'
- gem 'faker', '~> 1.1.2'
- G
- end
-
- it "installs gems parallely" do
- gemfile <<-G
- source "https://rubygems.org"
- gem 'activesupport', '3.2.13'
- gem 'faker', '~> 1.1.2'
- G
-
- bundle :update, :jobs => 2, :env => {"DEBUG" => "1"}
- (0..1).each {|i| expect(out).to include("#{i}: ") }
-
- bundle "show activesupport"
- expect(out).to match(/activesupport-3\.2\.13/)
-
- bundle "show faker"
- expect(out).to match(/faker/)
-
- bundle "config jobs"
- expect(out).to match(/: "2"/)
- end
-end
diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb
index 00e8e1bcca..05f9a32fa2 100644
--- a/spec/runtime/setup_spec.rb
+++ b/spec/runtime/setup_spec.rb
@@ -631,7 +631,7 @@ describe "Bundler.setup" do
expect(out).to eq("yay")
end
- it "ignores Gem.refresh" do
+ it "stubs out Gem.refresh so it does not reveal system gems" do
system_gems "rack-1.0.0"
install_gemfile <<-G
@@ -640,11 +640,12 @@ describe "Bundler.setup" do
G
run <<-R
+ puts Bundler.rubygems.find_name("rack").inspect
Gem.refresh
puts Bundler.rubygems.find_name("rack").inspect
R
- expect(out).to eq("[]")
+ expect(out).to eq("[]\n[]")
end
describe "when a vendored gem specification uses the :path option" do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 44f68c673a..5deb1e7bc0 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -43,6 +43,7 @@ RSpec.configure do |config|
config.include Spec::Rubygems
config.include Spec::Platforms
config.include Spec::Sudo
+ config.include Spec::Permissions
if Spec::Sudo.test_sudo?
config.filter_run :sudo => true
diff --git a/spec/support/artifice/endpoint_host_redirect.rb b/spec/support/artifice/endpoint_host_redirect.rb
new file mode 100644
index 0000000000..e44d63e2a6
--- /dev/null
+++ b/spec/support/artifice/endpoint_host_redirect.rb
@@ -0,0 +1,15 @@
+require File.expand_path("../endpoint", __FILE__)
+
+Artifice.deactivate
+
+class EndpointHostRedirect < Endpoint
+ get "/fetch/actual/gem/:id", :host_name => 'localgemserver.test' do
+ redirect "http://bundler.localgemserver.test#{request.path_info}"
+ end
+
+ get "/api/v1/dependencies" do
+ status 404
+ end
+end
+
+Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/support/permissions.rb b/spec/support/permissions.rb
new file mode 100644
index 0000000000..efdb4a5b28
--- /dev/null
+++ b/spec/support/permissions.rb
@@ -0,0 +1,10 @@
+module Spec
+ module Permissions
+ def with_umask(new_umask)
+ old_umask = File.umask(new_umask)
+ yield if block_given?
+ ensure
+ File.umask(old_umask)
+ end
+ end
+end