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.
This commit is contained in:
Chris Roberts 2016-11-21 22:02:57 -08:00
parent 53b9f1747c
commit a51949933f
1 changed files with 66 additions and 4 deletions

View File

@ -52,7 +52,6 @@ module Vagrant
begin begin
# Compose set for resolution # Compose set for resolution
composed_set = generate_vagrant_set 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 # Resolve the request set to ensure proper activation order
solution = request_set.resolve(composed_set) solution = request_set.resolve(composed_set)
rescue Gem::UnsatisfiableDependencyError => failure rescue Gem::UnsatisfiableDependencyError => failure
@ -128,6 +127,7 @@ module Vagrant
# Clean removes any unused gems. # Clean removes any unused gems.
def clean(plugins) def clean(plugins)
@logger.debug("Cleaning Vagrant plugins of stale gems.")
# Generate dependencies for all registered plugins # Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info| plugin_deps = plugins.map do |name, info|
gem_version = info['installed_gem_version'] gem_version = info['installed_gem_version']
@ -136,6 +136,8 @@ module Vagrant
Gem::Dependency.new(name, gem_version) Gem::Dependency.new(name, gem_version)
end end
@logger.debug("Current plugin dependency list: #{plugin_deps}")
# Load dependencies into a request set for resolution # Load dependencies into a request set for resolution
request_set = Gem::RequestSet.new(*plugin_deps) request_set = Gem::RequestSet.new(*plugin_deps)
# Never allow dependencies to be remotely satisfied during cleaning # Never allow dependencies to be remotely satisfied during cleaning
@ -151,6 +153,8 @@ module Vagrant
Gem::Specification.load(spec_path) Gem::Specification.load(spec_path)
end end
@logger.debug("Generating current plugin state solution set.")
# Resolve the request set to ensure proper activation order # Resolve the request set to ensure proper activation order
solution = request_set.resolve(current_set) solution = request_set.resolve(current_set)
solution_specs = solution.map(&:full_spec) solution_specs = solution.map(&:full_spec)
@ -228,20 +232,31 @@ module Vagrant
request_set = Gem::RequestSet.new(*plugin_deps) request_set = Gem::RequestSet.new(*plugin_deps)
# Generate all existing deps within the "vagrant system" # 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) Gem::Dependency.new(item.name, item.version)
end end
@logger.debug("Required constraints from builtin gems: #{existing_deps}")
# Import constraints into the request set to prevent installing # Import constraints into the request set to prevent installing
# gems that are incompatible with the core system # gems that are incompatible with the core system
request_set.import(existing_deps) 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 # Generate the required solution set for new plugins
solution = request_set.resolve(installer_set) solution = request_set.resolve(installer_set)
activate_solution(solution) activate_solution(solution)
@logger.debug("Installing required gems.")
# Install all remote gems into plugin path. Set the installer to ignore dependencies # 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 # 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 # 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) Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set)
end 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 # Generate the builtin resolver set
def generate_builtin_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 end
# Generate the plugin resolver set. Optionally provide specification names (short or # Generate the plugin resolver set. Optionally provide specification names (short or
@ -312,6 +350,30 @@ module Vagrant
end end
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 # 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 modified Gem::Resolver::VendorSet that supports multiple versions of
# a specific gem # a specific gem