From a51949933f58f8d6064019475bcf438701d63731 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Mon, 21 Nov 2016 22:02:57 -0800 Subject: [PATCH 1/2] Always apply builtin constraints within dependency restrictions Include detection of running context (within Bundler or not) and load the "buitin" gems based on that context. --- lib/vagrant/bundler.rb | 70 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb index bca00e947..8d8c6cc97 100644 --- a/lib/vagrant/bundler.rb +++ b/lib/vagrant/bundler.rb @@ -52,7 +52,6 @@ module Vagrant begin # Compose set for resolution composed_set = generate_vagrant_set - @logger.debug("Composed local RubyGems set for plugin init resolution: #{composed_set}") # Resolve the request set to ensure proper activation order solution = request_set.resolve(composed_set) rescue Gem::UnsatisfiableDependencyError => failure @@ -128,6 +127,7 @@ module Vagrant # Clean removes any unused gems. def clean(plugins) + @logger.debug("Cleaning Vagrant plugins of stale gems.") # Generate dependencies for all registered plugins plugin_deps = plugins.map do |name, info| gem_version = info['installed_gem_version'] @@ -136,6 +136,8 @@ module Vagrant Gem::Dependency.new(name, gem_version) end + @logger.debug("Current plugin dependency list: #{plugin_deps}") + # Load dependencies into a request set for resolution request_set = Gem::RequestSet.new(*plugin_deps) # Never allow dependencies to be remotely satisfied during cleaning @@ -151,6 +153,8 @@ module Vagrant Gem::Specification.load(spec_path) end + @logger.debug("Generating current plugin state solution set.") + # Resolve the request set to ensure proper activation order solution = request_set.resolve(current_set) solution_specs = solution.map(&:full_spec) @@ -228,20 +232,31 @@ module Vagrant request_set = Gem::RequestSet.new(*plugin_deps) # Generate all existing deps within the "vagrant system" - existing_deps = Gem::Specification.find_all{true}.map do |item| + existing_deps = vagrant_internal_specs.map do |item| + @logger.debug("Activating builtin specification: #{item.full_name}") Gem::Dependency.new(item.name, item.version) end + @logger.debug("Required constraints from builtin gems: #{existing_deps}") + # Import constraints into the request set to prevent installing # gems that are incompatible with the core system request_set.import(existing_deps) - installer_set = Gem::Resolver.compose_sets(installer_set, generate_plugin_set(skips)) + installer_set = Gem::Resolver.compose_sets( + installer_set, + generate_builtin_set, + generate_plugin_set(skips) + ) + + @logger.debug("Generating solution set for installation.") # Generate the required solution set for new plugins solution = request_set.resolve(installer_set) activate_solution(solution) + @logger.debug("Installing required gems.") + # Install all remote gems into plugin path. Set the installer to ignore dependencies # as we know the dependencies are satisfied and it will attempt to validate a gem's # dependencies are satisified by gems in the install directory (which will likely not @@ -256,9 +271,32 @@ module Vagrant Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set) end + # @return [Array<[Gem::Specification, String]>] spec and directory pairs + def vagrant_internal_specs + list = {} + directories = [Gem::Specification.default_specifications_dir] + Gem::Specification.find_all{true}.each do |spec| + list[spec.full_name] = spec + end + directories += Gem::Specification.dirs.find_all do |path| + !path.start_with?(Gem.user_dir) + end + Gem::Specification.each_spec(directories) do |spec| + if !list[spec.full_name] + list[spec.full_name] = spec + end + end + list.values + end + # Generate the builtin resolver set def generate_builtin_set - Gem::Resolver::CurrentSet.new + builtin_set = BuiltinSet.new + @logger.debug("Generating new builtin set instance.") + vagrant_internal_specs.each do |spec| + builtin_set.add_builtin_spec(spec) + end + builtin_set end # Generate the plugin resolver set. Optionally provide specification names (short or @@ -312,6 +350,30 @@ module Vagrant end end + # This is a custom Gem::Resolver::Set for use with vagrant "system" gems. It + # allows the installed set of gems to be used for providing a solution while + # enforcing strict constraints. This ensures that plugins cannot "upgrade" + # gems that are builtin to vagrant itself. + class BuiltinSet < Gem::Resolver::Set + def initialize + super + @remote = false + @specs = [] + end + + def add_builtin_spec(spec) + @specs.push(spec).uniq! + end + + def find_all(req) + @specs.select do |spec| + req.match?(spec) + end.map do |spec| + Gem::Resolver::InstalledSpecification.new(self, spec) + end + end + end + # This is a custom Gem::Resolver::Set for use with Vagrant plugins. It is # a modified Gem::Resolver::VendorSet that supports multiple versions of # a specific gem From 109510c05046ecb0c1d66d69df0b79111597e5bd Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Tue, 22 Nov 2016 07:56:52 -0800 Subject: [PATCH 2/2] Update travis configuration --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index b708c2c24..e998a970a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,18 +4,14 @@ sudo: false cache: bundler -before_install: - - gem uninstall bundler -aIxq --force - - gem uninstall -Ixq --force -i /home/travis/.rvm/gems/ruby-2.2.3@global bundler - - gem install bundler -v '1.12.5' - addons: apt: packages: - bsdtar rvm: - - 2.2.3 + - 2.2.5 + - 2.3.3 branches: only: