From 0c6d6d8e9d1a136760f6dd7d1dff39546b169d85 Mon Sep 17 00:00:00 2001 From: Darragh Bailey Date: Mon, 1 Nov 2021 17:31:48 +0000 Subject: [PATCH] Improve Gem spec selection when resolving When computing the solution set, if a gem is already loaded, make sure to use the specification of the loaded one instead of the first available as otherwise there is a risk that when multiple matches are available the specification for the wrong version may be picked. When this happens an error message will be triggered that looks like can't activate json-2.3.0, already activated json-2.5.1 This can occur for distribution packaged vagrants as well as installs for development purposes where the ruby install may contain a default gem spec of an older version than is needed. Fixes: #12521 Fixes: vagrant-libvirt/vagrant-libvirt#1390 --- lib/vagrant/bundler.rb | 7 ++++++- test/unit/vagrant/bundler_test.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb index eb2caabb0..46ef69f35 100644 --- a/lib/vagrant/bundler.rb +++ b/lib/vagrant/bundler.rb @@ -258,7 +258,12 @@ module Vagrant if solution_file&.valid? @logger.debug("loading cached solution set") solution = solution_file.dependency_list.map do |dep| - spec = composed_set.find_all(dep).first + spec = composed_set.find_all(dep).select do |dep_spec| + next(true) unless Gem.loaded_specs.has_key?(dep_spec.name) + + Gem.loaded_specs[dep_spec.name].version.eql?(dep_spec.version) + end.first + if !spec @logger.warn("failed to locate specification for dependency - #{dep}") @logger.warn("invalidating solution file - #{solution_file}") diff --git a/test/unit/vagrant/bundler_test.rb b/test/unit/vagrant/bundler_test.rb index 69f425c66..0dc3227eb 100644 --- a/test/unit/vagrant/bundler_test.rb +++ b/test/unit/vagrant/bundler_test.rb @@ -644,6 +644,34 @@ describe Vagrant::Bundler do expect(Gem.sources.sources.first.uri.to_s).to eq(described_class.const_get(:HASHICORP_GEMSTORE)) end end + + context "multiple specs" do + let(:solution_file) { double('solution_file') } + let(:vagrant_set) { double('vagrant_set') } + + before do + allow(subject).to receive(:load_solution_file).and_return(solution_file) + allow(subject).to receive(:generate_vagrant_set).and_return(vagrant_set) + allow(solution_file).to receive(:valid?).and_return(true) + end + + it "should activate spec of deps already loaded" do + spec = Gem.loaded_specs.first + deps = [spec[0]] + specs = [spec[1].dup, spec[1].dup] + specs[0].version = Gem::Version::new('0.0.1') + # make sure haven't accidentally modified both + expect(specs[0].version).to_not eq(specs[1].version) + + expect(solution_file).to receive(:dependency_list).and_return(deps) + expect(vagrant_set).to receive(:find_all).and_return(specs) + expect(subject).to receive(:activate_solution) do |activate_specs| + expect(activate_specs.length()).to eq(1) + expect(activate_specs[0].full_spec()).to eq(specs[1]) + end + subject.init!([]) + end + end end describe "#install" do