diff options
author | Andre Arko <andre@arko.net> | 2015-05-07 16:34:39 +0100 |
---|---|---|
committer | Andre Arko <andre@arko.net> | 2015-05-07 16:34:39 +0100 |
commit | 95858d54e19a6fe1adffeefc9e7eba942d3c8332 (patch) | |
tree | 723915396db04a4393f46fa330ae8a00a54f1934 | |
parent | 30569377750ae306997a37cde6e2fd814e1d24cd (diff) | |
parent | 21e6fe9ebb520b36b201d0e569d0b8b7c2b3980c (diff) | |
download | bundler-95858d54e19a6fe1adffeefc9e7eba942d3c8332.tar.gz |
Merge branch '1-99-dev' into 2-0-dev
146 files changed, 2878 insertions, 865 deletions
diff --git a/.travis.yml b/.travis.yml index cc6bb6fda3..95c2f51d61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,12 @@ before_script: travis_retry rake spec:travis:deps branches: only: - master +<<<<<<< HEAD - 2-0-dev +======= + - 1-99-dev + - 1-9-stable +>>>>>>> 1-99-dev - 1-8-stable - 1-7-stable - 1-6-stable @@ -33,6 +38,7 @@ rvm: - 2.0.0 - 1.9.3 - 1.8.7 + - rbx-2 # Rubygems versions MUST be available as rake tasks # see Rakefile:66 for the list of possible RGV values diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c9a0ac206..3a037c104d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,95 @@ +## 1.10.0.pre.1 (2015-05-05) + +Bugfixes: + + - always clean up tmp dirs (#3277, @hone, @indirect, @segiddins) + +## 1.10.0.pre (2015-05-03) + +Features: + + - support gem extensions built into any directory on RubyGems 2.2+ (#3582, @voxik) + - add 'bundler/inline' which provides a `gemfile` method (#3440, @segiddins) + - improved error reports for Gemfile errors (#3480, @segiddins) + - `lock` command (#3437, @segiddins) + - add `ignore_messages` config to suppress post-install text (#3510, @pducks32) + - improve `gem` minitest template (#3513, #3515, @arthurnn) + - add `install --force` to re-install installed gems (#3519, @segiddins) + - show more `outdated` information, including groups (@smlance, @indirect) + - add optional groups to the Gemfile (#3531, @jhass) + - accept glob argument to `gemspec` in Gemfile (#3464, @pjump) + - make timeouts and retries configurable via `config` (#3601, @pducks32) + - add `install_if` Gemfile method for conditional installs (#3611, @segiddins) + +Bugfixes: + + - standalone mode now uses builtin gems correctly (#3610, @segiddins) + - fix `rake spec:deps` on MinGW Ruby 2.0+ (#3487, @marutosi) + - remember all y/n answers when generating gems (#3579, @pducks32) + +Performance: + + - use RubyGems stub specifications when possible (#3580, @segiddins) + +Deprecations: + + - deprecated the (never enabled) `bundle_ruby` binary (@smlance) + +## 1.9.6 (2015-05-02) + +Bugfixes: + + - use RubyGems spec stubs if available (@segiddins) + - allow creating gems with names containing two dashes (#3483, @janlelis) + - allow creating gems with names extending constants (#3603, @amatsuda) + +## 1.9.5 (2015-04-29) + +Bugfixes: + + - respect Gemfile sources when installing a gem present in two sources (#3585, @tmoore) + +## 1.9.4 (2015-04-13) + +Bugfixes: + + - fix regression in installing x86 and universal gems (#3565, @jdmundrawala) + - improve error when gems are missing (#3564, @sealocal) + +## 1.9.3 (2015-04-12) + +Bugfixes: + + - handle removal of `specs` from rubygems/rubygems@620910 (#3558, @indirect) + - install 'universal' gems on Windows (#3066, @jdmundrawala) + - stop passing --local during `rake install` task (#3236, @indirect) + - guard against all possible accidental public gem pushes (#3533, @indirect) + +## 1.9.2 (2015-03-30) + +Bugfixes: + + - ensure gem executables are executable (#3517, #3511, @indirect) + - fix warnings in Molinillo (#3516, @segiddins) + - ensure duplicate dependencies do not propagate (#3522, @segiddins) + - keep gems locked when updating another gem from the same source (#3520, @indirect) + - resolve race that could build gems without saved arguments (#3404, @indirect) + +## 1.9.1 (2015-03-21) + +Bugfixes: + + - avoid exception in 'bundler/gem_tasks' (#3492, @segiddins) + +## 1.9.0 (2015-03-20) + ## 1.9.0.rc (2015-03-13) Bugfixes: - make Bundler.which stop finding directories (@nohoho) - handle Bundler prereleases correctly (#3470, @segiddins) + - add before_install to .travis.yml template for new gems (@kodnin) ## 1.9.0.pre.1 (2015-03-11) @@ -23,6 +109,31 @@ Features: - Molinillo resolver, shared with CocoaPods (@segiddins) - updated Thor to v0.19.1 (@segiddins) +## 1.8.9 (2015-05-02) + +Bugfixes: + + - Use RubyGems spec stubs if available (@segiddins) + +## 1.8.8 (2015-04-29) + +Bugfixes: + + - Respect Gemfile sources when installing a gem present in two sources (#3585, @tmoore) + +## 1.8.7 (2015-04-07) + +Bugfixes: + + - stop suppressing errors inside gems that get required (#3549, @indirect) + +## 1.8.6 (2015-03-30) + +Bugfixes: + + - keep gems locked when updating another gem from the same source (#3250, @indirect) + - resolve race that could build gems without saved arguments (#3404, @indirect) + ## 1.8.5 (2015-03-11) Bugfixes: @@ -122,6 +233,19 @@ Documentation: - add missing Gemfile global `path` explanation (@agenteo) +## 1.7.15 (2015-04-29) + +Bugfixes: + + - Respect Gemfile sources when installing a gem present in two sources (#3585, @tmoore) + +## 1.7.14 (2015-03-30) + +Bugfixes: + + - Keep gems locked when updating another gem from the same source (#3250, @indirect) + - Don't add extra quotes around long, quoted config values (@aroben, #3338) + ## 1.7.13 (2015-02-07) Bugfixes: @@ -27,23 +27,32 @@ module Rake end end +def clean_files(files, regex, replacement = '') + files.each do |file| + contents = File.read(file) + contents.gsub!(regex, replacement) + File.open(file, 'w') { |f| f << contents } + end +end + namespace :molinillo do task :namespace do - files = Dir.glob('lib/bundler/vendor/Molinillo*/**/*.rb') - sh "sed -i.bak 's/Molinillo/Bundler::Molinillo/g' #{files.join(' ')}" - sh "rm #{files.join('.bak ')}.bak" + files = Dir.glob('lib/bundler/vendor/molinillo*/**/*.rb') + clean_files(files, 'Molinillo', 'Bundler::Molinillo') + clean_files(files, /require (["'])molinillo/, 'require \1bundler/vendor/molinillo/lib/molinillo') end task :clean do - files = Dir.glob('lib/bundler/vendor/Molinillo*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } - puts files - sh "rm -r #{files.join(' ')}" + files = Dir.glob('lib/bundler/vendor/molinillo*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } + rm_r files end task :update, [:tag] => [] do |t, args| tag = args[:tag] Dir.chdir 'lib/bundler/vendor' do - `curl -L https://github.com/CocoaPods/molinillo/archive/#{tag}.tar.gz | tar -xz` + rm_rf 'molinillo' + sh "curl -L https://github.com/CocoaPods/molinillo/archive/#{tag}.tar.gz | tar -xz" + sh "mv Molinillo-* molinillo" end Rake::Task['molinillo:namespace'].invoke Rake::Task['molinillo:clean'].invoke @@ -53,20 +62,22 @@ end namespace :thor do task :namespace do files = Dir.glob('lib/bundler/vendor/thor*/**/*.rb') - sh "sed -i.bak 's/Thor/Bundler::Thor/g' #{files.join(' ')}" - sh "rm #{files.join('.bak ')}.bak" + clean_files(files, 'Thor', 'Bundler::Thor') + clean_files(files, /require (["'])thor/, 'require \1bundler/vendor/thor/lib/thor') + clean_files(files, /(autoload\s+[:\w]+,\s+["'])(thor[\w\/]+["'])/, '\1bundler/vendor/thor/lib/\2') end task :clean do files = Dir.glob('lib/bundler/vendor/thor*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last } - puts files - sh "rm -r #{files.join(' ')}" + rm_r files end task :update, [:tag] => [] do |t, args| tag = args[:tag] Dir.chdir 'lib/bundler/vendor' do - `curl -L https://github.com/erikhuda/thor/archive/#{tag}.tar.gz | tar -xz` + rm_rf 'thor' + sh "curl -L https://github.com/erikhuda/thor/archive/#{tag}.tar.gz | tar -xz" + sh "mv thor-* thor" end Rake::Task['thor:namespace'].invoke Rake::Task['thor:clean'].invoke @@ -280,12 +291,15 @@ begin task :clean do rm_rf "lib/bundler/man" end + + task(:require) { } end rescue LoadError namespace :man do - task(:build) { warn "Install the ronn gem to be able to release!" } - task(:clean) { warn "Install the ronn gem to be able to release!" } + task(:require) { abort "Install the ronn gem to be able to release!" } + task(:build) { warn "Install the ronn gem to build the help pages" } + task(:clean) { } end end @@ -297,6 +311,6 @@ end require 'bundler/gem_tasks' task :build => ["man:clean", "man:build"] -task :release => ["man:clean", "man:build"] +task :release => ["man:require", "man:clean", "man:build"] task :default => :spec diff --git a/bin/bundle_ruby b/bin/bundle_ruby new file mode 100755 index 0000000000..1309b8d6fd --- /dev/null +++ b/bin/bundle_ruby @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby + +Signal.trap("INT") { exit 1 } + +require 'bundler/ruby_version' +require 'bundler/ruby_dsl' +require 'bundler/shared_helpers' + +module Bundler + class GemfileError < RuntimeError; end + class Dsl + include RubyDsl + + attr_accessor :ruby_version + + def initialize + @ruby_version = nil + end + + def eval_gemfile(gemfile, contents = nil) + contents ||= File.open(gemfile, "rb") { |f| f.read } + instance_eval(contents, gemfile.to_s, 1) + rescue SyntaxError => e + bt = e.message.split("\n")[1..-1] + raise GemfileError, ["Gemfile syntax error:", *bt].join("\n") + rescue ScriptError, RegexpError, NameError, ArgumentError => e + e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})" + STDERR.puts e.backtrace.join("\n ") + raise GemfileError, "There was an error in your Gemfile," \ + " and Bundler cannot continue." + end + + def source(source, options = {}) + end + + def gem(name, *args) + end + + def group(*args, &blk) + end + end +end + +STDERR.puts "DEPRECATION: bundle_ruby is deprecated and will be removed in " \ + "Bundler 2.0." + +dsl = Bundler::Dsl.new +begin + dsl.eval_gemfile(Bundler::SharedHelpers.default_gemfile) + ruby_version = dsl.ruby_version + if ruby_version + puts ruby_version + else + puts "No ruby version specified" + end +rescue Bundler::GemfileError => e + puts e.message + exit(-1) +end diff --git a/bundler.gemspec b/bundler.gemspec index bd7e32e7c9..c9fafa59bd 100644 --- a/bundler.gemspec +++ b/bundler.gemspec @@ -16,10 +16,11 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' s.required_rubygems_version = '>= 1.3.6' - s.add_development_dependency 'mustache', '0.99.6' + s.add_development_dependency 'mustache', '0.99.6' s.add_development_dependency 'rdiscount', '~> 1.6' - s.add_development_dependency 'ronn', '~> 0.7.3' - s.add_development_dependency 'rspec', '~> 3.0' + s.add_development_dependency 'ronn', '~> 0.7.3' + s.add_development_dependency 'rspec', '~> 3.0' + s.add_development_dependency 'rake' s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } # we don't check in man pages, but we need to ship them because diff --git a/lib/bundler.rb b/lib/bundler.rb index 83c066e5e5..197e88c222 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -120,12 +120,7 @@ module Bundler # Load all groups, but only once @setup = load.setup else - @completed_groups ||= [] - # Figure out which groups haven't been loaded yet - unloaded = groups - @completed_groups - # Record groups that are now loaded - @completed_groups = groups - unloaded.any? ? load.setup(*groups) : load + load.setup(*groups) end end @@ -211,13 +206,11 @@ module Bundler end def tmp(name = Process.pid.to_s) - @tmp ||= Pathname.new Dir.mktmpdir("bundler") - @tmp.join(name) + Pathname.new(Dir.mktmpdir(["bundler", name])) end - def cleanup - FileUtils.remove_entry_secure(@tmp) if @tmp - rescue + def rm_rf(path) + FileUtils.remove_entry_secure(path) if path && File.exist?(path) end def settings diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 7d9d9d949c..37844de4d3 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -11,8 +11,6 @@ module Bundler rescue Exception => e Bundler.ui = UI::Shell.new raise e - ensure - Bundler.cleanup end def initialize(*args) @@ -20,7 +18,7 @@ module Bundler current_cmd = args.last[:current_command].name custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile] ENV['BUNDLE_GEMFILE'] = File.expand_path(custom_gemfile) if custom_gemfile - Bundler::Retry.attempts = options[:retry] || Bundler.settings[:retry] || Bundler::Retry::DEFAULT_ATTEMPTS + Bundler.settings[:retry] = options[:retry] if options[:retry] Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui) auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) rescue UnknownArgumentError => e @@ -134,6 +132,8 @@ module Bundler "Do not attempt to fetch gems remotely and use the gem cache instead" method_option "cache", :type => :boolean, :banner => "Update the existing gem cache." + method_option "force", :type => :boolean, :banner => + "Force downloading every gem." method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache." method_option "path", :type => :string, :banner => @@ -151,6 +151,8 @@ module Bundler Bundler.rubygems.security_policy_keys.join('|') method_option "without", :type => :array, :banner => "Exclude gems that are part of the specified named group." + method_option "with", :type => :array, :banner => + "Include gems that are part of the specified named group." def install require 'bundler/cli/install' @@ -175,6 +177,8 @@ module Bundler "Only output warnings and errors." method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)" + method_option "force", :type => :boolean, :banner => + "Force downloading every gem." def update(*gems) require 'bundler/cli/update' Update.new(options, gems).run diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index 9ce8de4a05..f58e12a594 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -20,9 +20,9 @@ module Bundler underscored_name = name.tr('-', '_') namespaced_path = name.tr('-', '/') - constant_name = name.split('_').map{|p| p[0..0].upcase + p[1..-1] unless p.empty?}.join - constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/ + constant_name = name.gsub(/-[_-]*(?![_-]|$)/){ '::' }.gsub(/([_-]+|(::)|^)(.|$)/){ $2.to_s + $3.upcase } constant_array = constant_name.split('::') + git_user_name = `git config user.name`.chomp git_user_email = `git config user.email`.chomp @@ -54,6 +54,11 @@ module Bundler "bin/setup.tt" => "bin/setup" } + executables = %w[ + bin/console + bin/setup + ] + if ask_and_set(:coc, "Do you want to include a code of conduct in gems you generate?", "Codes of conduct can increase contributions to your project by contributors who " \ "prefer collaborative, safe spaces. You can read more about the code of conduct at " \ @@ -61,6 +66,7 @@ module Bundler "of enforcing it, so be sure that you are prepared to do that. For suggestions about " \ "how to enforce codes of conduct, see bit.ly/coc-enforcement." ) + Bundler.ui.info "Code of conduct enabled in config" templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md") end @@ -70,10 +76,12 @@ module Bundler "at choosealicense.com/licenses/mit." ) config[:mit] = true + Bundler.ui.info "MIT License enabled in config" templates.merge!("LICENSE.txt.tt" => "LICENSE.txt") end if test_framework = ask_and_set_test_framework + config[:test] = test_framework templates.merge!(".travis.yml.tt" => ".travis.yml") case test_framework @@ -85,8 +93,8 @@ module Bundler ) when 'minitest' templates.merge!( - "test/minitest_helper.rb.tt" => "test/minitest_helper.rb", - "test/test_newgem.rb.tt" => "test/test_#{namespaced_path}.rb" + "test/test_helper.rb.tt" => "test/test_helper.rb", + "test/newgem_test.rb.tt" => "test/#{namespaced_path}_test.rb" ) end end @@ -105,6 +113,12 @@ module Bundler thor.template("newgem/#{src}", target.join(dst), config) end + executables.each do |file| + path = target.join(file) + executable = (path.stat.mode | 0111) + path.chmod(executable) + end + Bundler.ui.info "Initializing git repo in #{target}" Dir.chdir(target) { `git init`; `git add .` } @@ -125,7 +139,7 @@ module Bundler if choice.nil? Bundler.ui.confirm header - choice = (Bundler.ui.ask("#{message} y/(n):") =~ /y|yes/) + choice = Bundler.ui.yes? "#{message} y/(n):" Bundler.settings.set_global("gem.#{key}", choice) end @@ -174,7 +188,7 @@ module Bundler if name =~ /^\d/ Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers." exit 1 - elsif Object.const_defined?(constant_array.first) + elsif constant_array.inject(Object) {|c, s| (c.const_defined?(s) && c.const_get(s)) || break } Bundler.ui.error "Invalid gem name #{name} constant #{constant_array.join("::")} is already in use. Please choose another gem name." exit 1 end diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index e88c093538..7d09444596 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -10,10 +10,35 @@ module Bundler warn_if_root - if options[:without] - options[:without] = options[:without].map{|g| g.tr(' ', ':') } + [:with, :without].each do |option| + if options[option] + options[option] = options[option].join(":").tr(" ", ":").split(":") + end + end + + if options[:without] && options[:with] + conflicting_groups = options[:without] & options[:with] + unless conflicting_groups.empty? + Bundler.ui.error "You can't list a group in both, --with and --without." \ + "The offending groups are: #{conflicting_groups.join(", ")}." + exit 1 + end end + Bundler.settings.with = [] if options[:with] && options[:with].empty? + Bundler.settings.without = [] if options[:without] && options[:without].empty? + + with = options.fetch("with", []) + with |= Bundler.settings.with.map {|group| group.to_s } + with -= options[:without] if options[:without] + + without = options.fetch("without", []) + without |= Bundler.settings.without.map {|group| group.to_s } + without -= options[:with] if options[:with] + + options[:with] = with + options[:without] = without + ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD # Just disable color in deployment mode @@ -69,6 +94,7 @@ module Bundler Bundler.settings[:no_install] = true if options["no-install"] Bundler.settings[:clean] = options["clean"] if options["clean"] Bundler.settings.without = options[:without] + Bundler.settings.with = options[:with] Bundler::Fetcher.disable_endpoint = options["full-index"] Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil @@ -91,9 +117,10 @@ module Bundler Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed." end - Installer.post_install_messages.to_a.each do |name, msg| - Bundler.ui.confirm "Post-install message from #{name}:" - Bundler.ui.info msg + unless Bundler.settings["ignore_messages"] + Installer.post_install_messages.to_a.each do |name, msg| + print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"] + end end Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris| @@ -152,5 +179,10 @@ module Bundler "#{count} #{count == 1 ? 'gem' : 'gems'} now installed" end + def print_post_install_message(name, msg) + Bundler.ui.confirm "Post-install message from #{name}:" + Bundler.ui.info msg + end + end end diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb index f8779d762a..08bcbde609 100644 --- a/lib/bundler/cli/outdated.rb +++ b/lib/bundler/cli/outdated.rb @@ -62,8 +62,15 @@ module Bundler spec_version = "#{active_spec.version}#{active_spec.git_version}" current_version = "#{current_spec.version}#{current_spec.git_version}" - dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific? - Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip + dependency_version = %|, requested #{dependency.requirement}| if dependency && dependency.specific? + + if dependency + groups = dependency.groups.join(", ") + pl = (dependency.groups.length > 1) ? "s" : "" + groups = " in group#{pl} \"#{groups}\"" + end + + Bundler.ui.info " * #{active_spec.name} (newest #{spec_version}, installed #{current_version}#{dependency_version})#{groups}".rstrip out_count += 1 end Bundler.ui.debug "from #{active_spec.loaded_from}" diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 4c15866577..eb97b81a5d 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -43,10 +43,11 @@ module Bundler # @param unlock [Hash, Boolean, nil] Gems that have been requested # to be updated or true if all gems should be updated # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version - def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil) + # @param optional_groups [Array(String)] A list of optional groups + def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = []) @unlocking = unlock == true || !unlock.empty? - @dependencies, @sources, @unlock = dependencies, sources, unlock + @dependencies, @sources, @unlock, @optional_groups = dependencies, sources, unlock, optional_groups @remote = false @specs = nil @lockfile_contents = "" @@ -56,6 +57,7 @@ module Bundler @lockfile_contents = Bundler.read_file(lockfile) locked = LockfileParser.new(@lockfile_contents) @platforms = locked.platforms + @locked_bundler_version = locked.bundler_version if unlock != true @locked_deps = locked.dependencies @@ -161,7 +163,7 @@ module Bundler def requested_specs @requested_specs ||= begin - groups = self.groups - Bundler.settings.without + groups = requested_groups groups.map! { |g| g.to_sym } specs_for(groups) end @@ -184,11 +186,10 @@ module Bundler # @return [SpecSet] resolved dependencies def resolve @resolve ||= begin + last_resolve = converge_locked_specs if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?) - @locked_specs + last_resolve else - last_resolve = converge_locked_specs - # Run a resolve against the locally available gems last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve) end @@ -248,6 +249,16 @@ module Bundler return end + if @locked_bundler_version + locked_major = @locked_bundler_version.segments.first + current_major = Gem::Version.create(Bundler::VERSION).segments.first + + if locked_major < current_major + Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{Bundler::VERSION.split('.').first}, " \ + "after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}." + end + end + File.open(file, 'wb'){|f| f.puts(contents) } rescue Errno::EACCES raise Bundler::InstallError, @@ -256,6 +267,16 @@ module Bundler "#{File.expand_path(file)}" end + # Returns the version of Bundler that is creating or has created + # Gemfile.lock. Used in #to_lock. + def lock_version + if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION) + new_version = Bundler::VERSION + end + + new_version || @locked_bundler_version || Bundler::VERSION + end + def to_lock out = "" @@ -272,7 +293,7 @@ module Bundler each do |spec| next if spec.name == 'bundler' out << spec.to_lock - end + end out << "\n" end @@ -294,6 +315,10 @@ module Bundler handled << dep.name end + # Record the version of Bundler that was used to create the lockfile + out << "\nBUNDLED WITH\n" + out << " #{lock_version}\n" + out end @@ -521,7 +546,9 @@ module Bundler converged = [] @locked_specs.each do |s| - s.source = sources.get(s.source) + # Replace the locked dependency's source with the equivalent source from the Gemfile + dep = @dependencies.find { |d| s.satisfies?(d) } + s.source = (dep && dep.source) || sources.get(s.source) # Don't add a spec to the list if its source is expired. For example, # if you change a Git gem to Rubygems. @@ -561,12 +588,15 @@ module Bundler resolve end - def in_locked_deps?(dep, d) - d && dep.source == d.source + def in_locked_deps?(dep, locked_dep) + # Because the lockfile can't link a dep to a specific remote, we need to + # treat sources as equivalent anytime the locked dep has all the remotes + # that the Gemfile dep does. + locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source) end def satisfies_locked_spec?(dep) - @locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source == dep.source) } + @locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) } end def expanded_dependencies @@ -586,7 +616,7 @@ module Bundler end def requested_dependencies - groups = self.groups - Bundler.settings.without + groups = requested_groups groups.map! { |g| g.to_sym } dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? } end @@ -620,5 +650,8 @@ module Bundler names end + def requested_groups + self.groups - Bundler.settings.without - @optional_groups + Bundler.settings.with + end end end diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 6a9284fd56..e58ed4b7d5 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -50,11 +50,12 @@ module Bundler type = options["type"] || :runtime super(name, version, type) - @autorequire = nil - @groups = Array(options["group"] || :default).map { |g| g.to_sym } - @source = options["source"] - @platforms = Array(options["platforms"]) - @env = options["env"] + @autorequire = nil + @groups = Array(options["group"] || :default).map { |g| g.to_sym } + @source = options["source"] + @platforms = Array(options["platforms"]) + @env = options["env"] + @should_include = options.fetch("should_include", true) if options.key?('require') @autorequire = Array(options['require'] || []) @@ -74,7 +75,7 @@ module Bundler end def should_include? - current_env? && current_platform? + @should_include && current_env? && current_platform? end def current_env? diff --git a/lib/bundler/deployment.rb b/lib/bundler/deployment.rb new file mode 100644 index 0000000000..2a76ccc859 --- /dev/null +++ b/lib/bundler/deployment.rb @@ -0,0 +1,66 @@ +$stderr.puts "DEPRECATION: Bundler no longer integrates with " \ + "Capistrano, but Capistrano provides its own integration with " \ + "Bundler via the capistrano-bundler gem. Use it instead." + +module Bundler + class Deployment + def self.define_task(context, task_method = :task, opts = {}) + if defined?(Capistrano) && context.is_a?(Capistrano::Configuration) + context_name = "capistrano" + role_default = "{:except => {:no_release => true}}" + error_type = ::Capistrano::CommandError + else + context_name = "vlad" + role_default = "[:app]" + error_type = ::Rake::CommandFailedError + end + + roles = context.fetch(:bundle_roles, false) + opts[:roles] = roles if roles + + context.send :namespace, :bundle do + send :desc, <<-DESC + Install the current Bundler environment. By default, gems will be \ + installed to the shared/bundle path. Gems in the development and \ + test group will not be installed. The install command is executed \ + with the --deployment and --quiet flags. If the bundle cmd cannot \ + be found then you can override the bundle_cmd variable to specify \ + which one it should use. The base path to the app is fetched from \ + the :latest_release variable. Set it for custom deploy layouts. + + You can override any of these defaults by setting the variables shown below. + + N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \ + in your deploy.rb file. + + set :bundle_gemfile, "Gemfile" + set :bundle_dir, File.join(fetch(:shared_path), 'bundle') + set :bundle_flags, "--deployment --quiet" + set :bundle_without, [:development, :test] + set :bundle_with, [:mysql] + set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle" + set :bundle_roles, #{role_default} # e.g. [:app, :batch] + DESC + send task_method, :install, opts do + bundle_cmd = context.fetch(:bundle_cmd, "bundle") + bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet") + bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle')) + bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile") + bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact + bundle_with = [*context.fetch(:bundle_with, [])].compact + app_path = context.fetch(:latest_release) + if app_path.to_s.empty? + raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.") + end + args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"] + args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty? + args << bundle_flags.to_s + args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty? + args << "--with #{bundle_with.join(" ")}" unless bundle_with.empty? + + run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}" + end + end + end + end +end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index f9f09e8b00..dfefc59785 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -16,14 +16,16 @@ module Bundler attr_accessor :dependencies def initialize - @source = nil - @sources = SourceList.new - @git_sources = {} - @dependencies = [] - @groups = [] - @platforms = [] - @env = nil - @ruby_version = nil + @source = nil + @sources = SourceList.new + @git_sources = {} + @dependencies = [] + @groups = [] + @install_conditionals = [] + @optional_groups = [] + @platforms = [] + @env = nil + @ruby_version = nil add_git_sources end @@ -37,6 +39,7 @@ module Bundler def gemspec(opts = nil) path = opts && opts[:path] || '.' + glob = opts && opts[:glob] name = opts && opts[:name] || '{,*}' development_group = opts && opts[:development_group] || :development expanded_path = File.expand_path(path, Bundler.default_gemfile.dirname) @@ -47,7 +50,7 @@ module Bundler when 1 spec = Bundler.load_gemspec(gemspecs.first) raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec - gem spec.name, :path => path + gem spec.name, :path => path, :glob => glob group(development_group) do spec.development_dependencies.each do |dep| gem dep.name, *(dep.requirement.as_list + [:type => :development]) @@ -153,16 +156,32 @@ module Bundler end def to_definition(lockfile, unlock) - Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version) + Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups) end def group(*args, &blk) + opts = Hash === args.last ? args.pop.dup : {} + normalize_group_options(opts, args) + @groups.concat args + + if opts["optional"] + optional_groups = args - @optional_groups + @optional_groups.concat optional_groups + end + yield ensure args.each { @groups.pop } end + def install_if(*args, &blk) + @install_conditionals.concat args + blk.call + ensure + args.each { @groups.pop } + end + def platforms(*platforms) @platforms.concat platforms yield @@ -187,12 +206,24 @@ module Bundler def add_git_sources git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - "git://github.com/#{repo_name}.git" + # TODO 2.0 upgrade this setting to the default + if Bundler.settings["github.https"] + "https://github.com/#{repo_name}.git" + else + warn_github_source_change(repo_name) + "git://github.com/#{repo_name}.git" + end end - git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" } + # TODO 2.0 remove this deprecated git source + git_source(:gist) do |repo_name| + warn_deprecated_git_source(:gist, 'https://gist.github.com/#{repo_name}.git') + "https://gist.github.com/#{repo_name}.git" + end + # TODO 2.0 remove this deprecated git source git_source(:bitbucket) do |repo_name| + warn_deprecated_git_source(:bitbucket, 'https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git') user_name, repo_name = repo_name.split '/' repo_name ||= user_name "https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git" @@ -217,7 +248,7 @@ module Bundler end def valid_keys - @valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source) + @valid_keys ||= %w(group groups git path glob name branch ref tag require submodules platform platforms type source install_if) end def normalize_options(name, version, opts) @@ -231,25 +262,19 @@ module Bundler normalize_hash(opts) git_names = @git_sources.keys.map(&:to_s) - - invalid_keys = opts.keys - (valid_keys + git_names) - if invalid_keys.any? - message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} " - message << if invalid_keys.size > 1 - "as options for gem '#{name}', but they are invalid." - else - "as an option for gem '#{name}', but it is invalid." - end - - message << " Valid options are: #{valid_keys.join(", ")}" - raise InvalidOption, message - end + validate_keys("gem '#{name}'", opts, valid_keys + git_names) groups = @groups.dup opts["group"] = opts.delete("groups") || opts["group"] groups.concat Array(opts.delete("group")) groups = [:default] if groups.empty? + install_if = @install_conditionals.dup + install_if.concat Array(opts.delete("install_if")) + install_if = install_if.reduce(true) do |memo, val| + memo && (val.respond_to?(:call) ? val.call : val) + end + platforms = @platforms.dup opts["platforms"] = opts["platform"] || opts["platforms"] platforms.concat Array(opts.delete("platforms")) @@ -282,10 +307,35 @@ module Bundler end end - opts["source"] ||= @source - opts["env"] ||= @env - opts["platforms"] = platforms.dup - opts["group"] = groups + opts["source"] ||= @source + opts["env"] ||= @env + opts["platforms"] = platforms.dup + opts["group"] = groups + opts["should_include"] = install_if + end + + def normalize_group_options(opts, groups) + normalize_hash(opts) + + groups = groups.map {|group| ":#{group}" }.join(", ") + validate_keys("group #{groups}", opts, %w(optional)) + + opts["optional"] ||= false + end + + def validate_keys(command, opts, valid_keys) + invalid_keys = opts.keys - valid_keys + if invalid_keys.any? + message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} " + message << if invalid_keys.size > 1 + "as options for #{command}, but they are invalid." + else + "as an option for #{command}, but it is invalid." + end + + message << " Valid options are: #{valid_keys.join(", ")}" + raise InvalidOption, message + end end def normalize_source(source) @@ -305,21 +355,37 @@ module Bundler def check_primary_source_safety(source) return unless source.rubygems_primary_remotes.any? + # TODO 2.0 upgrade from setting to default if Bundler.settings[:disable_multisource] raise GemspecError, "Warning: this Gemfile contains multiple primary sources. " \ "Each source after the first must include a block to indicate which gems " \ - "should come from that source. To downgrade this error to a warning, run " \ - "`bundle config --delete disable_multisource`." + "should come from that source." else - Bundler.ui.warn "Warning: this Gemfile contains multiple primary sources. " \ + Bundler.ui.deprecate "Your Gemfile contains multiple primary sources. " \ "Using `source` more than once without a block is a security risk, and " \ "may result in installing unexpected gems. To resolve this warning, use " \ "a block to indicate which gems should come from the secondary source. " \ - "To upgrade this warning to an error, run `bundle config " \ - "disable_multisource true`." + "It will be an error to have multiple primary sources in Bundler 2.0. " \ + "To enable that error now, run `bundle config disable_multisource true`." end end + def warn_github_source_change(repo_name) + # TODO 2.0 remove deprecation + Bundler.ui.deprecate "The :github option uses the git: protocol, which is not secure. " \ + "Bundler 2.0 will use the https: protcol, which is secure. Enable this change now by " \ + "running `bundle config github.https true`." + end + + def warn_deprecated_git_source(name, repo_string) + # TODO 2.0 remove deprecation + Bundler.ui.deprecate "The :#{name} git source is deprecated, and will be removed " \ + "in Bundler 2.0. Add this code to your Gemfile to ensure it continues to work:\n" \ + " git_source(:#{name}) do |repo_name|\n" \ + " #{repo_string}\n" \ + " end", true + end + class DSLError < GemfileError # @return [String] the description that should be presented to the user. # @@ -422,4 +488,5 @@ module Bundler end end + end diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb index a60adcabb2..42c6eb6717 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -1,11 +1,15 @@ require 'bundler/vendored_persistent' -require 'securerandom' require 'cgi' +require 'securerandom' module Bundler # Handles all the fetching with the rubygems server class Fetcher + autoload :Downloader, 'bundler/fetcher/downloader' + autoload :Dependency, 'bundler/fetcher/dependency' + autoload :Index, 'bundler/fetcher/index' + # This error is raised when it looks like the network is down class NetworkDownError < HTTPError; end # This error is raised if the API returns a 413 (only printed in verbose) @@ -52,93 +56,19 @@ module Bundler class << self attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries - - def download_gem_from_uri(spec, uri) - spec.fetch_platform - - download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir - gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem" - - FileUtils.mkdir_p("#{download_path}/cache") - Bundler.rubygems.download_gem(spec, uri, download_path) - - if Bundler.requires_sudo? - Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache" - Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}" - end - - gem_path - end - - def user_agent - @user_agent ||= begin - ruby = Bundler.ruby_version - - agent = "bundler/#{Bundler::VERSION}" - agent << " rubygems/#{Gem::VERSION}" - agent << " ruby/#{ruby.version}" - agent << " (#{ruby.host})" - agent << " command/#{ARGV.first}" - - if ruby.engine != "ruby" - # engine_version raises on unknown engines - engine_version = ruby.engine_version rescue "???" - agent << " #{ruby.engine}/#{engine_version}" - end - - agent << " options/#{Bundler.settings.all.join(",")}" - - # add a random ID so we can consolidate runs server-side - agent << " " << SecureRandom.hex(8) - - # add any user agent strings set in the config - extra_ua = Bundler.settings[:user_agent] - agent << " " << extra_ua if extra_ua - - agent - end - end - end - def initialize(remote) - @redirect_limit = 5 # How many redirects to allow in one request - @api_timeout = 10 # How long to wait for each API call - @max_retries = 3 # How many retries for the API call + self.redirect_limit = Bundler.settings[:redirect] # How many redirects to allow in one request + self.api_timeout = Bundler.settings[:timeout] # How long to wait for each API call + self.max_retries = Bundler.settings[:retry] # How many retries for the API call + def initialize(remote) @remote = remote Socket.do_not_reverse_lookup = true connection # create persistent connection end - def connection - @connection ||= begin - needs_ssl = remote_uri.scheme == "https" || - Bundler.settings[:ssl_verify_mode] || - Bundler.settings[:ssl_client_cert] - raise SSLError if needs_ssl && !defined?(OpenSSL::SSL) - - con = Net::HTTP::Persistent.new 'bundler', :ENV - - if remote_uri.scheme == "https" - con.verify_mode = (Bundler.settings[:ssl_verify_mode] || - OpenSSL::SSL::VERIFY_PEER) - con.cert_store = bundler_cert_store - end - - if Bundler.settings[:ssl_client_cert] - pem = File.read(Bundler.settings[:ssl_client_cert]) - con.cert = OpenSSL::X509::Certificate.new(pem) - con.key = OpenSSL::PKey::RSA.new(pem) - end - - con.read_timeout = @api_timeout - con.override_headers["User-Agent"] = self.class.user_agent - con - end - end - def uri @remote.anonymized_uri end @@ -154,37 +84,26 @@ module Bundler elsif cached_spec_path = gemspec_cached_path(spec_file_name) Bundler.load_gemspec(cached_spec_path) else - Bundler.load_marshal Gem.inflate(fetch(uri)) + Bundler.load_marshal Gem.inflate(downloader.fetch uri) end rescue MarshalError raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \ "Your network or your gem server is probably having issues right now." end - # cached gem specification path, if one exists - def gemspec_cached_path spec_file_name - paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) } - paths = paths.select {|path| File.file? path } - paths.first - end - # return the specs in the bundler format as an index def specs(gem_names, source) old = Bundler.rubygems.sources - index = Index.new - - if gem_names && use_api - specs = fetch_remote_specs(gem_names) - end - - if specs.nil? - # API errors mean we should treat this as a non-API source - @use_api = false + index = Bundler::Index.new - specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do - fetch_all_remote_specs + specs = {} + fetchers.dup.each do |f| + unless f.api_fetcher? && !gem_names + break if specs = f.specs(gem_names) end + fetchers.delete(f) end + @use_api = false if fetchers.none?(&:api_fetcher?) specs[remote_uri].each do |name, version, platform, dependencies| next if name == 'bundler' @@ -200,184 +119,106 @@ module Bundler end index - rescue CertificateFailureError => e + rescue CertificateFailureError Bundler.ui.info "" if gem_names && use_api # newline after dots - raise e + raise ensure Bundler.rubygems.sources = old end - # fetch index - def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = []) - query_list = gem_names - full_dependency_list - - # only display the message on the first run - if Bundler.ui.debug? - Bundler.ui.debug "Query List: #{query_list.inspect}" - else - Bundler.ui.info ".", false - end - - return {remote_uri => last_spec_list} if query_list.empty? - - remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do - fetch_dependency_remote_specs(query_list) - end - - spec_list, deps_list = remote_specs - returned_gems = spec_list.map {|spec| spec.first }.uniq - fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list) - rescue HTTPError, MarshalError, GemspecError - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - Bundler.ui.debug "could not fetch from the dependency API, trying the full index" - @use_api = false - return nil - end - def use_api return @use_api if defined?(@use_api) if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint @use_api = false - elsif fetch(dependency_api_uri) - @use_api = true + else + fetchers.reject! { |f| f.api_fetcher? && !f.api_available? } + @use_api = fetchers.any?(&:api_fetcher?) end - rescue NetworkDownError => e - raise HTTPError, e.message - rescue AuthenticationRequiredError - # We got a 401 from the server. Don't fall back to the full index, just fail. - raise - rescue HTTPError - @use_api = false end - def inspect - "#<#{self.class}:0x#{object_id} uri=#{uri}>" - end + def user_agent + @user_agent ||= begin + ruby = Bundler.ruby_version - private + agent = "bundler/#{Bundler::VERSION}" + agent << " rubygems/#{Gem::VERSION}" + agent << " ruby/#{ruby.version}" + agent << " (#{ruby.host})" + agent << " command/#{ARGV.first}" - HTTP_ERRORS = [ - Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, - Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN, - Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, - Net::HTTP::Persistent::Error - ] + if ruby.engine != "ruby" + # engine_version raises on unknown engines + engine_version = ruby.engine_version rescue "???" + agent << " #{ruby.engine}/#{engine_version}" + end - def fetch(uri, counter = 0) - raise HTTPError, "Too many redirects" if counter >= @redirect_limit + agent << " options/#{Bundler.settings.all.join(",")}" - response = request(uri) - Bundler.ui.debug("HTTP #{response.code} #{response.message}") + # add a random ID so we can consolidate runs server-side + agent << " " << SecureRandom.hex(8) - case response - when Net::HTTPRedirection - new_uri = URI.parse(response["location"]) - if new_uri.host == uri.host - new_uri.user = uri.user - new_uri.password = uri.password - end - fetch(new_uri, counter + 1) - when Net::HTTPSuccess - response.body - when Net::HTTPRequestEntityTooLarge - raise FallbackError, response.body - when Net::HTTPUnauthorized - raise AuthenticationRequiredError, remote_uri.host - else - raise HTTPError, "#{response.class}: #{response.body}" + # add any user agent strings set in the config + extra_ua = Bundler.settings[:user_agent] + agent << " " << extra_ua if extra_ua + + agent end end - def request(uri) - Bundler.ui.debug "HTTP GET #{uri}" - req = Net::HTTP::Get.new uri.request_uri - if uri.user - user = CGI.unescape(uri.user) - password = uri.password ? CGI.unescape(uri.password) : nil - req.basic_auth(user, password) - end - connection.request(uri, req) - rescue OpenSSL::SSL::SSLError - raise CertificateFailureError.new(uri) - rescue *HTTP_ERRORS => e - Bundler.ui.trace e - case e.message - when /host down:/, /getaddrinfo: nodename nor servname provided/ - raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \ - "connection and try again." - else - raise HTTPError, "Network error while fetching #{uri}" - end + def fetchers + @fetchers ||= FETCHERS.map { |f| f.new(downloader, remote_uri, fetch_uri, uri) } end - def dependency_api_uri(gem_names = []) - uri = fetch_uri + "api/v1/dependencies" - uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any? - uri + def inspect + "#<#{self.class}:0x#{object_id} uri=#{uri}>" end - # fetch from Gemcutter Dependency Endpoint API - def fetch_dependency_remote_specs(gem_names) - Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}" - gem_list = [] - deps_list = [] + private - gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names| - marshalled_deps = fetch dependency_api_uri(names) - gem_list += Bundler.load_marshal(marshalled_deps) - end + FETCHERS = [Dependency, Index] - spec_list = gem_list.map do |s| - dependencies = s[:dependencies].map do |name, requirement| - dep = well_formed_dependency(name, requirement.split(", ")) - deps_list << dep.name - dep - end + def connection + @connection ||= begin + needs_ssl = remote_uri.scheme == "https" || + Bundler.settings[:ssl_verify_mode] || + Bundler.settings[:ssl_client_cert] + raise SSLError if needs_ssl && !defined?(OpenSSL::SSL) - [s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies] - end + con = Net::HTTP::Persistent.new 'bundler', :ENV - [spec_list, deps_list.uniq] - end + if remote_uri.scheme == "https" + con.verify_mode = (Bundler.settings[:ssl_verify_mode] || + OpenSSL::SSL::VERIFY_PEER) + con.cert_store = bundler_cert_store + end - # fetch from modern index: specs.4.8.gz - def fetch_all_remote_specs - old_sources = Bundler.rubygems.sources - Bundler.rubygems.sources = [remote_uri.to_s] - Bundler.rubygems.fetch_all_remote_specs - rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e - case e.message - when /certificate verify failed/ - raise CertificateFailureError.new(uri) - when /401/ - raise AuthenticationRequiredError, remote_uri - when /403/ - if remote_uri.userinfo - raise BadAuthenticationError, remote_uri - else - raise AuthenticationRequiredError, remote_uri + if Bundler.settings[:ssl_client_cert] + pem = File.read(Bundler.settings[:ssl_client_cert]) + con.cert = OpenSSL::X509::Certificate.new(pem) + con.key = OpenSSL::PKey::RSA.new(pem) end - else - Bundler.ui.trace e - raise HTTPError, "Could not fetch specs from #{uri}" + + con.read_timeout = Fetcher.api_timeout + con.override_headers["User-Agent"] = user_agent + con end - ensure - Bundler.rubygems.sources = old_sources end - def well_formed_dependency(name, *requirements) - Gem::Dependency.new(name, *requirements) - rescue ArgumentError => e - illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey' - raise e unless e.message.include?(illformed) - puts # we shouldn't print the error message on the "fetching info" status line - raise GemspecError, - "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \ - "gemspec. \nPlease ask the gem author to yank the bad version to fix " \ - "this issue. For more information, see http://bit.ly/syck-defaultkey." + # cached gem specification path, if one exists + def gemspec_cached_path spec_file_name + paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) } + paths = paths.select {|path| File.file? path } + paths.first end + HTTP_ERRORS = [ + Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, + Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN, + Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, + Net::HTTP::Persistent::Error + ] + def bundler_cert_store store = OpenSSL::X509::Store.new if Bundler.settings[:ssl_ca_cert] @@ -411,5 +252,10 @@ module Bundler def remote_uri @remote.uri end + + def downloader + @downloader ||= Downloader.new(connection, self.class.redirect_limit) + end + end end diff --git a/lib/bundler/fetcher/base.rb b/lib/bundler/fetcher/base.rb new file mode 100644 index 0000000000..31d6f8a09b --- /dev/null +++ b/lib/bundler/fetcher/base.rb @@ -0,0 +1,27 @@ +module Bundler + class Fetcher + class Base + attr_reader :downloader + attr_reader :remote_uri + attr_reader :fetch_uri + attr_reader :display_uri + + def initialize(downloader, remote_uri, fetch_uri, display_uri) + raise 'Abstract class' if self.class == Base + @downloader = downloader + @remote_uri = remote_uri + @fetch_uri = fetch_uri + @display_uri = display_uri + end + + def api_available? + api_fetcher? + end + + def api_fetcher? + false + end + + end + end +end diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb new file mode 100644 index 0000000000..ca36dcb53f --- /dev/null +++ b/lib/bundler/fetcher/dependency.rb @@ -0,0 +1,88 @@ +require 'bundler/fetcher/base' + +module Bundler + class Fetcher + class Dependency < Base + def api_available? + downloader.fetch(dependency_api_uri) + rescue NetworkDownError => e + raise HTTPError, e.message + rescue AuthenticationRequiredError + # We got a 401 from the server. Just fail. + raise + rescue HTTPError + end + + def api_fetcher? + true + end + + def specs(gem_names, full_dependency_list = [], last_spec_list = []) + query_list = gem_names - full_dependency_list + + # only display the message on the first run + if Bundler.ui.debug? + Bundler.ui.debug "Query List: #{query_list.inspect}" + else + Bundler.ui.info ".", false + end + + return {remote_uri => last_spec_list} if query_list.empty? + + remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do + dependency_specs(query_list) + end + + spec_list, deps_list = remote_specs + returned_gems = spec_list.map(&:first).uniq + specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list) + rescue HTTPError, MarshalError, GemspecError + Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over + Bundler.ui.debug "could not fetch from the dependency API, trying the full index" + return nil + end + + def dependency_specs(gem_names) + Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}" + gem_list = [] + deps_list = [] + + gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names| + marshalled_deps = downloader.fetch dependency_api_uri(names) + gem_list += Bundler.load_marshal(marshalled_deps) + end + + spec_list = gem_list.map do |s| + dependencies = s[:dependencies].map do |name, requirement| + dep = well_formed_dependency(name, requirement.split(", ")) + deps_list << dep.name + dep + end + + [s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies] + end + + [spec_list, deps_list.uniq] + end + + def dependency_api_uri(gem_names = []) + uri = fetch_uri + "api/v1/dependencies" + uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any? + uri + end + + def well_formed_dependency(name, *requirements) + Gem::Dependency.new(name, *requirements) + rescue ArgumentError => e + illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey' + raise e unless e.message.include?(illformed) + puts # we shouldn't print the error message on the "fetching info" status line + raise GemspecError, + "Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \ + "gemspec. \nPlease ask the gem author to yank the bad version to fix " \ + "this issue. For more information, see http://bit.ly/syck-defaultkey." + end + + end + end +end diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb new file mode 100644 index 0000000000..b480b4bae9 --- /dev/null +++ b/lib/bundler/fetcher/downloader.rb @@ -0,0 +1,61 @@ +module Bundler + class Fetcher + class Downloader + attr_reader :connection + attr_reader :redirect_limit + + def initialize(connection, redirect_limit) + @connection = connection + @redirect_limit = redirect_limit + end + + def fetch(uri, counter = 0) + raise HTTPError, "Too many redirects" if counter >= redirect_limit + + response = request(uri) + Bundler.ui.debug("HTTP #{response.code} #{response.message}") + + case response + when Net::HTTPRedirection + new_uri = URI.parse(response["location"]) + if new_uri.host == uri.host + new_uri.user = uri.user + new_uri.password = uri.password + end + fetch(new_uri, counter + 1) + when Net::HTTPSuccess + response.body + when Net::HTTPRequestEntityTooLarge + raise FallbackError, response.body + when Net::HTTPUnauthorized + raise AuthenticationRequiredError, uri.host + else + raise HTTPError, "#{response.class}: #{response.body}" + end + end + + def request(uri) + Bundler.ui.debug "HTTP GET #{uri}" + req = Net::HTTP::Get.new uri.request_uri + if uri.user + user = CGI.unescape(uri.user) + password = uri.password ? CGI.unescape(uri.password) : nil + req.basic_auth(user, password) + end + connection.request(uri, req) + rescue OpenSSL::SSL::SSLError + raise CertificateFailureError.new(uri) + rescue *HTTP_ERRORS => e + Bundler.ui.trace e + case e.message + when /host down:/, /getaddrinfo: nodename nor servname provided/ + raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \ + "connection and try again." + else + raise HTTPError, "Network error while fetching #{uri}" + end + end + + end + end +end
\ No newline at end of file diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb new file mode 100644 index 0000000000..f9bd279d0d --- /dev/null +++ b/lib/bundler/fetcher/index.rb @@ -0,0 +1,31 @@ +require 'bundler/fetcher/base' + +module Bundler + class Fetcher + class Index < Base + def specs(_gem_names) + old_sources = Bundler.rubygems.sources + Bundler.rubygems.sources = [remote_uri.to_s] + Bundler.rubygems.fetch_all_remote_specs + rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e + case e.message + when /certificate verify failed/ + raise CertificateFailureError.new(display_uri) + when /401/ + raise AuthenticationRequiredError, remote_uri + when /403/ + if remote_uri.userinfo + raise BadAuthenticationError, remote_uri + else + raise AuthenticationRequiredError, remote_uri + end + else + Bundler.ui.trace e + raise HTTPError, "Could not fetch specs from #{display_uri}" + end + ensure + Bundler.rubygems.sources = old_sources + end + end + end +end diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb index 4bd3e53381..9d4389e398 100644 --- a/lib/bundler/friendly_errors.rb +++ b/lib/bundler/friendly_errors.rb @@ -44,18 +44,27 @@ module Bundler def self.request_issue_report_for(e) Bundler.ui.info <<-EOS.gsub(/^ {6}/, '') - #{'――― ERROR REPORT TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――'} + #{'--- ERROR REPORT TEMPLATE -------------------------------------------------------'} - What did you do? + + I ran the command `#{$PROGRAM_NAME} #{ARGV.join(' ')}` + - What did you expect to happen? + + I expected Bundler to... + - What happened instead? + Instead, what actually happened was... + + Error details #{e.class}: #{e.message} - #{e.backtrace.join("\n ")} + #{e.backtrace.join("\n ")} #{Bundler::Env.new.report(:print_gemfile => false).gsub(/\n/, "\n ").strip} - #{'――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'} + #{'--- TEMPLATE END ----------------------------------------------------------------'} EOS @@ -73,7 +82,7 @@ module Bundler def self.issues_url(exception) 'https://github.com/bundler/bundler/search?q=' \ - "#{CGI.escape(exception.message)}&type=Issues" + "#{CGI.escape(exception.message.lines.first.chomp)}&type=Issues" end end diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb index 6db31901e4..9aac3e0d75 100644 --- a/lib/bundler/gem_helper.rb +++ b/lib/bundler/gem_helper.rb @@ -44,6 +44,11 @@ module Bundler install_gem(built_gem_path) end + desc "Build and install #{name}-#{version}.gem into system gems without network access." + task 'install:local' => 'build' do + install_gem(built_gem_path, :local) + end + desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems\n" \ "To prevent publishing in Rubygems use `gem_push=no rake release`" task 'release' => ['build', 'release:guard_clean', @@ -76,9 +81,9 @@ module Bundler File.join(base, 'pkg', file_name) end - def install_gem(built_gem_path=nil) + def install_gem(built_gem_path = nil, local = false) built_gem_path ||= build_gem - out, _ = sh_with_code("gem install '#{built_gem_path}' --local") + out, _ = sh_with_code("gem install '#{built_gem_path}'#{' --local' if local}") raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/] Bundler.ui.confirm "#{name} (#{version}) installed." end diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index a3bb514954..2af87c94d9 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -6,6 +6,7 @@ module Bundler [Gem::Platform.new('java'), Gem::Platform.new('java')], [Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')], [Gem::Platform.new('mswin64'), Gem::Platform.new('mswin64')], + [Gem::Platform.new('universal-mingw32'), Gem::Platform.new('universal-mingw32')], [Gem::Platform.new('x64-mingw32'), Gem::Platform.new('x64-mingw32')], [Gem::Platform.new('x86_64-mingw32'), Gem::Platform.new('x64-mingw32')], [Gem::Platform.new('mingw32'), Gem::Platform.new('x86-mingw32')] diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 8076c685e5..ea1edaca21 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -81,10 +81,6 @@ module Bundler end end - def source_types - sources.map{|s| s.class }.uniq - end - alias [] search def <<(spec) diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 10526115a4..d0a7dc4fda 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -79,38 +79,50 @@ module Bundler options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely! end + force = options["force"] + # the order that the resolver provides is significant, since # dependencies might actually affect the installation of a gem. # that said, it's a rare situation (other than rake), and parallel # installation is just SO MUCH FASTER. so we let people opt in. jobs = [Bundler.settings[:jobs].to_i-1, 1].max if jobs > 1 && can_install_in_parallel? - install_in_parallel jobs, options[:standalone] + require 'bundler/installer/parallel_installer' + install_in_parallel jobs, options[:standalone], force else - install_sequentially options[:standalone] + install_sequentially options[:standalone], force end lock unless Bundler.settings[:frozen] generate_standalone(options[:standalone]) if options[:standalone] end - def install_gem_from_spec(spec, standalone = false, worker = 0) + def install_gem_from_spec(spec, standalone = false, worker = 0, force = false) # Fetch the build settings, if there are any - settings = Bundler.settings["build.#{spec.name}"] - install_message = nil - post_install_message = nil - debug_message = nil - Bundler.rubygems.with_build_args [settings] do - install_message, post_install_message, debug_message = spec.source.install(spec) - if install_message.include? 'Installing' - Bundler.ui.confirm install_message - else - Bundler.ui.info install_message + settings = Bundler.settings["build.#{spec.name}"] + messages = nil + + install_options = { :force => force, :ensure_builtin_gems_cached => standalone } + + if settings + # Build arguments are global, so this is mutexed + Bundler.rubygems.with_build_args [settings] do + messages = spec.source.install(spec, install_options) end - Bundler.ui.debug debug_message if debug_message - Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}" + else + messages = spec.source.install(spec, install_options) end + install_message, post_install_message, debug_message = *messages + + if install_message.include? 'Installing' + Bundler.ui.confirm install_message + else + Bundler.ui.info install_message + end + Bundler.ui.debug debug_message if debug_message + Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}" + if Bundler.settings[:bin] && standalone generate_standalone_bundler_executable_stubs(spec) elsif Bundler.settings[:bin] @@ -258,68 +270,17 @@ module Bundler end end - def install_sequentially(standalone) + def install_sequentially(standalone, force = false) specs.each do |spec| - message = install_gem_from_spec spec, standalone, 0 + message = install_gem_from_spec spec, standalone, 0, force if message Installer.post_install_messages[spec.name] = message end end end - def install_in_parallel(size, standalone) - name2spec = {} - remains = {} - enqueued = {} - specs.each do |spec| - name2spec[spec.name] = spec - remains[spec.name] = true - end - - worker_pool = Worker.new size, lambda { |name, worker_num| - spec = name2spec[name] - message = install_gem_from_spec spec, standalone, worker_num - { :name => spec.name, :post_install => message } - } - - # 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 - remains.delete message[:name] - if message[:post_install] - Installer.post_install_messages[message[:name]] = message[:post_install] - 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 + def install_in_parallel(size, standalone, force = false) + ParallelInstaller.call(self, specs, size, standalone, force) end def create_bundle_path diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb new file mode 100644 index 0000000000..9c08c83b4c --- /dev/null +++ b/lib/bundler/installer/parallel_installer.rb @@ -0,0 +1,117 @@ +require 'bundler/worker' + + +class ParallelInstaller + + class SpecInstallation + + attr_accessor :spec, :name, :post_install_message, :state + def initialize(spec) + @spec, @name = spec, spec.name + @state = :none + @post_install_message = "" + end + + def installed? + state == :installed + end + + def enqueued? + state == :enqueued + end + + # Only true when spec in neither installed nor already enqueued + def ready_to_enqueue? + !installed? && !enqueued? + end + + def has_post_install_message? + !post_install_message.empty? + end + + def ignorable_dependency?(dep) + dep.type == :development || dep.name == @name + end + + # Checks installed dependencies against spec's dependencies to make + # sure needed dependencies have been installed. + def dependencies_installed?(remaining_specs) + installed_specs = remaining_specs.reject(&:installed?).map(&:name) + already_installed = lambda {|dep| installed_specs.include? dep.name } + dependencies.all? {|d| already_installed[d] } + end + + # Represents only the non-development dependencies and the ones that + # are itself. + def dependencies + @dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep } + end + + # Represents all dependencies + def all_dependencies + @spec.dependencies + end + end + + def self.call(*args) + new(*args).call + end + + # Returns max number of threads machine can handle with a min of 1 + def self.max_threads + [Bundler.settings[:jobs].to_i-1, 1].max + end + + def initialize(installer, all_specs, size, standalone, force) + @installer = installer + @size = size + @standalone = standalone + @force = force + @specs = all_specs.map { |s| SpecInstallation.new(s) } + end + + def call + enqueue_specs + process_specs until @specs.all?(&:installed?) + ensure + worker_pool && worker_pool.stop + end + + def worker_pool + @worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num| + message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num, @force + spec_install.post_install_message = message unless message.nil? + spec_install + } + end + + # Dequeue a spec and save its post-install message and then enqueue the + # remaining specs. + # Some specs might've had to wait til this spec was installed to be + # processed so the call to `enqueue_specs` is important after every + # dequeue. + def process_specs + spec = worker_pool.deq + spec.state = :installed + collect_post_install_message spec if spec.has_post_install_message? + enqueue_specs + end + + def collect_post_install_message(spec) + Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message + end + + # 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. + def enqueue_specs + @specs.select(&:ready_to_enqueue?).each do |spec| + if spec.dependencies_installed? @specs + worker_pool.enq spec + spec.state = :enqueued + end + end + end +end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 16f46b8580..1119bdeed8 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -41,7 +41,7 @@ module Bundler out = " #{name} (#{version}-#{platform})\n" end - dependencies.sort_by {|d| d.to_s }.each do |dep| + dependencies.sort_by {|d| d.to_s }.uniq.each do |dep| next if dep.type == :development out << " #{dep.to_lock}\n" end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 7030508988..21367fe70c 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -12,8 +12,9 @@ require "strscan" module Bundler class LockfileParser - attr_reader :sources, :dependencies, :specs, :platforms + attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version + BUNDLED = "BUNDLED WITH" DEPENDENCIES = "DEPENDENCIES" PLATFORMS = "PLATFORMS" GIT = "GIT" @@ -41,18 +42,38 @@ module Bundler @state = :dependency elsif line == PLATFORMS @state = :platform + elsif line == BUNDLED + @state = :bundled_with else send("parse_#{@state}", line) end end @sources << @rubygems_aggregate @specs = @specs.values + warn_for_outdated_bundler_version rescue ArgumentError => e Bundler.ui.debug(e) raise LockfileError, "Your lockfile is unreadable. Run `rm Gemfile.lock` " \ "and then `bundle install` to generate a new lockfile." end + def warn_for_outdated_bundler_version + return unless bundler_version + prerelease_text = bundler_version.prerelease? ? " --pre" : "" + current_version = Gem::Version.create(Bundler::VERSION) + case current_version.segments.first <=> bundler_version.segments.first + when -1 + raise LockfileError, "You must use Bundler #{bundler_version.segments.first} or greater with this lockfile." + when 0 + if current_version < bundler_version + Bundler.ui.warn "Warning: the running version of Bundler is older " \ + "than the version that created the lockfile. We suggest you " \ + "upgrade to the latest version of Bundler by running `gem " \ + "install bundler#{prerelease_text}`.\n" + end + end + end + private TYPES = { @@ -157,5 +178,12 @@ module Bundler end end + def parse_bundled_with(line) + line = line.strip + if Gem::Version.correct?(line) + @bundler_version = Gem::Version.create(line) + end + end + end end diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb index 523dafbbd6..e6a33ecbad 100644 --- a/lib/bundler/match_platform.rb +++ b/lib/bundler/match_platform.rb @@ -7,7 +7,7 @@ module Bundler def match_platform(p) Gem::Platform::RUBY == platform or platform.nil? or p == platform or - generic(Gem::Platform.new(platform)) == p + generic(Gem::Platform.new(platform)) === p end end end diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb index c80eaa40fc..456686dc0f 100644 --- a/lib/bundler/remote_specification.rb +++ b/lib/bundler/remote_specification.rb @@ -54,4 +54,24 @@ module Bundler end end end + + class StubSpecification < RemoteSpecification + def self.from_stub(stub) + spec = new(stub.name, stub.version, stub.platform, nil) + spec.stub = stub + spec + end + + attr_accessor :stub + + def to_yaml + _remote_specification.to_yaml + end + + private + + def _remote_specification + stub.to_spec + end + end end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index df2c515cc5..511a74139a 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -323,12 +323,8 @@ module Bundler message << "Source does not contain any versions of '#{requirement}'" end else - message = "Could not find gem '#{requirement}' " - if @index.source_types.include?(Bundler::Source::Rubygems) - message << "in any of the gem sources listed in your Gemfile." - else - message << "in the gems available on this machine." - end + message = "Could not find gem '#{requirement}' in any of the gem sources " \ + "listed in your Gemfile or available on this machine." end raise GemNotFound, message end diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb index ea4d7577d9..585888f8e1 100644 --- a/lib/bundler/retry.rb +++ b/lib/bundler/retry.rb @@ -1,23 +1,24 @@ module Bundler # General purpose class for retrying code that may fail class Retry - DEFAULT_ATTEMPTS = 2 attr_accessor :name, :total_runs, :current_run class << self - attr_accessor :attempts + def default_attempts + default_retries + 1 + end + alias_method :attempts, :default_attempts + + def default_retries + Bundler.settings[:retry] + end end - def initialize(name, exceptions = nil, attempts = nil) + def initialize(name, exceptions = nil, retries = self.class.default_retries) @name = name - attempts ||= default_attempts + @retries = retries @exceptions = Array(exceptions) || [] - @total_runs = attempts.next # will run once, then upto attempts.times - end - - def default_attempts - return Integer(self.class.attempts) if self.class.attempts - DEFAULT_ATTEMPTS + @total_runs = @retries + 1 # will run once, then upto attempts.times end def attempt(&block) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 1cb9e72808..047aa891a6 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -131,6 +131,19 @@ module Bundler yield end + def loaded_gem_paths + # RubyGems 2.2+ can put binary extension into dedicated folders, + # therefore use RubyGems facilities to obtain their load paths. + if Gem::Specification.method_defined? :full_require_paths + loaded_gem_paths = Gem.loaded_specs.map {|n, s| s.full_require_paths} + loaded_gem_paths.flatten + else + $LOAD_PATH.select do |p| + Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ } + end + end + end + def ui=(obj) Gem::DefaultUserInteraction.ui = obj end @@ -161,12 +174,14 @@ module Bundler end def with_build_args(args) - old_args = self.build_args - begin - self.build_args = args - yield - ensure - self.build_args = old_args + ext_lock.synchronize do + old_args = self.build_args + begin + self.build_args = args + yield + ensure + self.build_args = old_args + end end end @@ -559,12 +574,20 @@ module Bundler end end + # RubyGems 2.1.0 class MoreFuture < Future def initialize super backport_ext_builder_monitor end + def all_specs + require 'bundler/remote_specification' + Gem::Specification.stubs.map do |stub| + StubSpecification.from_stub(stub) + end + end + def backport_ext_builder_monitor require 'rubygems/ext' @@ -583,6 +606,18 @@ module Bundler def ext_lock Gem::Ext::Builder::CHDIR_MONITOR end + + if Gem::Specification.respond_to?(:stubs_for) + def find_name(name) + Gem::Specification.stubs_for(name).map(&:to_spec) + end + else + def find_name(name) + Gem::Specification.stubs.find_all do |spec| + spec.name == name + end.map(&:to_spec) + end + end end end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index f83cb30742..be61c2b15b 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -88,9 +88,6 @@ module Bundler raise if $1 != namespaced_file end end - rescue => e - Bundler.ui.debug e - Bundler.ui.warn "Unable to require #{required_file}. #{e.class}: #{e.message}." end end end diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index bed2c8e4d7..7a58c8af54 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -2,7 +2,9 @@ require 'uri' module Bundler class Settings - BOOL_KEYS = %w(frozen cache_all no_prune disable_local_branch_check gem.mit gem.coc).freeze + BOOL_KEYS = %w(frozen cache_all no_prune disable_local_branch_check ignore_messages gem.mit gem.coc).freeze + NUMBER_KEYS = %w(retry timeout redirect).freeze + DEFAULT_CONFIG = {:retry => 3, :timeout => 10, :redirect => 5} def initialize(root = nil) @root = root @@ -12,10 +14,13 @@ module Bundler def [](name) key = key_for(name) - value = (@local_config[key] || ENV[key] || @global_config[key]) + value = (@local_config[key] || ENV[key] || @global_config[key] || DEFAULT_CONFIG[name]) - if !value.nil? && is_bool(name) + case + when !value.nil? && is_bool(name) to_bool(value) + when !value.nil? && is_num(name) + value.to_i else value end @@ -84,6 +89,7 @@ module Bundler locations[:local] = @local_config[key] if @local_config.key?(key) locations[:env] = ENV[key] if ENV[key] locations[:global] = @global_config[key] if @global_config.key?(key) + locations[:default] = DEFAULT_CONFIG[key] if DEFAULT_CONFIG.key?(key) locations end @@ -108,11 +114,19 @@ module Bundler end def without=(array) - self[:without] = (array.empty? ? nil : array.join(":")) if array + set_array(:without, array) + end + + def with=(array) + set_array(:with, array) end def without - self[:without] ? self[:without].split(":").map { |w| w.to_sym } : [] + get_array(:without) + end + + def with + get_array(:with) end # @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"] @@ -153,14 +167,38 @@ module Bundler "BUNDLE_#{key}" end - def is_bool(key) - BOOL_KEYS.include?(key.to_s) + def parent_setting_for(name) + split_specfic_setting_for(name)[0] + end + + def specfic_gem_for(name) + split_specfic_setting_for(name)[1] + end + + def split_specfic_setting_for(name) + name.split(".") + end + + def is_bool(name) + BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s)) end def to_bool(value) !(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i || value == false) end + def is_num(value) + NUMBER_KEYS.include?(value.to_s) + end + + def get_array(key) + self[key] ? self[key].split(":").map { |w| w.to_sym } : [] + end + + def set_array(key, array) + self[key] = (array.empty? ? nil : array.join(":")) if array + end + def set_key(key, value, hash, file) key = key_for(key) diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 0087f0786a..f950041acc 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -21,6 +21,7 @@ module Bundler def default_gemfile gemfile = find_gemfile + deprecate_gemfile(gemfile) raise GemfileNotFound, "Could not locate Gemfile" unless gemfile Pathname.new(gemfile) end @@ -35,32 +36,27 @@ module Bundler end def default_bundle_dir - global_bundle_dir = File.join(Bundler.rubygems.user_home, ".bundle") bundle_dir = find_directory(".bundle") + return nil unless bundle_dir - if bundle_dir && bundle_dir != global_bundle_dir - Pathname.new(bundle_dir) - else - nil - end + global_bundle_dir = File.join(Bundler.rubygems.user_home, ".bundle") + return nil if bundle_dir == global_bundle_dir + + Pathname.new(bundle_dir) end def in_bundle? find_gemfile end - def chdir_monitor - Bundler.rubygems.ext_lock - end - def chdir(dir, &blk) - chdir_monitor.synchronize do + Bundler.rubygems.ext_lock.synchronize do Dir.chdir dir, &blk end end def pwd - chdir_monitor.synchronize do + Bundler.rubygems.ext_lock.synchronize do Dir.pwd end end @@ -102,7 +98,6 @@ module Bundler def find_gemfile given = ENV['BUNDLE_GEMFILE'] return given if given && !given.empty? - find_file('Gemfile', 'gems.rb') end @@ -140,15 +135,28 @@ module Bundler # handle 1.9 where system gems are always on the load path if defined?(::Gem) me = File.expand_path("../../", __FILE__) + me = /^#{Regexp.escape(me)}/ + + loaded_gem_paths = Bundler.rubygems.loaded_gem_paths + $LOAD_PATH.reject! do |p| - next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/ - p != File.dirname(__FILE__) && - Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ } + next if File.expand_path(p) =~ me + loaded_gem_paths.delete(p) end $LOAD_PATH.uniq! end end + def deprecate_gemfile(gemfile) + if gemfile && File.basename(gemfile) == "Gemfile" + Bundler.respond_to?(:ui) && Bundler.ui.deprecate( + "Gemfile and Gemfile.lock are " \ + "deprecated and will be replaced with gems.rb and " \ + "gems.locked in Bundler 2.0.\n" + ) + end + end + extend self end end diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index 213e98fb98..869931a1f0 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -27,5 +27,10 @@ module Bundler def can_lock?(spec) spec.source == self end + + def include?(other) + other == self + end + end end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index 62d615088c..230aa8fc40 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -159,9 +159,9 @@ module Bundler local_specs end - def install(spec) + def install(spec, force = false) debug = nil - if requires_checkout? && !@copied + if requires_checkout? && !@copied && !force debug = " * Checking out revision: #{ref}" git_proxy.copy_to(install_path, submodules) serialize_gemspecs_in(install_path) diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index 2b53bc7831..862fbcb97a 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -69,7 +69,7 @@ module Bundler File.basename(expanded_path.to_s) end - def install(spec) + def install(spec, force = false) generate_bin(spec, :disable_extensions) ["Using #{version_message(spec)} from #{to_s}", nil] end diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb index 28e69875aa..ef9faa9cbe 100644 --- a/lib/bundler/source/path/installer.rb +++ b/lib/bundler/source/path/installer.rb @@ -3,33 +3,38 @@ module Bundler class Path class Installer < Bundler::GemInstaller + attr_reader :spec + def initialize(spec, options = {}) @spec = spec - @tmp_bin_dir = "#{Bundler.tmp(spec.full_name)}/bin" - @gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin" - @bin_dir = Bundler.requires_sudo? ? @tmp_bin_dir : @gem_bin_dir @gem_dir = Bundler.rubygems.path(spec.full_gem_path) - @wrappers = options[:wrappers] || true - @env_shebang = options[:env_shebang] || true + @wrappers = true + @env_shebang = true @format_executable = options[:format_executable] || false @build_args = options[:build_args] || Bundler.rubygems.build_args + @gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin" + + if Bundler.requires_sudo? + @tmp_dir = Bundler.tmp(spec.full_name).to_s + @bin_dir = "#{@tmp_dir}/bin" + else + @bin_dir = @gem_bin_dir + end end def generate_bin return if spec.executables.nil? || spec.executables.empty? - if Bundler.requires_sudo? - FileUtils.mkdir_p(@tmp_bin_dir) unless File.exist?(@tmp_bin_dir) - end - super if Bundler.requires_sudo? Bundler.mkdir_p @gem_bin_dir spec.executables.each do |exe| - Bundler.sudo "cp -R #{@tmp_bin_dir}/#{exe} #{@gem_bin_dir}" + Bundler.sudo "cp -R #{@bin_dir}/#{exe} #{@gem_bin_dir}" end end + ensure + Bundler.rm_rf(@tmp_dir) if Bundler.requires_sudo? end end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 8f504bcf7f..797988f365 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -38,11 +38,15 @@ module Bundler end def eql?(o) - o.is_a?(Rubygems) && remotes_equal?(o.remotes) + o.is_a?(Rubygems) && o.credless_remotes == credless_remotes end alias == eql? + def include?(o) + o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty? + end + def can_lock?(spec) spec.source.is_a?(Rubygems) end @@ -81,8 +85,21 @@ module Bundler end end - def install(spec) - return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any? + def install(spec, opts = {}) + force = opts[:force] + ensure_builtin_gems_cached = opts[:ensure_builtin_gems_cached] + + if ensure_builtin_gems_cached && builtin_gem?(spec) + if !cached_path(spec) + cached_built_in_gem(spec) unless spec.remote + force = true + else + spec.loaded_from = loaded_from(spec) + end + end + + return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any? && !force + # Download the gem to get the spec, because some specs that are returned # by rubygems.org are broken and wrong. @@ -143,9 +160,7 @@ module Bundler spec.loaded_from = loaded_from(spec) ["Installing #{version_message(spec)}", spec.post_install_message] ensure - if install_path && Bundler.requires_sudo? - FileUtils.remove_entry_secure(install_path) - end + Bundler.rm_rf(install_path) if Bundler.requires_sudo? end def cache(spec, custom_path = nil) @@ -203,6 +218,10 @@ module Bundler protected + def credless_remotes + remotes.map(&method(:suppress_configured_credentials)) + end + def remotes_for_spec(spec) specs.search_all(spec.name).inject([]) do |uris, s| uris << s.remote if s.remote @@ -210,8 +229,6 @@ module Bundler end end - private - def loaded_from(spec) "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec" end @@ -293,7 +310,7 @@ module Bundler end def api_fetchers - fetchers.select{|f| f.use_api } + fetchers.select(&:use_api) end def remote_specs @@ -334,7 +351,7 @@ module Bundler end end until idxcount == idx.size - if api_fetchers.any? && api_fetchers.all?{|f| f.use_api } + if api_fetchers.any? # it's possible that gems from one source depend on gems from some # other source, so now we download gemspecs and iterate over those # dependencies, looking for gems we don't have info on yet. @@ -351,7 +368,7 @@ module Bundler end end - if !allow_api + unless allow_api api_fetchers.each do |f| Bundler.ui.info "Fetching source index from #{f.uri}" idx.use f.specs(nil, self) @@ -362,7 +379,23 @@ module Bundler def fetch_gem(spec) return false unless spec.remote - Fetcher.download_gem_from_uri(spec, spec.remote.uri) + uri = spec.remote.uri + spec.fetch_platform + + download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir + gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem" + + FileUtils.mkdir_p("#{download_path}/cache") + Bundler.rubygems.download_gem(spec, uri, download_path) + + if Bundler.requires_sudo? + Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache" + Bundler.sudo "mv #{download_path}/cache/#{spec.full_name}.gem #{gem_path}" + end + + gem_path + ensure + Bundler.rm_rf(download_path) if Bundler.requires_sudo? end def builtin_gem?(spec) @@ -373,10 +406,6 @@ module Bundler spec.loaded_from && spec.loaded_from.include?("specifications/default/") end - def remotes_equal?(other_remotes) - remotes.map(&method(:suppress_configured_credentials)) == other_remotes.map(&method(:suppress_configured_credentials)) - end - end end end diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index 49efbf7a8e..546b3b14be 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -15,7 +15,9 @@ module Bundler end def add_git_source(options = {}) - add_source_to_list Source::Git.new(options), git_sources + source = add_source_to_list(Source::Git.new(options), git_sources) + warn_on_git_protocol(source) + source end def add_rubygems_source(options = {}) @@ -97,5 +99,17 @@ module Bundler def combine_rubygems_sources Source::Rubygems.new("remotes" => rubygems_remotes) end + + def warn_on_git_protocol(source) + return if Bundler.settings["git.allow_insecure"] + + if source.uri =~ %r{^git\:} + Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \ + "which transmits data without encryption. Disable this warning with " \ + "`bundle config git.allow_insecure true`, or switch to the `https` " \ + "protocol to keep your data secure." + end + end + end end diff --git a/lib/bundler/templates/newgem/.travis.yml.tt b/lib/bundler/templates/newgem/.travis.yml.tt index 4c7eba6364..d78885d0ee 100644 --- a/lib/bundler/templates/newgem/.travis.yml.tt +++ b/lib/bundler/templates/newgem/.travis.yml.tt @@ -1,3 +1,4 @@ language: ruby rvm: - <%= RUBY_VERSION %> +before_install: gem install bundler -v <%= Bundler::VERSION %> diff --git a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt index c5393d1403..ce9bee75b0 100644 --- a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +++ b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt @@ -2,7 +2,7 @@ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. @@ -10,4 +10,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. -This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt index e0e87e59a7..3d3fb674a0 100644 --- a/lib/bundler/templates/newgem/Rakefile.tt +++ b/lib/bundler/templates/newgem/Rakefile.tt @@ -4,6 +4,8 @@ require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test" + t.libs << "lib" + t.test_files = FileList['test/**/*_test.rb'] end task :default => :test diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt index e647c17f69..16f12f279b 100644 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ b/lib/bundler/templates/newgem/newgem.gemspec.tt @@ -13,6 +13,14 @@ Gem::Specification.new do |spec| spec.license = "MIT" <%- end -%> + # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or + # delete this section to allow pushing this gem to any host. + if spec.respond_to?(:metadata) + spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" + else + raise "RubyGems 2.0 or newer is required to protect against public gem pushes." + end + spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } @@ -21,10 +29,6 @@ Gem::Specification.new do |spec| spec.extensions = ["ext/<%=config[:underscored_name]%>/extconf.rb"] <%- end -%> - if spec.respond_to?(:metadata) - spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server." - end - spec.add_development_dependency "bundler", "~> <%= config[:bundler_version] %>" spec.add_development_dependency "rake", "~> 10.0" <%- if config[:ext] -%> diff --git a/lib/bundler/templates/newgem/test/test_newgem.rb.tt b/lib/bundler/templates/newgem/test/newgem_test.rb.tt index d50f7da243..95e33a34ea 100644 --- a/lib/bundler/templates/newgem/test/test_newgem.rb.tt +++ b/lib/bundler/templates/newgem/test/newgem_test.rb.tt @@ -1,6 +1,6 @@ -require 'minitest_helper' +require 'test_helper' -class Test<%= config[:constant_name] %> < Minitest::Test +class <%= config[:constant_name] %>Test < Minitest::Test def test_that_it_has_a_version_number refute_nil ::<%= config[:constant_name] %>::VERSION end diff --git a/lib/bundler/templates/newgem/test/minitest_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt index 49a56c1800..49a56c1800 100644 --- a/lib/bundler/templates/newgem/test/minitest_helper.rb.tt +++ b/lib/bundler/templates/newgem/test/test_helper.rb.tt diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb index 8eb723181b..777aed0d86 100644 --- a/lib/bundler/ui/shell.rb +++ b/lib/bundler/ui/shell.rb @@ -3,7 +3,7 @@ module Bundler class Shell LEVELS = %w(silent error warn confirm info debug) - attr_writer :shell + attr_writer :shell, :deprecation_messages def initialize(options = {}) if options["no-color"] || !STDOUT.tty? @@ -11,6 +11,7 @@ module Bundler end @shell = Thor::Base.shell.new @level = ENV['DEBUG'] ? "debug" : "info" + @deprecation_messages = Set.new end def info(msg, newline = nil) @@ -25,6 +26,13 @@ module Bundler tell_stdout(msg, :yellow, newline) if level("warn") end + def deprecate(msg, newline = nil) + unless @deprecation_messages.include?(msg) + @deprecation_messages.add(msg) + tell_stderr("DEPRECATION: " + msg, :yellow, newline) + end + end + def error(msg, newline = nil) tell_stderr(msg, :red, newline) if level("error") end @@ -46,6 +54,14 @@ module Bundler @shell.ask(msg) end + def yes?(msg) + @shell.yes?(msg) + end + + def no? + @shell.no?(msg) + end + def level=(level) raise ArgumentError unless LEVELS.include?(level.to_s) @level = level diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb index 3eb3199b5c..b2420af7fb 100644 --- a/lib/bundler/ui/silent.rb +++ b/lib/bundler/ui/silent.rb @@ -10,6 +10,9 @@ module Bundler def warn(message, newline = nil) end + def deprecate(message, newline = nil) + end + def error(message, newline = nil) end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb b/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb deleted file mode 100644 index bf740e4848..0000000000 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'molinillo/gem_metadata' -require 'molinillo/errors' -require 'molinillo/resolver' -require 'molinillo/modules/ui' -require 'molinillo/modules/specification_provider' diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb new file mode 100644 index 0000000000..50af4a912c --- /dev/null +++ b/lib/bundler/vendor/molinillo/lib/molinillo.rb @@ -0,0 +1,5 @@ +require 'bundler/vendor/molinillo/lib/molinillo/gem_metadata' +require 'bundler/vendor/molinillo/lib/molinillo/errors' +require 'bundler/vendor/molinillo/lib/molinillo/resolver' +require 'bundler/vendor/molinillo/lib/molinillo/modules/ui' +require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb index 4ee5708a56..7cde9fa5eb 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb @@ -61,7 +61,7 @@ module Bundler::Molinillo hash[name] = vertex.dup.tap { |v| v.graph = self } end end - @root_vertices = Hash[vertices.select { |n, _v| other.root_vertices[n] }] + @root_vertices = Hash[@vertices.select { |n, _v| other.root_vertices[n] }] @edges = other.edges.map do |edge| Edge.new( vertex_named(edge.origin.name), diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb index b828d0c20d..b828d0c20d 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb index 0736b89c3c..2cf1b60b12 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb @@ -1,3 +1,3 @@ module Bundler::Molinillo - VERSION = '0.2.1' + VERSION = '0.2.3' end diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb index 79a85e778f..79a85e778f 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb index 097c0264ac..097c0264ac 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb index 376e4d4ed2..46a47d8028 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb @@ -73,6 +73,19 @@ module Bundler::Molinillo end_resolution end + # @return [Integer] the number of resolver iterations in between calls to + # {#resolver_ui}'s {UI#indicate_progress} method + attr_accessor :iteration_rate + private :iteration_rate + + # @return [Time] the time at which resolution began + attr_accessor :started_at + private :started_at + + # @return [Array<ResolutionState>] the stack of states for the resolution + attr_accessor :states + private :states + private # Sets up the resolution process @@ -98,18 +111,8 @@ module Bundler::Molinillo debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state end - require 'molinillo/state' - require 'molinillo/modules/specification_provider' - - # @return [Integer] the number of resolver iterations in between calls to - # {#resolver_ui}'s {UI#indicate_progress} method - attr_accessor :iteration_rate - - # @return [Time] the time at which resolution began - attr_accessor :started_at - - # @return [Array<ResolutionState>] the stack of states for the resolution - attr_accessor :states + require 'bundler/vendor/molinillo/lib/molinillo/state' + require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' ResolutionState.new.members.each do |member| define_method member do |*args, &block| diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb index 7cfe914d34..9a9d8fd1f8 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb @@ -1,4 +1,4 @@ -require 'molinillo/dependency_graph' +require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph' module Bundler::Molinillo # This class encapsulates a dependency resolver. @@ -7,7 +7,7 @@ module Bundler::Molinillo # # class Resolver - require 'molinillo/resolution' + require 'bundler/vendor/molinillo/lib/molinillo/resolution' # @return [SpecificationProvider] the specification provider used # in the resolution process diff --git a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb index 8e394f8672..8e394f8672 100644 --- a/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb b/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb deleted file mode 100644 index 74c789b763..0000000000 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "thor/parser/argument" -require "thor/parser/arguments" -require "thor/parser/option" -require "thor/parser/options" diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb index 8775d6a3e0..9ed67a44e2 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor.rb +++ b/lib/bundler/vendor/thor/lib/thor.rb @@ -1,5 +1,5 @@ require "set" -require "thor/base" +require "bundler/vendor/thor/lib/thor/base" class Bundler::Thor # rubocop:disable ClassLength class << self diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb index 7edc70f472..5a82dfd45f 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions.rb @@ -1,12 +1,12 @@ require "fileutils" require "uri" -require "thor/core_ext/io_binary_read" -require "thor/actions/create_file" -require "thor/actions/create_link" -require "thor/actions/directory" -require "thor/actions/empty_directory" -require "thor/actions/file_manipulation" -require "thor/actions/inject_into_file" +require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" +require "bundler/vendor/thor/lib/thor/actions/create_file" +require "bundler/vendor/thor/lib/thor/actions/create_link" +require "bundler/vendor/thor/lib/thor/actions/directory" +require "bundler/vendor/thor/lib/thor/actions/empty_directory" +require "bundler/vendor/thor/lib/thor/actions/file_manipulation" +require "bundler/vendor/thor/lib/thor/actions/inject_into_file" class Bundler::Thor module Actions diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb index 711ccb7d7b..a0f5640333 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_file.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require "bundler/vendor/thor/lib/thor/actions/empty_directory" class Bundler::Thor module Actions diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb index f633f25c18..be437922b6 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/create_link.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb @@ -1,4 +1,4 @@ -require "thor/actions/create_file" +require "bundler/vendor/thor/lib/thor/actions/create_file" class Bundler::Thor module Actions diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb index 3ed0649c27..1a2e25da2f 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/directory.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require "bundler/vendor/thor/lib/thor/actions/empty_directory" class Bundler::Thor module Actions diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb index cdc3768b4c..cdc3768b4c 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/empty_directory.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb index 2bdc78f578..2bdc78f578 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb index 45a70701b1..91ab245ae1 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/actions/inject_into_file.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require "bundler/vendor/thor/lib/thor/actions/empty_directory" class Bundler::Thor module Actions diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb index 56b78ebad6..c3667521a5 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/base.rb +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -1,17 +1,17 @@ -require "thor/command" -require "thor/core_ext/hash_with_indifferent_access" -require "thor/core_ext/ordered_hash" -require "thor/error" -require "thor/invocation" -require "thor/parser" -require "thor/shell" -require "thor/line_editor" -require "thor/util" +require "bundler/vendor/thor/lib/thor/command" +require "bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access" +require "bundler/vendor/thor/lib/thor/core_ext/ordered_hash" +require "bundler/vendor/thor/lib/thor/error" +require "bundler/vendor/thor/lib/thor/invocation" +require "bundler/vendor/thor/lib/thor/parser" +require "bundler/vendor/thor/lib/thor/shell" +require "bundler/vendor/thor/lib/thor/line_editor" +require "bundler/vendor/thor/lib/thor/util" class Bundler::Thor - autoload :Actions, "thor/actions" - autoload :RakeCompat, "thor/rake_compat" - autoload :Group, "thor/group" + autoload :Actions, "bundler/vendor/thor/lib/thor/actions" + autoload :RakeCompat, "bundler/vendor/thor/lib/thor/rake_compat" + autoload :Group, "bundler/vendor/thor/lib/thor/group" # Shortcuts for help. HELP_MAPPINGS = %w[-h -? --help -D] diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb index 72c8348cb6..72c8348cb6 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/command.rb +++ b/lib/bundler/vendor/thor/lib/thor/command.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb index 6cf61db812..6cf61db812 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/hash_with_indifferent_access.rb +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb index 19f3c3d43e..19f3c3d43e 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/io_binary_read.rb +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb index 7e80672a07..7e80672a07 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb +++ b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb index fc34c11268..fc34c11268 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/error.rb +++ b/lib/bundler/vendor/thor/lib/thor/error.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb index 71e7f1c3b8..13d168ad62 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/group.rb +++ b/lib/bundler/vendor/thor/lib/thor/group.rb @@ -1,4 +1,4 @@ -require "thor/base" +require "bundler/vendor/thor/lib/thor/base" # Bundler::Thor has a special class called Bundler::Thor::Group. The main difference to Bundler::Thor class # is that it invokes all commands at once. It also include some methods that allows diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb index 684df2c616..684df2c616 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/invocation.rb +++ b/lib/bundler/vendor/thor/lib/thor/invocation.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb b/lib/bundler/vendor/thor/lib/thor/line_editor.rb index 95c848e0e3..ce81a17484 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb +++ b/lib/bundler/vendor/thor/lib/thor/line_editor.rb @@ -1,5 +1,5 @@ -require "thor/line_editor/basic" -require "thor/line_editor/readline" +require "bundler/vendor/thor/lib/thor/line_editor/basic" +require "bundler/vendor/thor/lib/thor/line_editor/readline" class Bundler::Thor module LineEditor diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb index b121e95575..b121e95575 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb index dd39cff35d..dd39cff35d 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor/readline.rb +++ b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb diff --git a/lib/bundler/vendor/thor/lib/thor/parser.rb b/lib/bundler/vendor/thor/lib/thor/parser.rb new file mode 100644 index 0000000000..08f80e565d --- /dev/null +++ b/lib/bundler/vendor/thor/lib/thor/parser.rb @@ -0,0 +1,4 @@ +require "bundler/vendor/thor/lib/thor/parser/argument" +require "bundler/vendor/thor/lib/thor/parser/arguments" +require "bundler/vendor/thor/lib/thor/parser/option" +require "bundler/vendor/thor/lib/thor/parser/options" diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb index 84957903cd..84957903cd 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/argument.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb index c7bb648e31..c7bb648e31 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/arguments.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb index eb893617f4..eb893617f4 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/option.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/option.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb index deac6a0c16..deac6a0c16 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb index fcf6719df6..60282e2991 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/rake_compat.rb +++ b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb @@ -5,7 +5,7 @@ class Bundler::Thor # Adds a compatibility layer to your Bundler::Thor classes which allows you to use # rake package tasks. For example, to use rspec rake tasks, one can do: # - # require 'thor/rake_compat' + # require 'bundler/vendor/thor/lib/thor/rake_compat' # require 'rspec/core/rake_task' # # class Default < Bundler::Thor diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb index 5552fe44bd..f0d7bfe2e0 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/runner.rb +++ b/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -1,6 +1,6 @@ -require "thor" -require "thor/group" -require "thor/core_ext/io_binary_read" +require "bundler/vendor/thor/lib/thor" +require "bundler/vendor/thor/lib/thor/group" +require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" require "fileutils" require "open-uri" @@ -102,7 +102,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng desc "version", "Show Bundler::Thor version" def version - require "thor/version" + require "bundler/vendor/thor/lib/thor/version" say "Bundler::Thor #{Bundler::Thor::VERSION}" end diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb index 6a6ec5e0a4..91afdce2aa 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell.rb @@ -24,9 +24,9 @@ class Bundler::Thor SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] attr_writer :shell - autoload :Basic, "thor/shell/basic" - autoload :Color, "thor/shell/color" - autoload :HTML, "thor/shell/html" + autoload :Basic, "bundler/vendor/thor/lib/thor/shell/basic" + autoload :Color, "bundler/vendor/thor/lib/thor/shell/color" + autoload :HTML, "bundler/vendor/thor/lib/thor/shell/html" # Add shell to initialize config values. # diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index 278ffa3df0..278ffa3df0 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb index 3c2feba4e5..1e2d26cfc5 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/color.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb @@ -1,4 +1,4 @@ -require "thor/shell/basic" +require "bundler/vendor/thor/lib/thor/shell/basic" class Bundler::Thor module Shell diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb index 9e28690ad0..e1ea0de599 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/shell/html.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/html.rb @@ -1,4 +1,4 @@ -require "thor/shell/basic" +require "bundler/vendor/thor/lib/thor/shell/basic" class Bundler::Thor module Shell diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb index f4e98fc19f..f4e98fc19f 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/util.rb +++ b/lib/bundler/vendor/thor/lib/thor/util.rb diff --git a/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index 74b020a5ab..74b020a5ab 100644 --- a/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb index ce9ef0a3a1..4081f3fa92 100644 --- a/lib/bundler/vendored_molinillo.rb +++ b/lib/bundler/vendored_molinillo.rb @@ -1,5 +1,2 @@ -vendor = File.expand_path('../vendor/Molinillo-0.2.1/lib', __FILE__) -loaded = $:.include?(vendor) -$:.unshift(vendor) unless loaded -require 'molinillo' -$:.delete(vendor) unless loaded +module Bundler; end +require 'bundler/vendor/molinillo/lib/molinillo' diff --git a/lib/bundler/vendored_thor.rb b/lib/bundler/vendored_thor.rb index 2426f0c406..1931b5f278 100644 --- a/lib/bundler/vendored_thor.rb +++ b/lib/bundler/vendored_thor.rb @@ -1,5 +1,3 @@ -vendor = File.expand_path('../vendor/thor-0.19.1/lib', __FILE__) -loaded = $:.include?(vendor) -$:.unshift(vendor) unless loaded -require 'thor' -require 'thor/actions' +module Bundler; end +require 'bundler/vendor/thor/lib/thor' +require 'bundler/vendor/thor/lib/thor/actions' diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 6d53a18d95..c27bfd8933 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.9.0.rc" unless defined?(::Bundler::VERSION) + VERSION = "1.99.0.pre" unless defined?(::Bundler::VERSION) end diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn index 8bbc5140e1..b57dc45bef 100644 --- a/man/bundle-config.ronn +++ b/man/bundle-config.ronn @@ -35,6 +35,38 @@ local and global sources. Not compatible with --global or --local flag. Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will cause it to ignore all configuration. +## REMEMBERING OPTIONS + +Flags passed to `bundle install` or the Bundler runtime, such as `--path foo` or `--without production`, are not remembered between commands. If these options must be remembered, they must be set using `bundle config` (e.g., `bundle config path foo`). + +The options that can be remembered for future commands are: + +* `binstubs`: + Creates a directory (defaults to `~/bin`) and place any executables from the + gem there. These executables run in Bundler's context. If used, you might add + this directory to your environment's `PATH` variable. For instance, if the + `rails` gem comes with a `rails` executable, this flag will create a + `bin/rails` executable that ensures that all referred dependencies will be + resolved using the bundled gems. + +* `deployment`: + In deployment mode, Bundler will 'roll-out' the bundle for + `production` use. Please check carefully if you want to have this option + enabled in `development` or `test` environments. + +* `path`: + The location to install the specified gems to. This defaults to Rubygems' + setting. Bundler shares this location with Rubygems, `gem install ...` will + have gem installed there, too. Therefore, gems installed without a + `--path ...` setting will show up by calling `gem list`. Accodingly, gems + installed to other locations will not get listed. + +* `without`: + A space-separated list of groups referencing gems to skip during installation. + +* `with`: + A space-separated list of optional groups to be installed. + ## BUILD OPTIONS You can use `bundle config` to give bundler the flags to pass to the gem @@ -102,9 +134,18 @@ learn more about their operation in [bundle install(1)][bundle-install]. * `ssl_client_cert` (`BUNDLE_SSL_CLIENT_CERT`): Path to a designated file containing a X.509 client certificate and key in PEM format. +* `cache_path` (`BUNDLE_CACHE_PATH`): The directory that bundler will place + cached gems in when running <code>bundle package</code>, and that bundler + will look in when installing gems. +* `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`): When set, Gemfiles + containing multiple sources will produce errors instead of warnings. Use + `bundle config --delete disable_multisource` to unset. +* `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`): When set, no post install + messages will be printed. To silence a single gem, use dot notation like + `ignore_messages.httparty true`. In general, you should set these settings per-application by using the applicable -flag to the [bundle install(1)][bundle-install] command. +flag to the [bundle install(1)][bundle-install] or [bundle package(1)][bundle-package] command. You can set them globally either via environment variables or `bundle config`, whichever is preferable for your setup. If you use both, environment variables diff --git a/man/bundle-install.ronn b/man/bundle-install.ronn index 0af3fb6708..9db1832fd7 100644 --- a/man/bundle-install.ronn +++ b/man/bundle-install.ronn @@ -8,16 +8,19 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile [--full-index] [--gemfile=GEMFILE] [--jobs=NUMBER] - [--local] [--deployment] + [--local] + [--deployment] [--cache] [--no-prune] - [--path PATH] [--system] + [--path PATH] + [--system] [--quiet] [--retry=NUMBER] [--shebang] [--standalone[=GROUP[ GROUP...]]] [--trust-policy=POLICY] [--without=GROUP[ GROUP...]] + [--with=GROUP[ GROUP...]] ## DESCRIPTION @@ -38,6 +41,9 @@ update process below under [CONSERVATIVE UPDATING][]. ## OPTIONS +To apply any of `--deployment`, `--path`, `--binstubs`, or `--without` every +time `bundle install` is run, use `bundle config` (see bundle-config(1)). + * `--binstubs[=<directory>]`: Creates a directory (defaults to `~/bin`) and place any executables from the gem there. These executables run in Bundler's context. If used, you might add @@ -79,7 +85,7 @@ update process below under [CONSERVATIVE UPDATING][]. * `--system`: Installs the gems specified in the bundle to the system's Rubygems location. - This overrides any previous [remembered][REMEMBERED OPTIONS] use of `--path`. + This overrides any previous configuration of `--path`. * `--cache`: Update the cache in `vendor/cache` with the newly bundled gems during the @@ -93,8 +99,7 @@ update process below under [CONSERVATIVE UPDATING][]. setting. Bundler shares this location with Rubygems, `gem install ...` will have gem installed there, too. Therefore, gems installed without a `--path ...` setting will show up by calling `gem list`. Accodingly, gems - installed to other locations will not get listed. This setting is a - [remembered option][REMEMBERED OPTIONS]. + installed to other locations will not get listed. * `--quiet`: Do not print progress information to the standard output. Instead, Bundler @@ -124,8 +129,10 @@ update process below under [CONSERVATIVE UPDATING][]. * `--without=<list>`: A space-separated list of groups referencing gems to skip during installation. - This is a [remembered option][REMEMBERED OPTIONS]. +* `--with=<list>`: + A space-separated list of groups referencing gems to install. If an + optional group is given it is installed. ## DEPLOYMENT MODE @@ -250,41 +257,6 @@ the vagaries of the dependency resolution process, this usually affects more than just the gems you list in your Gemfile(5), and can (surprisingly) radically change the gems you are using. -## REMEMBERED OPTIONS - -Some options (marked above in the [OPTIONS][] section) are remembered -between calls to `bundle install`, and by the Bundler runtime. - -For instance, if you run `bundle install --without test`, a subsequent -call to `bundle install` that does not include a `--without` flag will -remember your previous choice. - -In addition, a call to `Bundler.setup` will not attempt to make the -gems in those groups available on the Ruby load path, as they were -not installed. - -The settings that are remembered are: - -* `--deployment`: - At runtime, this remembered setting will also result in Bundler - raising an exception if the `Gemfile.lock` is out of date. - -* `--path`: - Subsequent calls to `bundle install` will install gems to the - directory originally passed to `--path`. The Bundler runtime - will look for gems in that location. You can revert this - option by running `bundle install --system`. - -* `--binstubs`: - Bundler will update the executables every subsequent call to - `bundle install`. - -* `--without`: - As described above, Bundler will skip the gems specified by - `--without` in subsequent calls to `bundle install`. The - Bundler runtime will also not try to make the gems in the - skipped groups available. - ## THE GEMFILE.LOCK When you run `bundle install`, Bundler will persist the full names diff --git a/man/gemfile.5.ronn b/man/gemfile.5.ronn index 876c927380..89911695fc 100644 --- a/man/gemfile.5.ronn +++ b/man/gemfile.5.ronn @@ -430,11 +430,15 @@ applied to a group of gems by using block form. gem "sqlite3" end - group :development do + group :development, :optional => true do gem "wirble" gem "faker" end +In the case of the group block form the :optional option can be given +to prevent a group from being installed unless listed in the `--with` +option given to the `bundle install` command. + In the case of the `git` block form, the `:ref`, `:branch`, `:tag`, and `:submodules` options may be passed to the `git` method, and all gems in the block will inherit those options. @@ -453,10 +457,10 @@ files in your test code as you would if the project were installed as a gem; you need not manipulate the load path manually or require project files via relative paths. -The `gemspec` method supports optional `:path`, `:name`, and `:development_group` -options, which control where bundler looks for the `.gemspec`, what named -`.gemspec` it uses (if more than one is present), and which group development -dependencies are included in. +The `gemspec` method supports optional `:path`, `:glob`, `:name`, and `:development_group` +options, which control where bundler looks for the `.gemspec`, the glob it uses to look +for the gemspec (defaults to: "{,*,*/*}.gemspec"), what named `.gemspec` it uses +(if more than one is present), and which group development dependencies are included in. ## SOURCE PRIORITY diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index f26183e073..fa226002cc 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -17,13 +17,11 @@ describe Bundler do end end - context "on Ruby 1.8", :ruby => "1.8" do - it "catches YAML syntax errors" do - expect { subject }.to raise_error(Bundler::GemspecError) - end + it "catches YAML syntax errors" do + expect { subject }.to raise_error(Bundler::GemspecError) end - context "on Ruby 1.9", :ruby => "1.9", :if => defined?(YAML::ENGINE) do + context "on Rubies with a settable YAML engine", :if => defined?(YAML::ENGINE) do context "with Syck as YAML::Engine" do it "raises a GemspecError after YAML load throws ArgumentError" do orig_yamler, YAML::ENGINE.yamler = YAML::ENGINE.yamler, 'syck' diff --git a/spec/bundler/fetcher_spec.rb b/spec/bundler/fetcher_spec.rb index 4640f63519..0d47bc5e90 100644 --- a/spec/bundler/fetcher_spec.rb +++ b/spec/bundler/fetcher_spec.rb @@ -1,6 +1,9 @@ require 'spec_helper' +require 'bundler/fetcher' describe Bundler::Fetcher do + subject(:fetcher) { Bundler::Fetcher.new(double("remote", :uri => URI("https://example.com"))) } + before do allow(Bundler).to receive(:root){ Pathname.new("root") } end @@ -8,10 +11,10 @@ describe Bundler::Fetcher do describe "#user_agent" do it "builds user_agent with current ruby version and Bundler settings" do allow(Bundler.settings).to receive(:all).and_return(["foo", "bar"]) - expect(described_class.user_agent).to match(/bundler\/(\d.)/) - expect(described_class.user_agent).to match(/rubygems\/(\d.)/) - expect(described_class.user_agent).to match(/ruby\/(\d.)/) - expect(described_class.user_agent).to match(/options\/foo,bar/) + expect(fetcher.user_agent).to match(/bundler\/(\d.)/) + expect(fetcher.user_agent).to match(/rubygems\/(\d.)/) + expect(fetcher.user_agent).to match(/ruby\/(\d.)/) + expect(fetcher.user_agent).to match(/options\/foo,bar/) end end end diff --git a/spec/bundler/friendly_errors_spec.rb b/spec/bundler/friendly_errors_spec.rb index 96c333a739..ee0667cb2f 100644 --- a/spec/bundler/friendly_errors_spec.rb +++ b/spec/bundler/friendly_errors_spec.rb @@ -10,4 +10,21 @@ describe Bundler, "friendly errors" do end }.to raise_error(SystemExit) end + + describe "#issues_url" do + it "generates a search URL for the exception message" do + exception = Exception.new("Exception message") + + expect(Bundler.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=Exception+message&type=Issues") + end + + it "generates a search URL for only the first line of a multi-line exception message" do + exception = Exception.new(<<END) +First line of the exception message +Second line of the exception message +END + + expect(Bundler.issues_url(exception)).to eq("https://github.com/bundler/bundler/search?q=First+line+of+the+exception+message&type=Issues") + end + end end diff --git a/spec/bundler/gem_helper_spec.rb b/spec/bundler/gem_helper_spec.rb index 2b0f3d97aa..8cfacbac01 100644 --- a/spec/bundler/gem_helper_spec.rb +++ b/spec/bundler/gem_helper_spec.rb @@ -28,6 +28,15 @@ describe Bundler::GemHelper do end context "interpolates the name" do + before do + # Remove exception that prevents public pushes on older RubyGems versions + if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0") + content = File.read(app_gemspec_path) + content.sub!(/raise "RubyGems 2\.0 or newer.*/, "") + File.open(app_gemspec_path, "w"){|f| f.write(content) } + end + end + it "when there is only one gemspec" do expect(subject.gemspec.name).to eq(app_name) end @@ -62,7 +71,7 @@ describe Bundler::GemHelper do let(:app_version) { "0.1.0" } let(:app_gem_dir) { app_path.join("pkg") } let(:app_gem_path) { app_gem_dir.join("#{app_name}-#{app_version}.gem") } - let(:app_gemspec_content) { File.read(app_gemspec_path) } + let(:app_gemspec_content) { remove_push_guard(File.read(app_gemspec_path)) } before(:each) do content = app_gemspec_content.gsub("TODO: ", "") @@ -70,6 +79,14 @@ describe Bundler::GemHelper do File.open(app_gemspec_path, "w") { |file| file << content } end + def remove_push_guard(gemspec_content) + # Remove exception that prevents public pushes on older RubyGems versions + if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0") + gemspec_content.sub!(/raise "RubyGems 2\.0 or newer.*/, "") + end + gemspec_content + end + it "uses a shell UI for output" do expect(Bundler.ui).to be_a(Bundler::UI::Shell) end diff --git a/spec/bundler/retry_spec.rb b/spec/bundler/retry_spec.rb index 8e35a1013d..aa4e5e42ad 100644 --- a/spec/bundler/retry_spec.rb +++ b/spec/bundler/retry_spec.rb @@ -11,17 +11,6 @@ describe Bundler::Retry do expect(attempts).to eq(1) end - it "defaults to retrying twice" do - attempts = 0 - expect { - Bundler::Retry.new(nil).attempt do - attempts += 1 - raise "nope" - end - }.to raise_error("nope") - expect(attempts).to eq(3) - end - it "returns the first valid result" do jobs = [Proc.new{ raise "foo" }, Proc.new{ :bar }, Proc.new{ raise "foo" }] attempts = 0 diff --git a/spec/bundler/settings_spec.rb b/spec/bundler/settings_spec.rb index 1e197f22f0..cb8440689e 100644 --- a/spec/bundler/settings_spec.rb +++ b/spec/bundler/settings_spec.rb @@ -15,6 +15,34 @@ describe Bundler::Settings do end end + describe "#[]" do + context "when not set" do + context "when default value present" do + it "retrieves value" do + expect(settings[:retry]).to be 3 + end + end + + it "returns nil" do + expect(settings[:buttermilk]).to be nil + end + end + + context "when is boolean" do + it "returns a boolean" do + settings[:frozen] = "true" + expect(settings[:frozen]).to be true + end + context "when specific gem is configured" do + it "returns a boolean" do + settings["ignore_messages.foobar"] = "true" + expect(settings["ignore_messages.foobar"]).to be true + end + end + end + end + + describe "#mirror_for" do let(:uri) { URI("https://rubygems.org/") } diff --git a/spec/bundler/source_list_spec.rb b/spec/bundler/source_list_spec.rb index f5a8575ae8..25e0e54391 100644 --- a/spec/bundler/source_list_spec.rb +++ b/spec/bundler/source_list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Bundler::SourceList do before do - allow(Bundler).to receive(:root) { Pathname.new '/' } + allow(Bundler).to receive(:root) { Pathname.new './tmp/bundled_app' } end subject(:source_list) { Bundler::SourceList.new } @@ -40,26 +40,45 @@ describe Bundler::SourceList do end describe "#add_git_source" do - before do - @duplicate = source_list.add_git_source('uri' => 'git://host/path.git') - @new_source = source_list.add_git_source('uri' => 'git://host/path.git') - end - it "returns the new git source" do + @new_source = source_list.add_git_source('uri' => 'git://host/path.git') expect(@new_source).to be_instance_of(Bundler::Source::Git) end it "passes the provided options to the new source" do + @new_source = source_list.add_git_source('uri' => 'git://host/path.git') expect(@new_source.options).to eq('uri' => 'git://host/path.git') end it "adds the source to the beginning of git_sources" do + @new_source = source_list.add_git_source('uri' => 'git://host/path.git') expect(source_list.git_sources.first).to equal(@new_source) end it "removes existing duplicates" do + @duplicate = source_list.add_git_source('uri' => 'git://host/path.git') + @new_source = source_list.add_git_source('uri' => 'git://host/path.git') expect(source_list.git_sources).not_to include equal(@duplicate) end + + context "with the git: protocol" do + let(:msg) { "The git source `git://existing-git.org/path.git` " \ + "uses the `git` protocol, which transmits data without encryption. " \ + "Disable this warning with `bundle config git.allow_insecure true`, " \ + "or switch to the `https` protocol to keep your data secure." + } + + it "warns about git protocols" do + expect(Bundler.ui).to receive(:warn).with(msg) + source_list.add_git_source('uri' => 'git://existing-git.org/path.git') + end + + it "ignores git protocols on request" do + Bundler.settings["git.allow_insecure"] = true + expect(Bundler.ui).to_not receive(:warn).with(msg) + source_list.add_git_source('uri' => 'git://existing-git.org/path.git') + end + end end describe "#add_rubygems_source" do diff --git a/spec/cache/git_spec.rb b/spec/cache/git_spec.rb index b369504dcb..bfaa0c5f63 100644 --- a/spec/cache/git_spec.rb +++ b/spec/cache/git_spec.rb @@ -59,7 +59,7 @@ end bundle "#{cmd} --all" bundle "#{cmd} --all" - expect(err).to eq("") + expect(err).to lack_errors FileUtils.rm_rf lib_path("foo-1.0") should_be_installed "foo 1.0" end diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb index b92076911f..47bbe35317 100644 --- a/spec/commands/binstubs_spec.rb +++ b/spec/commands/binstubs_spec.rb @@ -47,7 +47,7 @@ describe "bundle binstubs <gem>" do bundle "binstubs" expect(exitstatus).to eq(1) if exitstatus - expect(err).to eq("`bundle binstubs` needs at least one gem to run.") + expect(err).to include("`bundle binstubs` needs at least one gem to run.") end it "does not bundle the bundler binary" do @@ -58,7 +58,7 @@ describe "bundle binstubs <gem>" do bundle "binstubs bundler" expect(bundled_app("bin/bundle")).not_to exist - expect(out).to eq("Sorry, Bundler can only be run via Rubygems.") + expect(out).to include("Sorry, Bundler can only be run via Rubygems.") end it "installs binstubs from git gems" do @@ -114,7 +114,7 @@ describe "bundle binstubs <gem>" do bundle "binstubs doesnt_exist" expect(exitstatus).to eq(7) if exitstatus - expect(err).to eq("Could not find gem 'doesnt_exist'.") + expect(err).to include("Could not find gem 'doesnt_exist'.") end end diff --git a/spec/commands/check_spec.rb b/spec/commands/check_spec.rb index 76d8264718..d5c1247bd3 100644 --- a/spec/commands/check_spec.rb +++ b/spec/commands/check_spec.rb @@ -9,7 +9,7 @@ describe "bundle check" do bundle :check expect(exitstatus).to eq(0) if exitstatus - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "works with the --gemfile flag when not in the directory" do @@ -20,7 +20,7 @@ describe "bundle check" do Dir.chdir tmp bundle "check --gemfile bundled_app/Gemfile" - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "creates a Gemfile.lock by default if one does not exist" do @@ -152,7 +152,7 @@ describe "bundle check" do G bundle :check - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "works with env conditionals" do @@ -183,7 +183,7 @@ describe "bundle check" do G bundle :check - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "outputs an error when the default Gemfile is not found" do @@ -242,7 +242,7 @@ describe "bundle check" do it "returns success" do bundle "check --path vendor/bundle" expect(exitstatus).to eq(0) if exitstatus - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "should write to .bundle/config" do @@ -265,7 +265,7 @@ describe "bundle check" do bundle :install bundle :check expect(exitstatus).to eq(0) if exitstatus - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end it "shows what is missing with the current Gemfile if it is not satisfied" do diff --git a/spec/commands/clean_spec.rb b/spec/commands/clean_spec.rb index 4c69e68eb8..14b9226844 100644 --- a/spec/commands/clean_spec.rb +++ b/spec/commands/clean_spec.rb @@ -36,7 +36,7 @@ describe "bundle clean" do bundle :clean - expect(out).to eq("Removing foo (1.0)") + expect(out).to include("Removing foo (1.0)") should_have_gems 'thin-1.0', 'rack-1.0.0' should_not_have_gems 'foo-1.0' @@ -64,7 +64,7 @@ describe "bundle clean" do bundle :clean - expect(out).to eq("Removing rack (0.9.1)") + expect(out).to include("Removing rack (0.9.1)") should_have_gems 'foo-1.0', 'rack-1.0.0' should_not_have_gems 'rack-0.9.1' @@ -92,7 +92,7 @@ describe "bundle clean" do bundle :clean - expect(out).to eq("Removing rack (1.0.0)") + expect(out).to include("Removing rack (1.0.0)") should_have_gems 'foo-1.0', 'rack-0.9.1' should_not_have_gems 'rack-1.0.0' @@ -115,7 +115,7 @@ describe "bundle clean" do bundle "install --without test_group" bundle :clean - expect(out).to eq("Removing rack (1.0.0)") + expect(out).to include("Removing rack (1.0.0)") should_have_gems 'foo-1.0' should_not_have_gems 'rack-1.0.0' @@ -170,7 +170,7 @@ describe "bundle clean" do bundle :clean - expect(out).to eq("Removing foo (#{revision[0..11]})") + expect(out).to include("Removing foo (#{revision[0..11]})") expect(vendored_gems("gems/rack-1.0.0")).to exist expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).not_to exist @@ -203,7 +203,7 @@ describe "bundle clean" do bundle "update" bundle :clean - expect(out).to eq("Removing foo-bar (#{revision[0..11]})") + expect(out).to include("Removing foo-bar (#{revision[0..11]})") expect(vendored_gems("gems/rack-1.0.0")).to exist expect(vendored_gems("bundler/gems/foo-bar-#{revision[0..11]}")).not_to exist @@ -227,7 +227,7 @@ describe "bundle clean" do bundle "install --path vendor/bundle" bundle :clean - expect(out).to eq("") + expect(out).to include("") expect(vendored_gems("bundler/gems/rails-#{revision[0..11]}")).to exist end @@ -251,7 +251,7 @@ describe "bundle clean" do bundle :clean - expect(out).to eq("") + expect(out).to include("") expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).to exist digest = Digest::SHA1.hexdigest(git_path.to_s) expect(vendored_gems("cache/bundler/git/foo-#{digest}")).to_not exist @@ -451,7 +451,7 @@ describe "bundle clean" do bundle :install bundle "clean --force" - expect(out).to eq("Removing foo (1.0)") + expect(out).to include("Removing foo (1.0)") sys_exec "gem list" expect(out).not_to include("foo (1.0)") expect(out).to include("rack (1.0.0)") @@ -552,8 +552,8 @@ describe "bundle clean" do bundle "clean --dry-run" - expect(out).not_to eq("Removing foo (1.0)") - expect(out).to eq("Would have removed foo (1.0)") + expect(out).not_to include("Removing foo (1.0)") + expect(out).to include("Would have removed foo (1.0)") should_have_gems 'thin-1.0', 'rack-1.0.0', 'foo-1.0' @@ -581,8 +581,8 @@ describe "bundle clean" do bundle "clean" - expect(out).to eq("Removing foo (1.0)") - expect(out).not_to eq("Would have removed foo (1.0)") + expect(out).to include("Removing foo (1.0)") + expect(out).not_to include("Would have removed foo (1.0)") should_have_gems 'thin-1.0', 'rack-1.0.0' should_not_have_gems 'foo-1.0' diff --git a/spec/commands/config_spec.rb b/spec/commands/config_spec.rb index f12a844cc1..cd1f23e4a0 100644 --- a/spec/commands/config_spec.rb +++ b/spec/commands/config_spec.rb @@ -258,6 +258,9 @@ E "here is quite a long string that will wrap to a second line but will not be " \ "surrounded by quotes" end + let(:long_string_without_special_characters) do + "here is quite a long string that will wrap to a second line but will not be surrounded by quotes" + end it "doesn't wrap values" do bundle "config foo #{long_string}" diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index ebc8c7cfde..47e9629a63 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -30,7 +30,7 @@ describe "bundle exec" do bundle "exec 'cd #{tmp('gems')} && rackup'" - expect(out).to eq("1.0.0") + expect(out).to include("1.0.0") end it "works when exec'ing something else" do @@ -85,14 +85,14 @@ describe "bundle exec" do expect(out).to eq("Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec.") end - expect(err).to eq("") + expect(err).to lack_errors end it "accepts --keep-file-descriptors" do install_gemfile '' bundle "exec --keep-file-descriptors echo foobar" - expect(err).to eq("") + expect(err).to lack_errors end it "can run a command named --verbose" do @@ -178,10 +178,10 @@ describe "bundle exec" do rubylib = rubylib.uniq.join(File::PATH_SEPARATOR) bundle "exec 'echo $RUBYLIB'" - expect(out).to eq(rubylib) + expect(out).to include(rubylib) bundle "exec 'echo $RUBYLIB'", :env => {"RUBYLIB" => rubylib} - expect(out).to eq(rubylib) + expect(out).to include(rubylib) end it "errors nicely when the argument doesn't exist" do @@ -226,13 +226,13 @@ describe "bundle exec" do it "works when unlocked" do bundle "exec 'cd #{tmp('gems')} && rackup'" - expect(out).to eq("1.0.0") + expect(out).to include("1.0.0") end it "works when locked" do should_be_locked bundle "exec 'cd #{tmp('gems')} && rackup'" - expect(out).to eq("1.0.0") + expect(out).to include("1.0.0") end end diff --git a/spec/commands/lock_spec.rb b/spec/commands/lock_spec.rb index 5e6f07dd4a..975e541d15 100644 --- a/spec/commands/lock_spec.rb +++ b/spec/commands/lock_spec.rb @@ -47,13 +47,16 @@ describe "bundle lock" do foo rails with_license + + BUNDLED WITH + #{Bundler::VERSION} L end it "prints a lockfile when there is no existing lockfile with --print" do bundle "lock --print" - expect(out).to eq(@lockfile) + expect(out).to include(@lockfile) end it "prints a lockfile when there is an existing lockfile with --print" do diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb index c8c5bf5917..9dd54176e2 100644 --- a/spec/commands/newgem_spec.rb +++ b/spec/commands/newgem_spec.rb @@ -7,6 +7,15 @@ describe "bundle gem" do global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false" end + def remove_push_guard(gem_name) + # Remove exception that prevents public pushes on older RubyGems versions + if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0") + path = "#{gem_name}/#{gem_name}.gemspec" + content = File.read(path).sub(/raise "RubyGems 2\.0 or newer.*/, "") + File.open(path, "w"){|f| f.write(content) } + end + end + before do @git_name = `git config --global user.name`.chomp `git config --global user.name "Bundler User"` @@ -48,7 +57,6 @@ describe "bundle gem" do bundle "gem newgem --bin" process_file(bundled_app('newgem', "newgem.gemspec")) do |line| - next line unless line =~ /TODO/ # Simulate replacing TODOs with real values case line when /spec\.metadata\['allowed_push_host'\]/, /spec\.homepage/ @@ -57,6 +65,9 @@ describe "bundle gem" do line.gsub(/\=.*$/, "= %q{A short summary of my new gem.}") when /spec\.description/ line.gsub(/\=.*$/, "= %q{A longer description of my new gem.}") + # Remove exception that prevents public pushes on older RubyGems versions + when /raise "RubyGems 2.0 or newer/ + line.gsub(/.*/, '') if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.0") else line end @@ -112,6 +123,7 @@ describe "bundle gem" do before do bundle "gem #{gem_name}" + remove_push_guard(gem_name) # reset gemspec cache for each test because of commit 3d4163a Bundler.clear_gemspec_cache end @@ -125,6 +137,11 @@ describe "bundle gem" do expect(bundled_app("test_gem/lib/test_gem.rb")).to exist expect(bundled_app("test_gem/lib/test_gem/version.rb")).to exist expect(bundled_app("test_gem/.gitignore")).to exist + + expect(bundled_app("test_gem/bin/setup")).to exist + expect(bundled_app("test_gem/bin/console")).to exist + expect(bundled_app("test_gem/bin/setup")).to be_executable + expect(bundled_app("test_gem/bin/console")).to be_executable end it "starts with version 0.1.0" do @@ -145,6 +162,7 @@ describe "bundle gem" do reset! in_app_root bundle "gem #{gem_name}" + remove_push_guard(gem_name) end it_should_behave_like "git config is absent" @@ -152,7 +170,7 @@ describe "bundle gem" do it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do expect(generated_gem.gemspec.metadata['allowed_push_host']). - to match("delete to allow pushes to any server") + to match(/mygemserver\.com/) end it "requires the version file" do @@ -255,8 +273,8 @@ describe "bundle gem" do end it "builds spec skeleton" do - expect(bundled_app("test_gem/test/test_test_gem.rb")).to exist - expect(bundled_app("test_gem/test/minitest_helper.rb")).to exist + expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist + expect(bundled_app("test_gem/test/test_helper.rb")).to exist end end @@ -268,20 +286,20 @@ describe "bundle gem" do end it "builds spec skeleton" do - expect(bundled_app("test_gem/test/test_test_gem.rb")).to exist - expect(bundled_app("test_gem/test/minitest_helper.rb")).to exist + expect(bundled_app("test_gem/test/test_gem_test.rb")).to exist + expect(bundled_app("test_gem/test/test_helper.rb")).to exist end it "requires 'test-gem'" do - expect(bundled_app("test_gem/test/minitest_helper.rb").read).to include("require 'test_gem'") + expect(bundled_app("test_gem/test/test_helper.rb").read).to include("require 'test_gem'") end it "requires 'minitest_helper'" do - expect(bundled_app("test_gem/test/test_test_gem.rb").read).to include("require 'minitest_helper'") + expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("require 'test_helper'") end it "creates a default test which fails" do - expect(bundled_app("test_gem/test/test_test_gem.rb").read).to include("assert false") + expect(bundled_app("test_gem/test/test_gem_test.rb").read).to include("assert false") end end @@ -318,6 +336,7 @@ describe "bundle gem" do before do bundle "gem #{gem_name} --mit" + remove_push_guard(gem_name) # reset gemspec cache for each test because of commit 3d4163a Bundler.clear_gemspec_cache end @@ -359,6 +378,7 @@ describe "bundle gem" do before do bundle "gem #{gem_name}" + remove_push_guard(gem_name) # reset gemspec cache for each test because of commit 3d4163a Bundler.clear_gemspec_cache end @@ -392,16 +412,12 @@ describe "bundle gem" do reset! in_app_root bundle "gem #{gem_name}" + remove_push_guard(gem_name) end it_should_behave_like "git config is absent" end - it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do - expect(generated_gem.gemspec.metadata['allowed_push_host']). - to match("delete to allow pushes to any server") - end - it "requires the version file" do expect(bundled_app("test-gem/lib/test/gem.rb").read).to match(/require "test\/gem\/version"/) end @@ -499,20 +515,20 @@ describe "bundle gem" do end it "builds spec skeleton" do - expect(bundled_app("test-gem/test/test_test/gem.rb")).to exist - expect(bundled_app("test-gem/test/minitest_helper.rb")).to exist + expect(bundled_app("test-gem/test/test/gem_test.rb")).to exist + expect(bundled_app("test-gem/test/test_helper.rb")).to exist end it "requires 'test/gem'" do - expect(bundled_app("test-gem/test/minitest_helper.rb").read).to match(/require 'test\/gem'/) + expect(bundled_app("test-gem/test/test_helper.rb").read).to match(/require 'test\/gem'/) end - it "requires 'minitest_helper'" do - expect(bundled_app("test-gem/test/test_test/gem.rb").read).to match(/require 'minitest_helper'/) + it "requires 'test_helper'" do + expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/require 'test_helper'/) end it "creates a default test which fails" do - expect(bundled_app("test-gem/test/test_test/gem.rb").read).to match(/assert false/) + expect(bundled_app("test-gem/test/test/gem_test.rb").read).to match(/assert false/) end it "creates a default rake task to run the test suite" do @@ -522,6 +538,8 @@ describe "bundle gem" do Rake::TestTask.new(:test) do |t| t.libs << "test" + t.libs << "lib" + t.test_files = FileList['test/**/*_test.rb'] end task :default => :test @@ -578,6 +596,44 @@ describe "bundle gem" do end end + describe "uncommon gem names" do + it "can deal with two dashes" do + bundle "gem a--a" + Bundler.clear_gemspec_cache + + expect(bundled_app("a--a/a--a.gemspec")).to exist + end + end + + describe "#ensure_safe_gem_name" do + before do + bundle "gem #{subject}" + end + after do + Bundler.clear_gemspec_cache + end + + context "with an existing const name" do + subject { "gem" } + it { expect(out).to include("Invalid gem name #{subject}") } + end + + context "with an existing hyphenated const name" do + subject { "gem-specification" } + it { expect(out).to include("Invalid gem name #{subject}") } + end + + context "starting with an existing const name" do + subject { "gem-somenewconstantname" } + it { expect(out).not_to include("Invalid gem name #{subject}") } + end + + context "ending with an existing const name" do + subject { "somenewconstantname-gem" } + it { expect(out).not_to include("Invalid gem name #{subject}") } + end + end + context "on first run" do before do in_app_root @@ -591,6 +647,17 @@ describe "bundle gem" do end expect(bundled_app("foobar/spec/spec_helper.rb")).to exist + rakefile = strip_whitespace <<-RAKEFILE + require "bundler/gem_tasks" + require "rspec/core/rake_task" + + RSpec::Core::RakeTask.new(:spec) + + task :default => :spec + RAKEFILE + + expect(bundled_app("foobar/Rakefile").read).to eq(rakefile) + expect(bundled_app("foobar/foobar.gemspec").read).to include('spec.add_development_dependency "rspec"') end it "asks about MIT license" do diff --git a/spec/commands/open_spec.rb b/spec/commands/open_spec.rb index 099a400a55..71945a85fa 100644 --- a/spec/commands/open_spec.rb +++ b/spec/commands/open_spec.rb @@ -10,17 +10,17 @@ describe "bundle open" do it "opens the gem with BUNDLER_EDITOR as highest priority" do bundle "open rails", :env => {"EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor"} - expect(out).to eq("bundler_editor #{default_bundle_path('gems', 'rails-2.3.2')}") + expect(out).to include("bundler_editor #{default_bundle_path('gems', 'rails-2.3.2')}") end it "opens the gem with VISUAL as 2nd highest priority" do bundle "open rails", :env => {"EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => ""} - expect(out).to eq("visual #{default_bundle_path('gems', 'rails-2.3.2')}") + expect(out).to include("visual #{default_bundle_path('gems', 'rails-2.3.2')}") end it "opens the gem with EDITOR as 3rd highest priority" do bundle "open rails", :env => {"EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => ""} - expect(out).to eq("editor #{default_bundle_path('gems', 'rails-2.3.2')}") + expect(out).to include("editor #{default_bundle_path('gems', 'rails-2.3.2')}") end it "complains if no EDITOR is set" do @@ -54,7 +54,7 @@ describe "bundle open" do it "opens the gem with short words" do bundle "open rec" , :env => {"EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor"} - expect(out).to eq("bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}") + expect(out).to include("bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}") end it "select the gem from many match gems" do diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb index 7ccd383f5f..6bb3a94baf 100644 --- a/spec/commands/outdated_spec.rb +++ b/spec/commands/outdated_spec.rb @@ -27,9 +27,9 @@ describe "bundle outdated" do bundle "outdated" - expect(out).to include("activesupport (3.0 > 2.3.5) Gemfile specifies \"= 2.3.5\"") - expect(out).to include("weakling (0.2 > 0.0.3) Gemfile specifies \"~> 0.0.1\"") - expect(out).to include("foo (1.0") + expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)") + expect(out).to include("weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)") + expect(out).to include("foo (newest 1.0") # Gem names are one per-line, between "*" and their parenthesized version. gem_list = out.split("\n").map { |g| g[ /\* (.*) \(/, 1] }.compact @@ -52,6 +52,21 @@ describe "bundle outdated" do expect(exitstatus).to be_zero if exitstatus end + + it "adds gem group to dependency output when repo is updated" do + install_gemfile <<-G + source "file://#{gem_repo2}" + + group :development, :test do + gem 'activesupport', '2.3.5' + end + G + + update_repo2 { build_gem "activesupport", "3.0" } + + bundle "outdated --verbose" + expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5) in groups \"development, test\"") + end end describe "with --local option" do @@ -71,8 +86,8 @@ describe "bundle outdated" do end bundle "outdated foo" - expect(out).not_to include("activesupport (3.0 > 2.3.5)") - expect(out).to include("foo (1.0") + expect(out).not_to include("activesupport (newest") + expect(out).to include("foo (newest 1.0") end end @@ -95,7 +110,7 @@ describe "bundle outdated" do end bundle "outdated --pre" - expect(out).to include("activesupport (3.0.0.beta > 2.3.5) Gemfile specifies \"= 2.3.5\"") + expect(out).to include("activesupport (newest 3.0.0.beta, installed 2.3.5, requested = 2.3.5)") end end @@ -112,7 +127,7 @@ describe "bundle outdated" do G bundle "outdated" - expect(out).to include("activesupport (3.0.0.beta.2 > 3.0.0.beta.1) Gemfile specifies \"= 3.0.0.beta.1\"") + expect(out).to include("(newest 3.0.0.beta.2, installed 3.0.0.beta.1, requested = 3.0.0.beta.1)") end end end @@ -126,8 +141,8 @@ describe "bundle outdated" do bundle "outdated --strict" - expect(out).to_not include("activesupport (3.0 > 2.3.5) Gemfile specifies \"= 2.3.5\"") - expect(out).to include("weakling (0.0.5 > 0.0.3) Gemfile specifies \"~> 0.0.1\"") + expect(out).to_not include("activesupport (newest") + expect(out).to include("(newest 0.0.5, installed 0.0.3, requested ~> 0.0.1)") end it "only reports gem dependencies when they can actually be updated" do @@ -138,7 +153,7 @@ describe "bundle outdated" do bundle "outdated --strict" - expect(out).to_not include("rack (1.2 > 0.9.1)") + expect(out).to_not include("rack (1.2") end end diff --git a/spec/commands/package_spec.rb b/spec/commands/package_spec.rb index f72868cb2a..bc0ebdfc21 100644 --- a/spec/commands/package_spec.rb +++ b/spec/commands/package_spec.rb @@ -100,7 +100,7 @@ describe "bundle install with gem sources" do end bundle :install - expect(err).to be_empty + expect(err).to lack_errors should_be_installed "rack 1.0" end diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb index 3e5f9f8020..f38da20222 100644 --- a/spec/commands/show_spec.rb +++ b/spec/commands/show_spec.rb @@ -121,7 +121,7 @@ describe "bundle show" do it "does not output git errors" do bundle :show - expect(err).to be_empty + expect(err).to lack_errors end end diff --git a/spec/deprecation_spec.rb b/spec/deprecation_spec.rb new file mode 100644 index 0000000000..fce0702653 --- /dev/null +++ b/spec/deprecation_spec.rb @@ -0,0 +1,119 @@ +require "spec_helper" + +describe "Bundler version 1.99" do + context "when bundle is run" do + it "should print a single deprecation warning" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + expect(err).to eq("DEPRECATION: Gemfile and Gemfile.lock are " \ + "deprecated and will be replaced with gems.rb and gems.locked in " \ + "Bundler 2.0.") + end + + it "should not warn about gems.rb" do + create_file "gems.rb", <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle :install + expect(err).to lack_errors + end + end + + context "when Bundler.setup is run in a ruby script" do + it "should print a single deprecation warning" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack", :group => :test + G + + ruby <<-RUBY + require 'rubygems' + require 'bundler' + require 'bundler/vendored_thor' + + Bundler.ui = Bundler::UI::Shell.new + Bundler.setup + Bundler.setup + RUBY + + expect(err).to eq("DEPRECATION: Gemfile and Gemfile.lock are " \ + "deprecated and will be replaced with gems.rb and gems.locked in " \ + "Bundler 2.0.") + end + end + + context "when `bundler/deployment` is required in a ruby script" do + it "should print a capistrano deprecation warning" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack", :group => :test + G + + ruby(<<-RUBY, { :expect_err => true }) + require 'bundler/deployment' + RUBY + + expect(err).to eq("DEPRECATION: Bundler no longer integrates " \ + "with Capistrano, but Capistrano provides " \ + "its own integration with Bundler via the " \ + "capistrano-bundler gem. Use it instead.") + end + end + + describe Bundler::Dsl do + before do + @rubygems = double("rubygems") + allow(Bundler::Source::Rubygems).to receive(:new){ @rubygems } + end + + context "with github gems" do + it "warns about the https change" do + allow(Bundler.ui).to receive(:deprecate) + msg = "The :github option uses the git: protocol, which is not secure. " \ + "Bundler 2.0 will use the https: protcol, which is secure. Enable this change now by " \ + "running `bundle config github.https true`." + expect(Bundler.ui).to receive(:deprecate).with(msg) + subject.gem("sparks", :github => "indirect/sparks") + end + + it "upgrades to https on request" do + Bundler.settings["github.https"] = true + subject.gem("sparks", :github => "indirect/sparks") + github_uri = "https://github.com/indirect/sparks.git" + expect(subject.dependencies.first.source.uri).to eq(github_uri) + end + end + + context "with bitbucket gems" do + it "warns about removal" do + allow(Bundler.ui).to receive(:deprecate) + msg = "The :bitbucket git source is deprecated, and will be removed " \ + "in Bundler 2.0. Add this code to your Gemfile to ensure it " \ + "continues to work:\n git_source(:bitbucket) do |repo_name|\n " \ + " https://\#{user_name}@bitbucket.org/\#{user_name}/\#{repo_name}" \ + ".git\n end" + expect(Bundler.ui).to receive(:deprecate).with(msg, true) + subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails") + end + end + + context "with gist gems" do + it "warns about removal" do + allow(Bundler.ui).to receive(:deprecate) + msg = "The :gist git source is deprecated, and will be removed " \ + "in Bundler 2.0. Add this code to your Gemfile to ensure it " \ + "continues to work:\n git_source(:gist) do |repo_name|\n " \ + " https://gist.github.com/\#{repo_name}.git\n" \ + " end" + expect(Bundler.ui).to receive(:deprecate).with(msg, true) + subject.gem("not-really-a-gem", :gist => "1234") + end + end + end + +end diff --git a/spec/install/bundler_spec.rb b/spec/install/bundler_spec.rb index 309f8c4e88..53e4768c18 100644 --- a/spec/install/bundler_spec.rb +++ b/spec/install/bundler_spec.rb @@ -102,7 +102,7 @@ describe "bundle install" do rails_fail (>= 0) ruby depends on activesupport (= 1.2.3) ruby E - expect(err).to eq(nice_error) + expect(err).to include(nice_error) end it "causes a conflict if a child dependency conflicts with the Gemfile" do @@ -120,7 +120,7 @@ describe "bundle install" do activesupport (= 2.3.5) ruby E - expect(err).to eq(nice_error) + expect(err).to include(nice_error) end it "can install dependencies with newer bundler version" do @@ -133,7 +133,7 @@ describe "bundle install" do #simulate_new_machine bundle "check" - expect(out).to eq("The Gemfile's dependencies are satisfied") + expect(out).to include("The Gemfile's dependencies are satisfied") end end diff --git a/spec/install/force_spec.rb b/spec/install/force_spec.rb new file mode 100644 index 0000000000..269390994a --- /dev/null +++ b/spec/install/force_spec.rb @@ -0,0 +1,20 @@ +require "spec_helper" + +describe "bundle install" do + describe "with --force" do + before :each do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle "install" + end + + it "re-installs installed gems" do + bundle "install --force" + expect(out).to include "Installing rack 1.0.0" + should_be_installed "rack 1.0.0" + end + end +end diff --git a/spec/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb index ccaee588bd..107f44b08b 100644 --- a/spec/install/gemfile/git_spec.rb +++ b/spec/install/gemfile/git_spec.rb @@ -162,7 +162,7 @@ describe "bundle install with git sources" do gem "foo" end G - expect(err).to eq("") + expect(err).to lack_errors run <<-RUBY require 'foo' @@ -782,7 +782,7 @@ describe "bundle install with git sources" do bundle :install, :expect_err => true, :requires => [lib_path('install_hooks.rb')] - expect(err).to eq("Ran pre-install hook: foo-1.0") + expect(err).to eq_err("Ran pre-install hook: foo-1.0") end it "runs post-install hooks" do @@ -802,7 +802,7 @@ describe "bundle install with git sources" do bundle :install, :expect_err => true, :requires => [lib_path('install_hooks.rb')] - expect(err).to eq("Ran post-install hook: foo-1.0") + expect(err).to eq_err("Ran post-install hook: foo-1.0") end it "complains if the install hook fails" do diff --git a/spec/install/gemfile/path_spec.rb b/spec/install/gemfile/path_spec.rb index 7dd651f70d..57c7e9d366 100644 --- a/spec/install/gemfile/path_spec.rb +++ b/spec/install/gemfile/path_spec.rb @@ -262,7 +262,7 @@ describe "bundle install with explicit source paths" do install_gemfile <<-G gem 'foo', '1.0', :path => "#{lib_path('foo-1.0')}" G - expect(err).to eq("") + expect(err).to lack_errors end it "removes the .gem file after installing" do @@ -472,7 +472,7 @@ describe "bundle install with explicit source paths" do bundle :install, :expect_err => true, :requires => [lib_path('install_hooks.rb')] - expect(err).to eq("Ran pre-install hook: foo-1.0") + expect(err).to eq_err("Ran pre-install hook: foo-1.0") end it "runs post-install hooks" do @@ -492,7 +492,7 @@ describe "bundle install with explicit source paths" do bundle :install, :expect_err => true, :requires => [lib_path('install_hooks.rb')] - expect(err).to eq("Ran post-install hook: foo-1.0") + expect(err).to eq_err("Ran post-install hook: foo-1.0") end it "complains if the install hook fails" do diff --git a/spec/install/gems/flex_spec.rb b/spec/install/gems/flex_spec.rb index ec565ca790..50638784dd 100644 --- a/spec/install/gems/flex_spec.rb +++ b/spec/install/gems/flex_spec.rb @@ -265,6 +265,9 @@ describe "bundle flex_install" do DEPENDENCIES rack + + BUNDLED WITH + #{Bundler::VERSION} L end end diff --git a/spec/install/gems/groups_spec.rb b/spec/install/gems/groups_spec.rb index 23af4aee65..c6bbebb435 100644 --- a/spec/install/gems/groups_spec.rb +++ b/spec/install/gems/groups_spec.rb @@ -26,7 +26,7 @@ describe "bundle install with groups" do puts ACTIVESUPPORT R - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "installs gems with inline :groups into those groups" do @@ -37,7 +37,7 @@ describe "bundle install with groups" do puts THIN R - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "sets up everything if Bundler.setup is used with no groups" do @@ -58,7 +58,7 @@ describe "bundle install with groups" do puts THIN RUBY - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "sets up old groups when they have previously been removed" do @@ -80,6 +80,9 @@ describe "bundle install with groups" do group :emo do gem "activesupport", "2.3.5" end + group :debugging, :optional => true do + gem "thin" + end G end @@ -159,6 +162,69 @@ describe "bundle install with groups" do bundle :install should_not_be_installed "activesupport 2.3.5" end + + it "does not install gems from the optional group" do + bundle :install + should_not_be_installed "thin 1.0" + end + + it "does install gems from the optional group when requested" do + bundle :install, :with => "debugging" + should_be_installed "thin 1.0" + end + + it "does install gems from the previously requested group" do + bundle :install, :with => "debugging" + should_be_installed "thin 1.0" + bundle :install + should_be_installed "thin 1.0" + end + + it "does install gems from the optional groups requested with BUNDLE_WITH" do + ENV["BUNDLE_WITH"] = "debugging" + bundle :install + should_be_installed "thin 1.0" + ENV["BUNDLE_WITH"] = nil + end + + it "clears with when passed an empty list" do + bundle :install, :with => "debugging" + bundle 'install --with ""' + should_not_be_installed "thin 1.0" + end + + it "does remove groups from without when passed at with" do + bundle :install, :without => "emo" + bundle :install, :with => "emo" + should_be_installed "activesupport 2.3.5" + end + + it "does remove groups from with when passed at without" do + bundle :install, :with => "debugging" + bundle :install, :without => "debugging" + should_not_be_installed "thin 1.0" + end + + it "errors out when passing a group to with and without" do + bundle :install, :with => "emo debugging", :without => "emo" + expect(out).to include("The offending groups are: emo") + end + + it "can add and remove a group at the same time" do + bundle :install, :with => "debugging", :without => "emo" + should_be_installed "thin 1.0" + should_not_be_installed "activesupport 2.3.5" + end + + it "does have no effect when listing a not optional group in with" do + bundle :install, :with => "emo" + should_be_installed "activesupport 2.3.5" + end + + it "does have no effect when listing an optional group in without" do + bundle :install, :without => "debugging" + should_not_be_installed "thin 1.0" + end end describe "with gems assigned to multiple groups" do @@ -301,7 +367,7 @@ describe "bundle install with groups" do it "does not hit the remote a second time" do FileUtils.rm_rf gem_repo2 bundle "install --without rack" - expect(err).to be_empty + expect(err).to lack_errors end end diff --git a/spec/install/gems/install_if.rb b/spec/install/gems/install_if.rb new file mode 100644 index 0000000000..79e6dd87bd --- /dev/null +++ b/spec/install/gems/install_if.rb @@ -0,0 +1,38 @@ +require "spec_helper" + +describe "bundle install with install_if conditionals" do + it "follows the install_if DSL" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + install_if(lambda { true }) do + gem "activesupport", "2.3.5" + end + gem "thin", :install_if => false + G + + should_be_installed("rack 1.0", "activesupport 2.3.5") + should_not_be_installed("thin") + + lockfile_should_be <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + activesupport (2.3.5) + rack (1.0.0) + thin (1.0) + rack + + PLATFORMS + ruby + + DEPENDENCIES + activesupport (= 2.3.5) + rack + thin + + BUNDLED WITH + 1.9.4 + L + end +end diff --git a/spec/install/gems/post_install_spec.rb b/spec/install/gems/post_install_spec.rb index 69841fea44..76a3a6ca0d 100644 --- a/spec/install/gems/post_install_spec.rb +++ b/spec/install/gems/post_install_spec.rb @@ -1,121 +1,150 @@ require 'spec_helper' -describe "bundle install with gem sources" do - describe "when gems include post install messages" do - it "should display the post-install messages after installing" do - gemfile <<-G - source "file://#{gem_repo1}" - gem 'rack' - gem 'thin' - gem 'rack-obama' - G - - bundle :install - expect(out).to include("Post-install message from rack:") - expect(out).to include("Rack's post install message") - expect(out).to include("Post-install message from thin:") - expect(out).to include("Thin's post install message") - expect(out).to include("Post-install message from rack-obama:") - expect(out).to include("Rack-obama's post install message") +describe "bundle install" do + context "with gem sources" do + context "when gems include post install messages" do + it "should display the post-install messages after installing" do + gemfile <<-G + source "file://#{gem_repo1}" + gem 'rack' + gem 'thin' + gem 'rack-obama' + G + + bundle :install + expect(out).to include("Post-install message from rack:") + expect(out).to include("Rack's post install message") + expect(out).to include("Post-install message from thin:") + expect(out).to include("Thin's post install message") + expect(out).to include("Post-install message from rack-obama:") + expect(out).to include("Rack-obama's post install message") + end end - end - describe "when gems do not include post install messages" do - it "should not display any post-install messages" do - gemfile <<-G - source "file://#{gem_repo1}" - gem "activesupport" - G + context "when gems do not include post install messages" do + it "should not display any post-install messages" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "activesupport" + G - bundle :install - expect(out).not_to include("Post-install message") + bundle :install + expect(out).not_to include("Post-install message") + end end - end - describe "when a dependecy includes a post install message" do - it "should display the post install message" do - gemfile <<-G - source "file://#{gem_repo1}" - gem 'rack_middleware' - G + context "when a dependecy includes a post install message" do + it "should display the post install message" do + gemfile <<-G + source "file://#{gem_repo1}" + gem 'rack_middleware' + G - bundle :install - expect(out).to include("Post-install message from rack:") - expect(out).to include("Rack's post install message") + bundle :install + expect(out).to include("Post-install message from rack:") + expect(out).to include("Rack's post install message") + end end end -end -describe "bundle install with git sources" do - describe "when gems include post install messages" do - it "should display the post-install messages after installing" do - build_git "foo" do |s| - s.post_install_message = "Foo's post install message" + context "with git sources" do + context "when gems include post install messages" do + it "should display the post-install messages after installing" do + build_git "foo" do |s| + s.post_install_message = "Foo's post install message" + end + gemfile <<-G + source "file://#{gem_repo1}" + gem 'foo', :git => '#{lib_path("foo-1.0")}' + G + + bundle :install + expect(out).to include("Post-install message from foo:") + expect(out).to include("Foo's post install message") end - gemfile <<-G - source "file://#{gem_repo1}" - gem 'foo', :git => '#{lib_path("foo-1.0")}' - G - - bundle :install - expect(out).to include("Post-install message from foo:") - expect(out).to include("Foo's post install message") - end - it "should display the post-install messages if repo is updated" do - build_git "foo" do |s| - s.post_install_message = "Foo's post install message" + it "should display the post-install messages if repo is updated" do + build_git "foo" do |s| + s.post_install_message = "Foo's post install message" + end + gemfile <<-G + source "file://#{gem_repo1}" + gem 'foo', :git => '#{lib_path("foo-1.0")}' + G + bundle :install + + build_git "foo", "1.1" do |s| + s.post_install_message = "Foo's 1.1 post install message" + end + gemfile <<-G + source "file://#{gem_repo1}" + gem 'foo', :git => '#{lib_path("foo-1.1")}' + G + bundle :install + + expect(out).to include("Post-install message from foo:") + expect(out).to include("Foo's 1.1 post install message") end - gemfile <<-G - source "file://#{gem_repo1}" - gem 'foo', :git => '#{lib_path("foo-1.0")}' - G - bundle :install - build_git "foo", "1.1" do |s| - s.post_install_message = "Foo's 1.1 post install message" + it "should not display the post-install messages if repo is not updated" do + build_git "foo" do |s| + s.post_install_message = "Foo's post install message" + end + gemfile <<-G + source "file://#{gem_repo1}" + gem 'foo', :git => '#{lib_path("foo-1.0")}' + G + + bundle :install + expect(out).to include("Post-install message from foo:") + expect(out).to include("Foo's post install message") + + bundle :install + expect(out).not_to include("Post-install message") end - gemfile <<-G - source "file://#{gem_repo1}" - gem 'foo', :git => '#{lib_path("foo-1.1")}' - G - bundle :install - - expect(out).to include("Post-install message from foo:") - expect(out).to include("Foo's 1.1 post install message") end - it "should not display the post-install messages if repo is not updated" do - build_git "foo" do |s| - s.post_install_message = "Foo's post install message" + context "when gems do not include post install messages" do + it "should not display any post-install messages" do + build_git "foo" do |s| + s.post_install_message = nil + end + gemfile <<-G + source "file://#{gem_repo1}" + gem 'foo', :git => '#{lib_path("foo-1.0")}' + G + + bundle :install + expect(out).not_to include("Post-install message") end + end + end + + context "when ignore post-install messages for gem is set" do + it "doesn't display any post-install messages" do gemfile <<-G source "file://#{gem_repo1}" - gem 'foo', :git => '#{lib_path("foo-1.0")}' + gem "rack" G - bundle :install - expect(out).to include("Post-install message from foo:") - expect(out).to include("Foo's post install message") + bundle "config ignore_messages.rack true" bundle :install expect(out).not_to include("Post-install message") end end - describe "when gems do not include post install messages" do - it "should not display any post-install messages" do - build_git "foo" do |s| - s.post_install_message = nil - end + context "when ignore post-install messages for all gems" do + it "doesn't display any post-install messages" do gemfile <<-G source "file://#{gem_repo1}" - gem 'foo', :git => '#{lib_path("foo-1.0")}' + gem "rack" G + bundle "config ignore_messages true" + bundle :install expect(out).not_to include("Post-install message") end end - end diff --git a/spec/install/gems/sources_spec.rb b/spec/install/gems/sources_spec.rb index a268436248..78a1c0af2b 100644 --- a/spec/install/gems/sources_spec.rb +++ b/spec/install/gems/sources_spec.rb @@ -30,7 +30,7 @@ describe "bundle install with gems on multiple sources" do it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first" do bundle :install - expect(out).to include("Warning: this Gemfile contains multiple primary sources.") + expect(err).to include("DEPRECATION: Your Gemfile contains multiple primary sources.") expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: file:#{gem_repo1}") should_be_installed("rack-obama 1.0.0", "rack 1.0.0") @@ -59,7 +59,7 @@ describe "bundle install with gems on multiple sources" do it "warns about ambiguous gems, but installs anyway" do bundle :install - expect(out).to include("Warning: this Gemfile contains multiple primary sources.") + expect(err).to include("DEPRECATION: Your Gemfile contains multiple primary sources.") expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: file:#{gem_repo1}") should_be_installed("rack-obama 1.0.0", "rack 1.0.0") @@ -223,12 +223,47 @@ describe "bundle install with gems on multiple sources" do it "installs from the other source and warns about ambiguous gems" do bundle :install - expect(out).to include("Warning: this Gemfile contains multiple primary sources.") + + expect(err).to include("DEPRECATION: Your Gemfile contains multiple primary sources.") expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: file:#{gem_repo2}") should_be_installed("depends_on_rack 1.0.1", "rack 1.0.0") end end + + context "and only the dependency is pinned" do + before do + # need this to be broken to check for correct source ordering + build_repo gem_repo2 do + build_gem "rack", "1.0.0" do |s| + s.write "lib/rack.rb", "RACK = 'FAIL'" + end + end + + gemfile <<-G + source "file://#{gem_repo3}" # contains depends_on_rack + source "file://#{gem_repo2}" # contains broken rack + + gem "depends_on_rack" # installed from gem_repo3 + gem "rack", :source => "file://#{gem_repo1}" + G + end + + it "installs the dependency from the pinned source without warning" do + bundle :install + + expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.") + should_be_installed("depends_on_rack 1.0.1", "rack 1.0.0") + + # In https://github.com/bundler/bundler/issues/3585 this failed + # when there is already a lock file, and the gems are missing, so try again + system_gems [] + bundle :install + + expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.") + should_be_installed("depends_on_rack 1.0.1", "rack 1.0.0") + end + end end end @@ -319,4 +354,64 @@ describe "bundle install with gems on multiple sources" do should_be_installed("rack 1.0.0") end end + + context "when a single source contains multiple locked gems" do + before do + # 1. With these gems, + build_repo4 do + build_gem "foo", "0.1" + build_gem "bar", "0.1" + end + + # 2. Installing this gemfile will produce... + gemfile <<-G + source 'file://#{gem_repo1}' + gem 'rack' + gem 'foo', '~> 0.1', :source => 'file://#{gem_repo4}' + gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}' + G + + # 3. this lockfile. + lockfile <<-L + GEM + remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote1/ + remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote4/ + specs: + bar (0.1) + foo (0.1) + rack (1.0.0) + + PLATFORMS + ruby + + DEPENDENCIES + bar (~> 0.1)! + foo (~> 0.1)! + rack + L + + bundle "install --path ../gems/system" + + # 4. Then we add some new versions... + update_repo4 do + build_gem "foo", "0.2" + build_gem "bar", "0.3" + end + end + + it "allows them to be unlocked separately" do + # 5. and install this gemfile, updating only foo. + install_gemfile <<-G + source 'file://#{gem_repo1}' + gem 'rack' + gem 'foo', '~> 0.2', :source => 'file://#{gem_repo4}' + gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}' + G + + # 6. Which should update foo to 0.2, but not the (locked) bar 0.1 + should_be_installed("foo 0.2") + should_be_installed("bar 0.1") + end + end + end diff --git a/spec/install/gems/sudo_spec.rb b/spec/install/gems/sudo_spec.rb index 366432188b..3e833960e2 100644 --- a/spec/install/gems/sudo_spec.rb +++ b/spec/install/gems/sudo_spec.rb @@ -96,8 +96,11 @@ describe "when using sudo", :sudo => true do end describe "and BUNDLE_PATH is not writable" do - it "installs" do + before do sudo "chmod ugo-w #{default_bundle_path}" + end + + it "installs" do install_gemfile <<-G source "file://#{gem_repo1}" gem "rack", '1.0' @@ -106,6 +109,21 @@ describe "when using sudo", :sudo => true do expect(default_bundle_path("gems/rack-1.0.0")).to exist should_be_installed "rack 1.0" end + + it "cleans up the tmpdirs generated" do + require 'tmpdir' + Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir| + FileUtils.remove_entry_secure(tmpdir) + end + + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*") + + expect(tmpdirs).to be_empty + end end describe "and GEM_HOME is not writable" do diff --git a/spec/install/gemspecs_spec.rb b/spec/install/gemspecs_spec.rb index 201ba9564a..47ef073f2c 100644 --- a/spec/install/gemspecs_spec.rb +++ b/spec/install/gemspecs_spec.rb @@ -15,7 +15,7 @@ describe "bundle install" do gem "yaml_spec" G bundle :install - expect(err).to be_empty + expect(err).to lack_errors end it "still installs correctly when using path" do @@ -24,7 +24,7 @@ describe "bundle install" do install_gemfile <<-G gem 'yaml_spec', :path => "#{lib_path('yaml_spec-1.0')}" G - expect(err).to eq("") + expect(err).to lack_errors end end diff --git a/spec/install/parallel/spec_installation_spec.rb b/spec/install/parallel/spec_installation_spec.rb new file mode 100644 index 0000000000..97288fe02f --- /dev/null +++ b/spec/install/parallel/spec_installation_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +require 'bundler/installer/parallel_installer' + +describe ParallelInstaller::SpecInstallation do + describe "#ready_to_enqueue?" do + + let!(:dep) do + a_spec = Object.new + def a_spec.name + "I like tests" + end + a_spec + end + + context "when in enqueued state" do + it "is falsey" do + spec = ParallelInstaller::SpecInstallation.new(dep) + spec.state = :enqueued + expect(spec.ready_to_enqueue?).to be_falsey + end + end + + context "when in installed state" do + it "returns falsey" do + spec = ParallelInstaller::SpecInstallation.new(dep) + spec.state = :installed + expect(spec.ready_to_enqueue?).to be_falsey + end + end + + it "returns truthy" do + spec = ParallelInstaller::SpecInstallation.new(dep) + expect(spec.ready_to_enqueue?).to be_truthy + end + end + +end diff --git a/spec/install/post_bundle_message_spec.rb b/spec/install/post_bundle_message_spec.rb index 78133d9283..5bdf165062 100644 --- a/spec/install/post_bundle_message_spec.rb +++ b/spec/install/post_bundle_message_spec.rb @@ -81,6 +81,22 @@ describe "post bundle message" do expect(out).to include(bundle_complete_message) end end + + describe "with misspelled or non-existent gem name" do + before :each do + gemfile <<-G + source 'https://rubygems.org/' + gem "rails" + gem "misspelled-gem-name", :group => :development + G + end + + it "should report a helpufl error message" do + bundle :install + expect(out).to include("Fetching gem metadata from https://rubygems.org/") + expect(out).to include("Could not find gem 'misspelled-gem-name (>= 0) ruby' in any of the gem sources listed in your Gemfile or available on this machine.") + end + end end describe "for second bundle install run" do diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb index 4ae6baac44..618dafc69e 100644 --- a/spec/lock/lockfile_spec.rb +++ b/spec/lock/lockfile_spec.rb @@ -4,12 +4,255 @@ describe "the lockfile format" do include Bundler::GemHelpers it "generates a simple lockfile for a single source, gem" do + + install_gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + lockfile_should_be <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + #{Bundler::VERSION} + G + end + + it "updates the lockfile's bundler version if current ver. is newer" do + + # TODO: verno below should be one less than prev ver (unless at min) + + lockfile <<-L + GIT + remote: git://github.com/nex3/haml.git + revision: 8a2271f + specs: + + GEM + remote: file://#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + omg! + rack + + BUNDLED WITH + 1.8.2 + L + + install_gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + lockfile_should_be <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + #{Bundler::VERSION} + G + end + + it "updates the lockfile's bundler version if not present" do + + lockfile <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + L + + install_gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + lockfile_should_be <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + #{Bundler::VERSION} + G + end + + it "outputs a warning if the current is older than lockfile's bundler version" do + lockfile <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + 9999999.1.0 + L + + simulate_bundler_version "9999999.0.0" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + end + + expect(out).to include("Warning: the running version of Bundler is " \ + "older than the version that created the lockfile") + + lockfile_should_be <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + 9999999.1.0 + G + end + + it "errors if the current is a major version older than lockfile's bundler version" do + lockfile <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + 9999999.0.0 + L + install_gemfile <<-G source "file://#{gem_repo1}" gem "rack" G + expect(exitstatus > 0) if exitstatus + expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.") + end + + it "shows a friendly error when running with a new bundler 2 lockfile" do + lockfile <<-L + GEM + remote: https://rails-assets.org/ + specs: + rails-assets-bootstrap (3.3.4) + rails-assets-jquery (>= 1.9.1) + rails-assets-jquery (2.1.4) + + GEM + remote: https://rubygems.org/ + specs: + rake (10.4.2) + + PLATFORMS + ruby + + DEPENDENCIES + rails-assets-bootstrap! + rake + + BUNDLED WITH + 9999999.0.0 + L + + install_gemfile <<-G + source 'https://rubygems.org' + gem 'rake' + + source 'https://rails-assets.org' do + gem 'rails-assets-bootstrap' + end + G + + expect(exitstatus > 0) if exitstatus + expect(out).to include("You must use Bundler 9999999 or greater with this lockfile.") + end + + it "warns when updating bundler major version" do + lockfile <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic(Gem::Platform.local)} + + DEPENDENCIES + rack + + BUNDLED WITH + 1.10.0 + L + + simulate_bundler_version "9999999.0.0" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + end + + expect(out).to include("Warning: the lockfile is being updated to Bundler " \ + "9999999, after which you will be unable to return to Bundler 1.") + lockfile_should_be <<-G GEM remote: file:#{gem_repo1}/ @@ -21,6 +264,9 @@ describe "the lockfile format" do DEPENDENCIES rack + + BUNDLED WITH + 9999999.0.0 G end @@ -44,6 +290,9 @@ describe "the lockfile format" do DEPENDENCIES rack-obama + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -67,6 +316,9 @@ describe "the lockfile format" do DEPENDENCIES rack-obama (>= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -94,6 +346,9 @@ describe "the lockfile format" do DEPENDENCIES rack-obama (>= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -116,6 +371,9 @@ describe "the lockfile format" do DEPENDENCIES net-sftp + + BUNDLED WITH + #{Bundler::VERSION} G should_be_installed "net-sftp 1.1.1", "net-ssh 1.0.0" @@ -143,6 +401,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -176,6 +437,9 @@ describe "the lockfile format" do DEPENDENCIES omg! rack + + BUNDLED WITH + #{Bundler::VERSION} L bundle "install" @@ -206,6 +470,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -233,6 +500,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -260,6 +530,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -284,6 +557,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -323,6 +599,9 @@ describe "the lockfile format" do bar! foo! rack + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -355,6 +634,9 @@ describe "the lockfile format" do actionpack rack-obama thin + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -391,6 +673,9 @@ describe "the lockfile format" do DEPENDENCIES rails + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -414,6 +699,9 @@ describe "the lockfile format" do DEPENDENCIES double_deps + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -437,6 +725,9 @@ describe "the lockfile format" do DEPENDENCIES rack-obama (>= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -460,6 +751,9 @@ describe "the lockfile format" do DEPENDENCIES rack-obama (>= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -485,6 +779,9 @@ describe "the lockfile format" do DEPENDENCIES foo + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -510,6 +807,9 @@ describe "the lockfile format" do DEPENDENCIES foo + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -535,6 +835,9 @@ describe "the lockfile format" do DEPENDENCIES foo + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -559,6 +862,9 @@ describe "the lockfile format" do DEPENDENCIES foo! + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -574,6 +880,9 @@ describe "the lockfile format" do DEPENDENCIES rack + + BUNDLED WITH + #{Bundler::VERSION} G install_gemfile <<-G @@ -596,6 +905,9 @@ describe "the lockfile format" do DEPENDENCIES rack + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -622,6 +934,9 @@ describe "the lockfile format" do DEPENDENCIES platform_specific + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -650,6 +965,9 @@ describe "the lockfile format" do DEPENDENCIES activesupport rack + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -671,6 +989,9 @@ describe "the lockfile format" do DEPENDENCIES rack + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -692,6 +1013,9 @@ describe "the lockfile format" do DEPENDENCIES rack (= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -713,6 +1037,9 @@ describe "the lockfile format" do DEPENDENCIES rack (= 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -756,6 +1083,9 @@ describe "the lockfile format" do DEPENDENCIES rack (> 0.9, < 1.0) + + BUNDLED WITH + #{Bundler::VERSION} G end @@ -803,6 +1133,9 @@ describe "the lockfile format" do DEPENDENCIES omg! + + BUNDLED WITH + #{Bundler::VERSION} L FileUtils.rm_rf(bundled_app('vendor')) @@ -827,6 +1160,9 @@ describe "the lockfile format" do DEPENDENCIES omg! + + BUNDLED WITH + #{Bundler::VERSION} L end @@ -836,7 +1172,9 @@ describe "the lockfile format" do File.utime(time, time, bundled_app('Gemfile.lock')) end before(:each) do + build_repo2 + install_gemfile <<-G source "file://#{gem_repo2}" gem "rack" @@ -913,6 +1251,9 @@ describe "the lockfile format" do DEPENDENCIES rack + + BUNDLED WITH + #{Bundler::VERSION} L install_gemfile(<<-G, :expect_err => true) diff --git a/spec/other/bundle_ruby_spec.rb b/spec/other/bundle_ruby_spec.rb new file mode 100644 index 0000000000..d3ecbca430 --- /dev/null +++ b/spec/other/bundle_ruby_spec.rb @@ -0,0 +1,158 @@ +require "spec_helper" + +describe "bundle_ruby" do + context "when run" do + it "displays a deprecation warning" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3' + + gem "foo" + G + + bundle_ruby + + expect(err).to include("DEPRECATION: bundle_ruby is deprecated and will " \ + "be removed in Bundler 2.0.") + end + end + + context "without patchlevel" do + it "returns the ruby version" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3' + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.9.3") + end + + it "engine defaults to MRI" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.9.3" + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.9.3") + end + + it "handles jruby" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5' + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.8.7 (jruby 1.6.5)") + end + + it "handles rbx" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4' + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.8.7 (rbx 1.2.4)") + end + + it "raises an error if engine is used but engine version is not" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.8.7", :engine => 'rbx' + + gem "foo" + G + + bundle_ruby :exitstatus => true + expect(exitstatus).not_to eq(0) if exitstatus + + bundle_ruby + expect(out).to include("Please define :engine_version") + end + + it "raises an error if engine_version is used but engine is not" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.8.7", :engine_version => '1.2.4' + + gem "foo" + G + + bundle_ruby :exitstatus => true + expect(exitstatus).not_to eq(0) if exitstatus + + bundle_ruby + expect(out).to include("Please define :engine") + end + + it "raises an error if engine version doesn't match ruby version for MRI" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4' + + gem "foo" + G + + bundle_ruby :exitstatus => true + expect(exitstatus).not_to eq(0) if exitstatus + + bundle_ruby + expect(out).to include("ruby_version must match the :engine_version for MRI") + end + + it "should print if no ruby version is specified" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "foo" + G + + bundle_ruby + + expect(out).to include("No ruby version specified") + end + end + + context "when using patchlevel" do + it "returns the ruby version" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.9.3", :patchlevel => '429', :engine => 'ruby', :engine_version => '1.9.3' + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.9.3p429") + end + + it "handles an engine" do + gemfile <<-G + source "file://#{gem_repo1}" + ruby "1.9.3", :patchlevel => '392', :engine => 'jruby', :engine_version => '1.7.4' + + gem "foo" + G + + bundle_ruby + + expect(out).to include("ruby 1.9.3p392 (jruby 1.7.4)") + end + end +end diff --git a/spec/other/ext_spec.rb b/spec/other/ext_spec.rb index cbee163771..dca6c80a59 100644 --- a/spec/other/ext_spec.rb +++ b/spec/other/ext_spec.rb @@ -5,6 +5,14 @@ describe "Gem::Specification#match_platform" do darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10" expect(darwin.match_platform(pl('java'))).to eq(false) end + + context "when platform is a string" do + it "matches when platform is a string" do + lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32") + expect(lazy_spec.match_platform(pl('x86-mingw32'))).to eq(true) + expect(lazy_spec.match_platform(pl('x64-mingw32'))).to eq(true) + end + end end describe "Bundler::GemHelpers#generic" do @@ -49,12 +57,12 @@ describe "Gem::SourceIndex#refresh!" do end it "does not explode when called", :if => rubygems_1_7 do - run "Gem.source_index.refresh!" - run "Gem::SourceIndex.new([]).refresh!" + run "Gem.source_index.refresh!", :expect_err => true + run "Gem::SourceIndex.new([]).refresh!", :expect_err => true end it "does not explode when called", :unless => rubygems_1_7 do - run "Gem.source_index.refresh!" - run "Gem::SourceIndex.from_gems_in([]).refresh!" + run "Gem.source_index.refresh!", :expect_err => true + run "Gem::SourceIndex.from_gems_in([]).refresh!", :expect_err => true end end diff --git a/spec/other/platform_spec.rb b/spec/other/platform_spec.rb index b71446ff49..86216df2a7 100644 --- a/spec/other/platform_spec.rb +++ b/spec/other/platform_spec.rb @@ -1126,8 +1126,8 @@ G G bundle "outdated" - expect(out).to include("activesupport (3.0 > 2.3.5)") - expect(out).to include("foo (1.0") + expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5") + expect(out).to include("foo (newest 1.0") end it "returns list of outdated gems when the ruby version matches for any engine" do @@ -1146,8 +1146,8 @@ G G bundle "outdated" - expect(out).to include("activesupport (3.0 > 2.3.5)") - expect(out).to include("foo (1.0") + expect(out).to include("activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)") + expect(out).to include("foo (newest 1.0") end end diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb index 81fcddfb32..7654007ba9 100644 --- a/spec/quality_spec.rb +++ b/spec/quality_spec.rb @@ -97,7 +97,7 @@ describe "The library itself" do end end - expect(@err.split("\n").reject { |f| f =~ %r{lib/bundler/vendor} }).to eq([]) + expect(@err.split("\n").reject{|r| r =~ /^DEPRECATION/ }).to eq([]) expect(@out).to eq("") end end diff --git a/spec/realworld/edgecases_spec.rb b/spec/realworld/edgecases_spec.rb index 15c7697564..19d5bae578 100644 --- a/spec/realworld/edgecases_spec.rb +++ b/spec/realworld/edgecases_spec.rb @@ -7,7 +7,7 @@ describe "real world edgecases", :realworld => true do source :rubygems gem "linecache", "0.46" G - expect(err).to eq("") + expect(err).to lack_errors end # https://github.com/bundler/bundler/issues/1202 @@ -81,7 +81,7 @@ describe "real world edgecases", :realworld => true do bundle "install --path vendor/bundle", :expect_err => true expect(err).not_to include("Could not find rake") - expect(err).to be_empty + expect(err).to lack_errors end it "checks out git repos when the lockfile is corrupted" do diff --git a/spec/realworld/parallel_spec.rb b/spec/realworld/parallel_spec.rb index 67311a7e7c..409c146d9b 100644 --- a/spec/realworld/parallel_spec.rb +++ b/spec/realworld/parallel_spec.rb @@ -6,7 +6,7 @@ describe "parallel", :realworld => true do source "https://rubygems.org" gem 'activesupport', '~> 3.2.13' gem 'faker', '~> 1.1.2' - gem 'i18n', '~> 0.6.0' # Because 1.7+ requires Ruby 1.9.3+ + gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+ G bundle :install, :jobs => 4, :env => {"DEBUG" => "1"} @@ -38,7 +38,7 @@ describe "parallel", :realworld => true do source "https://rubygems.org" gem 'activesupport', '~> 3.2.12' gem 'faker', '~> 1.1.2' - gem 'i18n', '~> 0.6.0' # Because 1.7+ requires Ruby 1.9.3+ + gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+ G bundle :update, :jobs => 4, :env => {"DEBUG" => "1"} diff --git a/spec/resolver/platform_spec.rb b/spec/resolver/platform_spec.rb index 2a126e4836..149bc921fc 100644 --- a/spec/resolver/platform_spec.rb +++ b/spec/resolver/platform_spec.rb @@ -35,6 +35,7 @@ describe "Resolving platform craziness" do platforms "mingw32 mswin32 x64-mingw32" do |platform| gem "thin", "1.2.7", platform end + gem "win32-api", "1.5.1", "universal-mingw32" end end @@ -57,6 +58,18 @@ describe "Resolving platform craziness" do dep "thin" should_resolve_as %w(thin-1.2.7-x64-mingw32) end + + it "finds universal-mingw gems on x86-mingw" do + platform "x86-mingw32" + dep "win32-api" + should_resolve_as %w(win32-api-1.5.1-universal-mingw32) + end + + it "finds universal-mingw gems on x64-mingw" do + platform "x64-mingw32" + dep "win32-api" + should_resolve_as %w(win32-api-1.5.1-universal-mingw32) + end end describe "with conflicting cases" do diff --git a/spec/runtime/gem_tasks_spec.rb b/spec/runtime/gem_tasks_spec.rb new file mode 100644 index 0000000000..5124b05a3c --- /dev/null +++ b/spec/runtime/gem_tasks_spec.rb @@ -0,0 +1,35 @@ +require "spec_helper" + +describe "require 'bundler/gem_tasks'" do + before :each do + bundled_app("foo.gemspec").open("w") do |f| + f.write <<-GEMSPEC + Gem::Specification.new do |s| + s.name = "foo" + end + GEMSPEC + end + bundled_app("Rakefile").open("w") do |f| + f.write <<-RAKEFILE + $:.unshift("#{bundler_path}") + require "bundler/gem_tasks" + RAKEFILE + end + end + + it "includes the relevant tasks" do + with_gem_path_as(Spec::Path.base_system_gems.to_s) do + sys_exec "ruby -S rake -T" + end + + expect(err).to eq("") + expected_tasks = [ + "rake build", + "rake install", + "rake release", + ] + tasks = out.lines.to_a.map { |s| s.split('#').first.strip } + expect(tasks & expected_tasks).to eq(expected_tasks) + expect(exitstatus).to eq(0) if exitstatus + end +end diff --git a/spec/runtime/require_spec.rb b/spec/runtime/require_spec.rb index 5e5e9db22f..59c7a6850c 100644 --- a/spec/runtime/require_spec.rb +++ b/spec/runtime/require_spec.rb @@ -95,7 +95,7 @@ describe "Bundler.require" do Bundler.require R - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "doesn't swallow the error when the library has an unrelated error" do @@ -117,7 +117,7 @@ describe "Bundler.require" do RUBY run(cmd, :expect_err => true) - expect(err).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar") + expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar") end describe "with namespaced gems" do @@ -153,7 +153,7 @@ describe "Bundler.require" do RUBY ruby(cmd, :expect_err => true) - expect(err).to be_empty + expect(err).to lack_errors end it "does not mangle explictly given requires" do @@ -165,7 +165,7 @@ describe "Bundler.require" do load_error_run <<-R, 'jquery-rails' Bundler.require R - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "handles the case where regex fails" do @@ -187,7 +187,7 @@ describe "Bundler.require" do RUBY run(cmd, :expect_err => true) - expect(err).to eq("ZOMG LOAD ERROR") + expect(err).to eq_err("ZOMG LOAD ERROR") end it "doesn't swallow the error when the library has an unrelated error" do @@ -210,7 +210,7 @@ describe "Bundler.require" do RUBY run(cmd, :expect_err => true) - expect(err).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar") + expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar") end end @@ -316,7 +316,7 @@ describe "Bundler.require" do load_error_run <<-R, 'no_such_file_omg' Bundler.require R - expect(err).to eq('ZOMG LOAD ERROR') + expect(err).to eq_err('ZOMG LOAD ERROR') end end end @@ -335,7 +335,7 @@ describe "Bundler.require with platform specific dependencies" do G run "Bundler.require", :expect_err => true - expect(err).to be_empty + expect(err).to lack_errors end it "requires gems pinned to multiple platforms, including the current one" do @@ -350,6 +350,6 @@ describe "Bundler.require with platform specific dependencies" do run "Bundler.require; puts RACK", :expect_err => true expect(out).to eq("1.0.0") - expect(err).to be_empty + expect(err).to lack_errors end end diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index 934e3218dd..68640e1074 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -16,7 +16,7 @@ describe "Bundler.setup" do require 'rack' puts RACK RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to eq("1.0.0") end end @@ -42,7 +42,7 @@ describe "Bundler.setup" do puts "WIN" end RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to eq("WIN") end @@ -55,7 +55,7 @@ describe "Bundler.setup" do require 'rack' puts RACK RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to eq("1.0.0") end @@ -69,7 +69,7 @@ describe "Bundler.setup" do require 'rack' puts RACK RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to eq("1.0.0") end @@ -87,9 +87,24 @@ describe "Bundler.setup" do puts "FAIL" end RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to match("WIN") end + + it "handles multiple non-additive invocations" do + ruby <<-RUBY + require 'bundler' + Bundler.setup(:default, :test) + Bundler.setup(:default) + require 'rack' + + puts "FAIL" + RUBY + + expect(err).to match("rack") + expect(err).to match("LoadError") + expect(out).not_to match("FAIL") + end end it "raises if the Gemfile was not yet installed" do @@ -230,7 +245,7 @@ describe "Bundler.setup" do end R - expect(err).to be_empty + expect(err).to lack_errors end it "replaces #gem but raises when the version is wrong" do @@ -256,7 +271,7 @@ describe "Bundler.setup" do end R - expect(err).to be_empty + expect(err).to lack_errors end end @@ -572,7 +587,7 @@ describe "Bundler.setup" do end R - expect(err).to be_empty + expect(err).to lack_errors end end end @@ -607,7 +622,7 @@ describe "Bundler.setup" do ENV["GEM_HOME"] = "" bundle %{exec ruby -e "require 'set'"} - expect(err).to be_empty + expect(err).to lack_errors end it "should prepend gemspec require paths to $LOAD_PATH in order" do @@ -628,6 +643,39 @@ describe "Bundler.setup" do expect(out).to eq("yay") end + it "should clean $LOAD_PATH properly" do + gem_name = 'very_simple_binary' + full_gem_name = gem_name + '-1.0' + ext_dir = File.join(tmp "extenstions", full_gem_name) + + install_gem full_gem_name + + install_gemfile <<-G + source "file://#{gem_repo1}" + G + + ruby <<-R + if Gem::Specification.method_defined? :extension_dir + s = Gem::Specification.find_by_name '#{gem_name}' + s.extension_dir = '#{ext_dir}' + + # Don't build extensions. + s.class.send(:define_method, :build_extensions) { nil } + end + + require 'bundler' + gem '#{gem_name}' + + puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2 + + Bundler.setup + + puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} == 0 + R + + expect(out).to eq("true\ntrue") + end + it "stubs out Gem.refresh so it does not reveal system gems" do system_gems "rack-1.0.0" @@ -663,7 +711,7 @@ describe "Bundler.setup" do require 'foo' R end - expect(err).to eq("") + expect(err).to lack_errors end it "should make sure the Bundler.root is really included in the path relative to the Gemfile" do @@ -688,7 +736,7 @@ describe "Bundler.setup" do R end - expect(err).to eq("") + expect(err).to lack_errors end end @@ -834,7 +882,7 @@ describe "Bundler.setup" do Bundler.load RUBY - expect(err).to eq("") + expect(err).to lack_errors expect(out).to eq("") end end @@ -846,7 +894,7 @@ describe "Bundler.setup" do G bundle %|exec ruby -e "require 'bundler'; Bundler.setup"| - expect(err).to be_empty + expect(err).to lack_errors end end diff --git a/spec/support/builders.rb b/spec/support/builders.rb index 5490a2b976..1a1c916779 100644 --- a/spec/support/builders.rb +++ b/spec/support/builders.rb @@ -260,12 +260,15 @@ module Spec FileUtils.rm_rf Dir[gem_repo3("prerelease*")] end - # A repo that has no pre-installed gems included. (The caller completely determines the contents with the block) - def build_repo4(&blk) + # A repo that has no pre-installed gems included. (The caller completely + # determines the contents with the block.) + def build_repo4 FileUtils.rm_rf gem_repo4 - build_repo(gem_repo4) do - yield if block_given? - end + build_repo(gem_repo4) { yield } + end + + def update_repo4 + update_repo(gem_repo4) { yield } end def update_repo2 diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index e42c67be57..66d4a81ba6 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -1,5 +1,17 @@ module Spec module Matchers + RSpec::Matchers.define :lack_errors do + match do |actual| + actual.gsub(/(^DEPRECATION:.+)/, '') == '' + end + end + + RSpec::Matchers.define :eq_err do |expected| + match do |actual| + actual.gsub(/(^DEPRECATION:.+\n)/, '') == expected + end + end + RSpec::Matchers.define :have_dep do |*args| dep = Bundler::Dependency.new(*args) diff --git a/spec/update/gems_spec.rb b/spec/update/gems_spec.rb index 83928a5213..32ebbd45d0 100644 --- a/spec/update/gems_spec.rb +++ b/spec/update/gems_spec.rb @@ -99,6 +99,16 @@ describe "bundle update" do should_not_be_installed "rack 1.2" end end + + describe "in a frozen bundle" do + it "should fail loudly" do + bundle "install --deployment" + bundle "update" + + expect(out).to match(/You are trying to install in deployment mode after changing.your Gemfile/m) + expect(exitstatus).not_to eq(0) if exitstatus + end + end end describe "bundle update in more complicated situations" do @@ -155,6 +165,11 @@ describe "bundle update when a gem depends on a newer version of bundler" do G end + it "should not explode" do + bundle "update" + expect(err).to lack_errors + end + it "should explain that bundler conflicted" do bundle "update" expect(err).not_to match(/in snapshot/i) diff --git a/spec/update/git_spec.rb b/spec/update/git_spec.rb index 9f5e71b3e2..8e075be80c 100644 --- a/spec/update/git_spec.rb +++ b/spec/update/git_spec.rb @@ -89,7 +89,7 @@ describe "bundle update" do gem "foo", "1.0", :git => "#{lib_path('foo_two')}" G - expect(err).to be_empty + expect(err).to lack_errors expect(out).to include("Fetching #{lib_path}/foo_two") expect(out).to include("Bundle complete!") end |