bundler.rb (vagrant-2.2.13) | : | bundler.rb (vagrant-2.2.14) | ||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
module Vagrant | module Vagrant | |||
# This class manages Vagrant's interaction with Bundler. Vagrant uses | # This class manages Vagrant's interaction with Bundler. Vagrant uses | |||
# Bundler as a way to properly resolve all dependencies of Vagrant and | # Bundler as a way to properly resolve all dependencies of Vagrant and | |||
# all Vagrant-installed plugins. | # all Vagrant-installed plugins. | |||
class Bundler | class Bundler | |||
class SolutionFile | class SolutionFile | |||
# @return [Pathname] path to plugin file | # @return [Pathname] path to plugin file | |||
attr_reader :plugin_file | attr_reader :plugin_file | |||
# @return [Pathname] path to solution file | # @return [Pathname] path to solution file | |||
attr_reader :solution_file | attr_reader :solution_file | |||
# @return [Array<Gem::Dependency>] list of required dependencies | # @return [Array<Gem::Resolver::DependencyRequest>] list of required depen dencies | |||
attr_reader :dependency_list | attr_reader :dependency_list | |||
# @param [Pathname] plugin_file Path to plugin file | # @param [Pathname] plugin_file Path to plugin file | |||
# @param [Pathname] solution_file Custom path to solution file | # @param [Pathname] solution_file Custom path to solution file | |||
def initialize(plugin_file:, solution_file: nil) | def initialize(plugin_file:, solution_file: nil) | |||
@logger = Log4r::Logger.new("vagrant::bundler::signature_file") | @logger = Log4r::Logger.new("vagrant::bundler::solution_file") | |||
@plugin_file = Pathname.new(plugin_file.to_s) | @plugin_file = Pathname.new(plugin_file.to_s) | |||
if solution_file | if solution_file | |||
@solution_file = Pathname.new(solution_file.to_s) | @solution_file = Pathname.new(solution_file.to_s) | |||
else | else | |||
@solution_file = Pathname.new(@plugin_file.to_s + ".sol") | @solution_file = Pathname.new(@plugin_file.to_s + ".sol") | |||
end | end | |||
@valid = false | @valid = false | |||
@dependency_list = [].freeze | @dependency_list = [].freeze | |||
@logger.debug("new solution file instance plugin_file=#{plugin_file} " \ | @logger.debug("new solution file instance plugin_file=#{plugin_file} " \ | |||
"solution_file=#{solution_file}") | "solution_file=#{solution_file}") | |||
load | load | |||
end | end | |||
# Set the list of dependencies for this solution | # Set the list of dependencies for this solution | |||
# | # | |||
# @param [Array<Gem::Dependency>] dependency_list List of dependencies for the solution | # @param [Array<Gem::Dependency>] dependency_list List of dependencies for the solution | |||
# @return [Array<Gem::Resolver::DependencyRequest>] | ||||
def dependency_list=(dependency_list) | def dependency_list=(dependency_list) | |||
Array(dependency_list).each do |d| | Array(dependency_list).each do |d| | |||
if !d.is_a?(Gem::Dependency) | if !d.is_a?(Gem::Dependency) | |||
raise TypeError, "Expected `Gem::Dependency` but received `#{d.class }`" | raise TypeError, "Expected `Gem::Dependency` but received `#{d.class }`" | |||
end | end | |||
end | end | |||
@dependency_list = dependency_list.map(&:freeze).freeze | @dependency_list = dependency_list.map do |d| | |||
Gem::Resolver::DependencyRequest.new(d, nil).freeze | ||||
end.freeze | ||||
end | end | |||
# @return [Boolean] contained solution is valid | # @return [Boolean] contained solution is valid | |||
def valid? | def valid? | |||
@valid | @valid | |||
end | end | |||
# @return [FalseClass] invalidate this solution file | # @return [FalseClass] invalidate this solution file | |||
def invalidate! | def invalidate! | |||
@logger.debug("manually invalidating solution file") | ||||
@valid = false | @valid = false | |||
@logger.debug("manually invalidating solution file #{self}") | ||||
@valid | ||||
end | end | |||
# Delete the solution file | # Delete the solution file | |||
# | # | |||
# @return [Boolean] true if file was deleted | # @return [Boolean] true if file was deleted | |||
def delete! | def delete! | |||
if !solution_file.exist? | if !solution_file.exist? | |||
@logger.debug("solution file does not exist. nothing to delete.") | @logger.debug("solution file does not exist. nothing to delete.") | |||
return false | return false | |||
end | end | |||
skipping to change at line 95 | skipping to change at line 99 | |||
@logger.debug("plugin file does not exist, not storing solution") | @logger.debug("plugin file does not exist, not storing solution") | |||
return | return | |||
end | end | |||
if !solution_file.dirname.exist? | if !solution_file.dirname.exist? | |||
@logger.debug("creating directory for solution file: #{solution_file.d irname}") | @logger.debug("creating directory for solution file: #{solution_file.d irname}") | |||
solution_file.dirname.mkpath | solution_file.dirname.mkpath | |||
end | end | |||
@logger.debug("writing solution file contents to disk") | @logger.debug("writing solution file contents to disk") | |||
solution_file.write({ | solution_file.write({ | |||
dependencies: dependency_list.map { |d| | dependencies: dependency_list.map { |d| | |||
[d.name, d.requirements_list] | [d.dependency.name, d.dependency.requirements_list] | |||
}, | }, | |||
checksum: plugin_file_checksum, | checksum: plugin_file_checksum, | |||
vagrant_version: Vagrant::VERSION | vagrant_version: Vagrant::VERSION | |||
}.to_json) | }.to_json) | |||
@valid = true | @valid = true | |||
end | end | |||
def to_s # :nodoc: | def to_s # :nodoc: | |||
"<Vagrant::Bundler::SolutionFile:#{plugin_file}:" \ | "<Vagrant::Bundler::SolutionFile:#{plugin_file}:" \ | |||
"#{solution_file}:#{valid? ? "valid" : "invalid"}>" | "#{solution_file}:#{valid? ? "valid" : "invalid"}>" | |||
skipping to change at line 124 | skipping to change at line 128 | |||
if !plugin_file.exist? || !solution_file.exist? | if !plugin_file.exist? || !solution_file.exist? | |||
@logger.debug("missing file so skipping loading") | @logger.debug("missing file so skipping loading") | |||
return | return | |||
end | end | |||
solution = read_solution || return | solution = read_solution || return | |||
return if !valid_solution?( | return if !valid_solution?( | |||
checksum: solution[:checksum], | checksum: solution[:checksum], | |||
version: solution[:vagrant_version] | version: solution[:vagrant_version] | |||
) | ) | |||
@logger.debug("loading solution dependency list") | @logger.debug("loading solution dependency list") | |||
@dependency_list = solution[:dependencies].map do |name, requirements| | @dependency_list = Array(solution[:dependencies]).map do |name, requirem | |||
Gem::Dependency.new(name, requirements) | ents| | |||
end | gd = Gem::Dependency.new(name, requirements) | |||
Gem::Resolver::DependencyRequest.new(gd, nil).freeze | ||||
end.freeze | ||||
@logger.debug("solution dependency list: #{dependency_list}") | @logger.debug("solution dependency list: #{dependency_list}") | |||
@valid = true | @valid = true | |||
end | end | |||
# Validate the given checksum matches the plugin file | # Validate the given checksum matches the plugin file | |||
# checksum | # checksum | |||
# | # | |||
# @param [String] checksum Checksum value to validate | # @param [String] checksum Checksum value to validate | |||
# @return [Boolean] | # @return [Boolean] | |||
def valid_solution?(checksum:, version:) | def valid_solution?(checksum:, version:) | |||
skipping to change at line 161 | skipping to change at line 166 | |||
# Read contents of solution file and parse | # Read contents of solution file and parse | |||
# | # | |||
# @return [Hash] | # @return [Hash] | |||
def read_solution | def read_solution | |||
@logger.debug("reading solution file - #{solution_file}") | @logger.debug("reading solution file - #{solution_file}") | |||
begin | begin | |||
hash = JSON.load(solution_file.read) | hash = JSON.load(solution_file.read) | |||
Vagrant::Util::HashWithIndifferentAccess.new(hash) | Vagrant::Util::HashWithIndifferentAccess.new(hash) | |||
rescue => err | rescue => err | |||
@logger.warn("failed to load solution file, ignoring (error: #{err})") | @logger.warn("failed to load solution file, ignoring (error: #{err})") | |||
nil | ||||
end | end | |||
end | end | |||
end | end | |||
# Location of HashiCorp gem repository | # Location of HashiCorp gem repository | |||
HASHICORP_GEMSTORE = "https://gems.hashicorp.com/".freeze | HASHICORP_GEMSTORE = "https://gems.hashicorp.com/".freeze | |||
# Default gem repositories | # Default gem repositories | |||
DEFAULT_GEM_SOURCES = [ | DEFAULT_GEM_SOURCES = [ | |||
HASHICORP_GEMSTORE, | HASHICORP_GEMSTORE, | |||
skipping to change at line 237 | skipping to change at line 243 | |||
# Initializes Bundler and the various gem paths so that we can begin | # Initializes Bundler and the various gem paths so that we can begin | |||
# loading gems. | # loading gems. | |||
def init!(plugins, repair=false, **opts) | def init!(plugins, repair=false, **opts) | |||
if !@initial_specifications | if !@initial_specifications | |||
@initial_specifications = Gem::Specification.find_all{true} | @initial_specifications = Gem::Specification.find_all{true} | |||
else | else | |||
Gem::Specification.all = @initial_specifications | Gem::Specification.all = @initial_specifications | |||
Gem::Specification.reset | Gem::Specification.reset | |||
end | end | |||
enable_prerelease!(specs: @initial_specifications) | ||||
solution_file = load_solution_file(opts) | solution_file = load_solution_file(opts) | |||
@logger.debug("solution file in use for init: #{solution_file}") | @logger.debug("solution file in use for init: #{solution_file}") | |||
solution = nil | solution = nil | |||
composed_set = generate_vagrant_set | composed_set = generate_vagrant_set | |||
# Force the composed set to allow prereleases | ||||
if Vagrant.allow_prerelease_dependencies? | ||||
@logger.debug("enabling prerelease dependency matching due to user reque | ||||
st") | ||||
composed_set.prerelease = true | ||||
end | ||||
if solution_file&.valid? | if solution_file&.valid? | |||
@logger.debug("loading cached solution set") | @logger.debug("loading cached solution set") | |||
solution = solution_file.dependency_list.map do |dep| | solution = solution_file.dependency_list.map do |dep| | |||
spec = composed_set.find_all(dep).first | spec = composed_set.find_all(dep).first | |||
if !spec | if !spec | |||
@logger.warn("failed to locate specification for dependency - #{dep} ") | @logger.warn("failed to locate specification for dependency - #{dep} ") | |||
@logger.warn("invalidating solution file - #{solution_file}") | @logger.warn("invalidating solution file - #{solution_file}") | |||
solution_file.invalidate! | solution_file.invalidate! | |||
break | break | |||
end | end | |||
skipping to change at line 469 | skipping to change at line 479 | |||
@verbose = true | @verbose = true | |||
yield | yield | |||
@verbose = initial_state | @verbose = initial_state | |||
else | else | |||
@verbose = true | @verbose = true | |||
end | end | |||
end | end | |||
protected | protected | |||
# This will enable prerelease if any of the root dependency constraints | ||||
# include prerelease versions | ||||
# | ||||
# @param [Array<Gem::Specification>] spec_list List of specifications | ||||
# @param [Gem::RequestSet] rs Request set of dependencies | ||||
def enable_prerelease!(specs: nil, request_set: nil) | ||||
pre = specs.detect do |spec| | ||||
spec.version.prerelease? | ||||
end if specs | ||||
if pre | ||||
@logger.debug("Enabling prerelease plugin resolution due to dependency: | ||||
#{pre.full_name}") | ||||
ENV["VAGRANT_ALLOW_PRERELEASE"] = "1" | ||||
return | ||||
end | ||||
dep = request_set.dependencies.detect do |d| | ||||
d.prerelease? | ||||
end if request_set | ||||
if dep | ||||
@logger.debug("Enabling prerelease plugin resolution due to dependency: | ||||
#{dep}") | ||||
ENV["VAGRANT_ALLOW_PRERELEASE"] = "1" | ||||
return | ||||
end | ||||
end | ||||
def internal_install(plugins, update, **extra) | def internal_install(plugins, update, **extra) | |||
update = {} if !update.is_a?(Hash) | update = {} if !update.is_a?(Hash) | |||
skips = [] | skips = [] | |||
source_list = {} | source_list = {} | |||
system_plugins = plugins.map do |plugin_name, plugin_info| | system_plugins = plugins.map do |plugin_name, plugin_info| | |||
plugin_name if plugin_info["system"] | plugin_name if plugin_info["system"] | |||
end.compact | end.compact | |||
installer_set = VagrantSet.new(:both) | installer_set = VagrantSet.new(:both) | |||
installer_set.system_plugins = system_plugins | installer_set.system_plugins = system_plugins | |||
skipping to change at line 579 | skipping to change at line 565 | |||
validate_configured_sources! | validate_configured_sources! | |||
source_list.values.each{|srcs| srcs.delete_if{|src| default_sources.includ e?(src)}} | source_list.values.each{|srcs| srcs.delete_if{|src| default_sources.includ e?(src)}} | |||
installer_set.prefer_sources = source_list | installer_set.prefer_sources = source_list | |||
@logger.debug("Current source list for install: #{Gem.sources.to_a}") | @logger.debug("Current source list for install: #{Gem.sources.to_a}") | |||
# Create the request set for the new plugins | # Create the request set for the new plugins | |||
request_set = Gem::RequestSet.new(*plugin_deps) | request_set = Gem::RequestSet.new(*plugin_deps) | |||
enable_prerelease!(request_set: request_set) | ||||
request_set.prerelease = Vagrant.prerelease? || | ||||
Vagrant.allow_prerelease_dependencies? | ||||
installer_set = Gem::Resolver.compose_sets( | installer_set = Gem::Resolver.compose_sets( | |||
installer_set, | installer_set, | |||
generate_builtin_set(system_plugins), | generate_builtin_set(system_plugins), | |||
generate_plugin_set(skips) | generate_plugin_set(skips) | |||
) | ) | |||
if Vagrant.allow_prerelease_dependencies? | ||||
@logger.debug("enabling prerelease dependency matching based on user req | ||||
uest") | ||||
request_set.prerelease = true | ||||
installer_set.prerelease = true | ||||
end | ||||
@logger.debug("Generating solution set for installation.") | @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) | |||
# Remove gems which are already installed | # Remove gems which are already installed | |||
request_set.sorted_requests.delete_if do |act_req| | request_set.sorted_requests.delete_if do |act_req| | |||
rs = act_req.spec | rs = act_req.spec | |||
skipping to change at line 842 | skipping to change at line 832 | |||
super | super | |||
@remote = false | @remote = false | |||
@specs = [] | @specs = [] | |||
end | end | |||
def add_builtin_spec(spec) | def add_builtin_spec(spec) | |||
@specs.push(spec).uniq! | @specs.push(spec).uniq! | |||
end | end | |||
def find_all(req) | def find_all(req) | |||
@specs.select do |spec| | r = @specs.select do |spec| | |||
allow_prerelease = Vagrant.allow_prerelease_dependencies? || | # When matching requests against builtin specs, we _always_ enable | |||
(spec.name == "vagrant" && Vagrant.prerelease?) | # prerelease matching since any prerelease that's found in this | |||
req.match?(spec, allow_prerelease) | # set has been added explicitly and should be available for all | |||
# plugins to resolve against. This includes Vagrant itself since | ||||
# it is considered a prerelease when in development mode | ||||
req.match?(spec, true) | ||||
end.map do |spec| | end.map do |spec| | |||
Gem::Resolver::InstalledSpecification.new(self, spec) | Gem::Resolver::InstalledSpecification.new(self, spec) | |||
end | end | |||
# If any of the results are a prerelease, we need to mark the request | ||||
# to allow prereleases so the solution can be properly fulfilled | ||||
if r.any? { |x| x.version.prerelease? } | ||||
req.dependency.prerelease = true | ||||
end | ||||
r | ||||
end | 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 | |||
class PluginSet < Gem::Resolver::VendorSet | class PluginSet < Gem::Resolver::VendorSet | |||
## | ## | |||
# Adds a specification to the set with the given +name+ which has been | # Adds a specification to the set with the given +name+ which has been | |||
# unpacked into the given +directory+. | # unpacked into the given +directory+. | |||
skipping to change at line 882 | skipping to change at line 881 | |||
@directories[spec] = directory | @directories[spec] = directory | |||
spec | spec | |||
end | end | |||
## | ## | |||
# Returns an Array of VendorSpecification objects matching the | # Returns an Array of VendorSpecification objects matching the | |||
# DependencyRequest +req+. | # DependencyRequest +req+. | |||
def find_all(req) | def find_all(req) | |||
@specs.values.flatten.select do |spec| | @specs.values.flatten.select do |spec| | |||
req.match?(spec) | req.match?(spec, prerelease) | |||
end.map do |spec| | end.map do |spec| | |||
source = Gem::Source::Vendor.new(@directories[spec]) | source = Gem::Source::Vendor.new(@directories[spec]) | |||
Gem::Resolver::VendorSpecification.new(self, spec, source) | Gem::Resolver::VendorSpecification.new(self, spec, source) | |||
end | end | |||
end | end | |||
## | ## | |||
# Loads a spec with the given +name+. +version+, +platform+ and +source+ a re | # Loads a spec with the given +name+. +version+, +platform+ and +source+ a re | |||
# ignored. | # ignored. | |||
def load_spec(name, version, platform, source) | def load_spec(name, version, platform, source) | |||
End of changes. 17 change blocks. | ||||
44 lines changed or deleted | 44 lines changed or added |