From 1b333a6768ce8708c7d01bffc27a46952e5b6011 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Thu, 6 Apr 2017 19:25:25 -0500 Subject: [Git] Avoid loading full gemspecs in -rbundler/setup when stubs are available --- lib/bundler/source/git.rb | 8 ++++++++ lib/bundler/source/path.rb | 10 ++++++++-- lib/bundler/stub_specification.rb | 11 ++++++++++- spec/runtime/require_spec.rb | 24 ++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index c95c94583f..d92b741202 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -234,6 +234,7 @@ module Bundler # The gemspecs we cache should already be evaluated. spec = Bundler.load_gemspec(spec_path) next unless spec + Bundler.rubygems.set_installed_by_version(spec) Bundler.rubygems.validate(spec) File.open(spec_path, "wb") {|file| file.write(spec.to_ruby) } end @@ -302,6 +303,13 @@ module Bundler # no-op, since we validate when re-serializing the gemspec def validate_spec(_spec); end + + if defined?(::Gem::StubSpecification) + def load_gemspec(file) + stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent) + StubSpecification.from_stub(stub) + end + end end end end diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index 4b01496aac..0ad0a029f0 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -144,6 +144,12 @@ module Bundler SharedHelpers.in_bundle? && app_cache_path.exist? end + def load_gemspec(file) + return unless spec = Bundler.load_gemspec(file) + Bundler.rubygems.set_installed_by_version(spec) + spec + end + def validate_spec(spec) Bundler.rubygems.validate(spec) end @@ -154,9 +160,9 @@ module Bundler if File.directory?(expanded_path) # We sort depth-first since `<<` will override the earlier-found specs Dir["#{expanded_path}/#{@glob}"].sort_by {|p| -p.split(File::SEPARATOR).size }.each do |file| - next unless spec = Bundler.load_gemspec(file) + next unless spec = load_gemspec(file) spec.source = self - Bundler.rubygems.set_installed_by_version(spec) + # Validation causes extension_dir to be calculated, which depends # on #source, so we validate here instead of load_gemspec validate_spec(spec) diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb index 9e43f06364..2e8938e12d 100644 --- a/lib/bundler/stub_specification.rb +++ b/lib/bundler/stub_specification.rb @@ -36,8 +36,17 @@ module Bundler stub.default_gem end + # This is what we do in bundler/rubygems_ext def full_gem_path - stub.full_gem_path + # this cannot check source.is_a?(Bundler::Plugin::API::Source) + # because that _could_ trip the autoload, and if there are unresolved + # gems at that time, this method could be called inside another require, + # thus raising with that constant being undefined. Better to check a method + if source.respond_to?(:path) || (source.respond_to?(:bundler_plugin_api_source?) && source.bundler_plugin_api_source?) + Pathname.new(loaded_from).dirname.expand_path(source.root).to_s.untaint + else + rg_full_gem_path + end end def full_require_paths diff --git a/spec/runtime/require_spec.rb b/spec/runtime/require_spec.rb index 716080deb0..c07f6344fd 100644 --- a/spec/runtime/require_spec.rb +++ b/spec/runtime/require_spec.rb @@ -383,6 +383,30 @@ RSpec.describe "Bundler.require" do expect(out).to eq("WIN") end + + it "does not load git gemspecs that are used", :rubygems => ">= 2.3" do + build_git "foo" + + install_gemfile! <<-G + gem "foo", :git => "#{lib_path("foo-1.0")}" + G + + run! <<-R + path = Gem.loaded_specs["foo"].loaded_from + contents = File.read(path) + contents = contents.lines.insert(-2, "\n raise 'broken gemspec'\n").join + File.open(path, "w") do |f| + f.write contents + end + R + + run! <<-R + Bundler.require + puts "WIN" + R + + expect(out).to eq("WIN") + end end RSpec.describe "Bundler.require with platform specific dependencies" do -- cgit v1.2.1