diff options
author | Smit Shah <who828@gmail.com> | 2014-01-18 08:53:19 +0530 |
---|---|---|
committer | Smit Shah <who828@gmail.com> | 2014-01-18 08:53:43 +0530 |
commit | e19548c4eb6e1bb79261b8ac648448d9904b376f (patch) | |
tree | 45ab4e4f80d491ca26f52970815d9177c18f914b | |
parent | 318aab8149c6d9d4740291be23eff98dfc3cca4c (diff) | |
parent | 1f86e5acbad68fe5acd959d01a605b321b4f9d2c (diff) | |
download | bundler-e19548c4eb6e1bb79261b8ac648448d9904b376f.tar.gz |
Merged with master
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) @@ -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 |