Merge pull request #7793 from chrisroberts/enhancement/bundler-less
[core] Remove bundler usage for plugin management
This commit is contained in:
commit
2d6071a55b
76
bin/vagrant
76
bin/vagrant
|
@ -20,43 +20,23 @@ if argv.include?("-v") || argv.include?("--version")
|
|||
exit 0
|
||||
end
|
||||
|
||||
# This is kind of hacky, and I'd love to find a better way to do this, but
|
||||
# if we're accessing the plugin interface, we want to NOT load plugins
|
||||
# for this run, because they can actually interfere with the function
|
||||
# of the plugin interface.
|
||||
# Disable plugin loading for commands where plugins are not required
|
||||
argv.each_index do |i|
|
||||
arg = argv[i]
|
||||
|
||||
if !arg.start_with?("-")
|
||||
if arg == "plugin"
|
||||
ENV["VAGRANT_NO_PLUGINS"] = "1"
|
||||
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
|
||||
if ["plugin", "help"].include?(arg) || (arg == "box" && argv[i+1] == "list")
|
||||
ENV['VAGRANT_NO_PLUGINS'] = "1"
|
||||
end
|
||||
|
||||
if arg == "help"
|
||||
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
|
||||
end
|
||||
|
||||
if arg == "box" && argv[i+1] == "list"
|
||||
ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}"
|
||||
if arg == "plugin" && ["repair", "expunge"].include?(argv[i+1])
|
||||
ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1"
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# First, make sure that we're executing using the proper Bundler context
|
||||
# with our plugins. If we're not, then load that and reload Vagrant.
|
||||
if !ENV["VAGRANT_INTERNAL_BUNDLERIZED"]
|
||||
require "rbconfig"
|
||||
ruby_path = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
|
||||
Kernel.exec(
|
||||
ruby_path,
|
||||
File.expand_path("../../lib/vagrant/pre-rubygems.rb", __FILE__),
|
||||
*ARGV)
|
||||
raise "Fatal error: this line should never be reached"
|
||||
end
|
||||
|
||||
# Set logging level to `debug`. This is done before loading 'vagrant', as it
|
||||
# sets up the logging system.
|
||||
if argv.include?("--debug")
|
||||
|
@ -64,52 +44,6 @@ if argv.include?("--debug")
|
|||
ENV["VAGRANT_LOG"] = "debug"
|
||||
end
|
||||
|
||||
# Setup our dependencies by initializing Bundler. If we're using plugins,
|
||||
# then also initialize the paths to the plugins.
|
||||
begin
|
||||
require "bundler"
|
||||
rescue Errno::EINVAL
|
||||
# Bundler can generated the EINVAL error during initial require, which means
|
||||
# nothing has yet been setup (so no access to I18n). Note that vagrant has
|
||||
# failed early and copy information related to problem and possible solution.
|
||||
$stderr.puts "Vagrant failed to initialize at a very early stage:\n\n"
|
||||
$stderr.puts "Vagrant received an \"EINVAL\" error while attempting to set some"
|
||||
$stderr.puts "environment variables. This is usually caused by the total size of your"
|
||||
$stderr.puts "environment variables being too large. Vagrant sets a handful of"
|
||||
$stderr.puts "environment variables to function and requires this to work. Please"
|
||||
$stderr.puts "delete some environment variables prior to executing Vagrant to"
|
||||
$stderr.puts "fix this."
|
||||
exit 255
|
||||
end
|
||||
|
||||
begin
|
||||
$vagrant_bundler_runtime = Bundler.setup(:default, :plugins)
|
||||
rescue Bundler::GemNotFound
|
||||
$stderr.puts "Bundler, the underlying system used to manage Vagrant plugins,"
|
||||
$stderr.puts "is reporting that a plugin or its dependency can't be found."
|
||||
$stderr.puts "This is usually caused by manual tampering with the 'plugins.json'"
|
||||
$stderr.puts "file in the Vagrant home directory. To fix this error, please"
|
||||
$stderr.puts "remove that file and reinstall all your plugins using `vagrant"
|
||||
$stderr.puts "plugin install`."
|
||||
rescue Bundler::VersionConflict => e
|
||||
$stderr.puts "Vagrant experienced a version conflict with some installed plugins!"
|
||||
$stderr.puts "This usually happens if you recently upgraded Vagrant. As part of the"
|
||||
$stderr.puts "upgrade process, some existing plugins are no longer compatible with"
|
||||
$stderr.puts "this version of Vagrant. The recommended way to fix this is to remove"
|
||||
$stderr.puts "your existing plugins and reinstall them one-by-one. To remove all"
|
||||
$stderr.puts "plugins:"
|
||||
$stderr.puts ""
|
||||
$stderr.puts " rm -r ~/.vagrant.d/plugins.json ~/.vagrant.d/gems"
|
||||
$stderr.puts ""
|
||||
$stderr.puts "Note if you have an alternate VAGRANT_HOME environmental variable"
|
||||
$stderr.puts "set, the folders above will be in that directory rather than your"
|
||||
$stderr.puts "user's home directory."
|
||||
$stderr.puts ""
|
||||
$stderr.puts "The error message is shown below:\n\n"
|
||||
$stderr.puts e.message
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Stdout/stderr should not buffer output
|
||||
$stdout.sync = true
|
||||
$stderr.sync = true
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
require "vagrant/shared_helpers"
|
||||
|
||||
if Vagrant.plugins_enabled? && !defined?(Bundler)
|
||||
puts "It appears that Vagrant was not properly loaded. Specifically,"
|
||||
puts "the bundler context Vagrant requires was not setup. Please execute"
|
||||
puts "vagrant using only the `vagrant` executable."
|
||||
abort
|
||||
end
|
||||
|
||||
require 'rubygems'
|
||||
require 'log4r'
|
||||
|
||||
|
@ -72,11 +65,6 @@ global_logger.info("RubyGems version: #{Gem::VERSION}")
|
|||
ENV.each do |k, v|
|
||||
global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/
|
||||
end
|
||||
global_logger.info("Plugins:")
|
||||
Bundler.definition.specs_for([:plugins]).each do |spec|
|
||||
global_logger.info(" - #{spec.name} = #{spec.version}")
|
||||
end
|
||||
|
||||
|
||||
# We need these components always so instead of an autoload we
|
||||
# just require them explicitly here.
|
||||
|
@ -254,6 +242,38 @@ if I18n.config.respond_to?(:enforce_available_locales=)
|
|||
I18n.config.enforce_available_locales = true
|
||||
end
|
||||
|
||||
# Setup the plugin manager and load any defined plugins
|
||||
require_relative "vagrant/plugin/manager"
|
||||
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||
|
||||
global_logger.info("Plugins:")
|
||||
plugins.each do |plugin_name, plugin_info|
|
||||
global_logger.info(" - #{plugin_name} = #{plugin_info["installed_gem_version"]}")
|
||||
end
|
||||
|
||||
if Vagrant.plugins_init?
|
||||
begin
|
||||
Vagrant::Bundler.instance.init!(plugins)
|
||||
rescue Gem::ConflictError, Gem::DependencyError => e
|
||||
$stderr.puts "Vagrant experienced a version conflict with some installed plugins!"
|
||||
$stderr.puts "This usually happens if you recently upgraded Vagrant. As part of the"
|
||||
$stderr.puts "upgrade process, some existing plugins are no longer compatible with"
|
||||
$stderr.puts "this version of Vagrant. The recommended way to fix this is to remove"
|
||||
$stderr.puts "your existing plugins and reinstall them one-by-one. To remove all"
|
||||
$stderr.puts "plugins:"
|
||||
$stderr.puts ""
|
||||
$stderr.puts " vagrant expunge"
|
||||
$stderr.puts ""
|
||||
$stderr.puts "Note if you have an alternate VAGRANT_HOME environmental variable"
|
||||
$stderr.puts "set, the folders above will be in that directory rather than your"
|
||||
$stderr.puts "user's home directory."
|
||||
$stderr.puts ""
|
||||
$stderr.puts "The error message is shown below:\n\n"
|
||||
$stderr.puts e.message
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# A lambda that knows how to load plugins from a single directory.
|
||||
plugin_load_proc = lambda do |directory|
|
||||
# We only care about directories
|
||||
|
|
|
@ -4,7 +4,8 @@ require "set"
|
|||
require "tempfile"
|
||||
require "fileutils"
|
||||
|
||||
require "bundler"
|
||||
require "rubygems/package"
|
||||
require "rubygems/uninstaller"
|
||||
|
||||
require_relative "shared_helpers"
|
||||
require_relative "version"
|
||||
|
@ -15,83 +16,115 @@ module Vagrant
|
|||
# Bundler as a way to properly resolve all dependencies of Vagrant and
|
||||
# all Vagrant-installed plugins.
|
||||
class Bundler
|
||||
|
||||
HASHICORP_GEMSTORE = 'https://gems.hashicorp.com'.freeze
|
||||
|
||||
def self.instance
|
||||
@bundler ||= self.new
|
||||
end
|
||||
|
||||
attr_reader :plugin_gem_path
|
||||
|
||||
def initialize
|
||||
@enabled = true if ENV["VAGRANT_INSTALLER_ENV"] ||
|
||||
ENV["VAGRANT_FORCE_BUNDLER"]
|
||||
@enabled = !::Bundler::SharedHelpers.in_bundle? if !@enabled
|
||||
@monitor = Monitor.new
|
||||
|
||||
@gem_home = ENV["GEM_HOME"]
|
||||
@gem_path = ENV["GEM_PATH"]
|
||||
|
||||
# Set the Bundler UI to be a silent UI. We have to add the
|
||||
# `silence` method to it because Bundler UI doesn't have it.
|
||||
::Bundler.ui =
|
||||
if ::Bundler::UI.const_defined? :Silent
|
||||
# bundler >= 1.6.0, we use our custom UI
|
||||
BundlerUI.new
|
||||
else
|
||||
# bundler < 1.6.0
|
||||
::Bundler::UI.new
|
||||
end
|
||||
if !::Bundler.ui.respond_to?(:silence)
|
||||
ui = ::Bundler.ui
|
||||
def ui.silence(*args)
|
||||
yield
|
||||
end
|
||||
end
|
||||
@plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
|
||||
end
|
||||
|
||||
# Initializes Bundler and the various gem paths so that we can begin
|
||||
# loading gems. This must only be called once.
|
||||
def init!(plugins)
|
||||
# If we're not enabled, then we don't do anything.
|
||||
return if !@enabled
|
||||
def init!(plugins, repair=false)
|
||||
# Add HashiCorp RubyGems source
|
||||
Gem.sources << HASHICORP_GEMSTORE
|
||||
|
||||
bundle_path = Vagrant.user_data_path.join("gems")
|
||||
|
||||
# Setup the "local" Bundler configuration. We need to set BUNDLE_PATH
|
||||
# because the existence of this actually suppresses `sudo`.
|
||||
@appconfigpath = Dir.mktmpdir("vagrant-bundle-app-config")
|
||||
File.open(File.join(@appconfigpath, "config"), "w+") do |f|
|
||||
f.write("BUNDLE_PATH: \"#{bundle_path}\"")
|
||||
# Generate dependencies for all registered plugins
|
||||
plugin_deps = plugins.map do |name, info|
|
||||
Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'])
|
||||
end
|
||||
|
||||
# Setup the Bundler configuration
|
||||
@configfile = tempfile("vagrant-configfile")
|
||||
@configfile.close
|
||||
# Load dependencies into a request set for resolution
|
||||
request_set = Gem::RequestSet.new(*plugin_deps)
|
||||
# Never allow dependencies to be remotely satisfied during init
|
||||
request_set.remote = false
|
||||
|
||||
# Build up the Gemfile for our Bundler context. We make sure to
|
||||
# lock Vagrant to our current Vagrant version. In addition to that,
|
||||
# we add all our plugin dependencies.
|
||||
@gemfile = build_gemfile(plugins)
|
||||
# Sets that we can resolve our dependencies from
|
||||
current_set = Gem::Resolver::CurrentSet.new
|
||||
plugin_set = Gem::Resolver::VendorSet.new
|
||||
repair_result = nil
|
||||
begin
|
||||
# Register all known plugin specifications to the plugin set
|
||||
Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).each do |spec_path|
|
||||
spec = Gem::Specification.load(spec_path)
|
||||
desired_spec_path = File.join(spec.gem_dir, "#{spec.name}.gemspec")
|
||||
# Vendor set requires the spec to be within the gem directory. Some gems will package their
|
||||
# spec file, and that's not what we want to load.
|
||||
if !File.exist?(desired_spec_path) || !FileUtils.cmp(spec.spec_file, desired_spec_path)
|
||||
File.write(desired_spec_path, spec.to_ruby)
|
||||
end
|
||||
plugin_set.add_vendor_gem(spec.name, spec.gem_dir)
|
||||
end
|
||||
|
||||
Util::SafeEnv.change_env do |env|
|
||||
# Set the environmental variables for Bundler
|
||||
env["BUNDLE_APP_CONFIG"] = @appconfigpath
|
||||
env["BUNDLE_CONFIG"] = @configfile.path
|
||||
env["BUNDLE_GEMFILE"] = @gemfile.path
|
||||
env["BUNDLE_RETRY"] = "3"
|
||||
env["GEM_PATH"] =
|
||||
"#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
|
||||
# Compose set for resolution
|
||||
composed_set = Gem::Resolver.compose_sets(current_set, plugin_set)
|
||||
|
||||
# Resolve the request set to ensure proper activation order
|
||||
solution = request_set.resolve(composed_set)
|
||||
rescue Gem::UnsatisfiableDependencyError => failure
|
||||
if repair
|
||||
raise failure if @init_retried
|
||||
install(plugins)
|
||||
@init_retried = true
|
||||
retry
|
||||
else
|
||||
$stderr.puts "Vagrant failed to properly initialize due to an error while"
|
||||
$stderr.puts "while attempting to load configured plugins. This can be caused"
|
||||
$stderr.puts "by manually tampering with the 'plugins.json' file, or by a"
|
||||
$stderr.puts "recent Vagrant upgrade. To fix this problem, please run:\n\n"
|
||||
$stderr.puts " vagrant plugin repair\n\n"
|
||||
$stderr.puts "The error message is shown below:\n\n"
|
||||
$stderr.puts failure.message
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
Gem.clear_paths
|
||||
# Activate the gems
|
||||
begin
|
||||
retried = false
|
||||
solution.each do |activation_request|
|
||||
unless activation_request.full_spec.activated?
|
||||
activation_request.full_spec.activate
|
||||
if(defined?(::Bundler))
|
||||
::Bundler.rubygems.mark_loaded activation_request.full_spec
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Gem::LoadError
|
||||
# Depending on the version of Ruby, the ordering of the solution set
|
||||
# will be either 0..n (molinillo) or n..0 (pre-molinillo). Instead of
|
||||
# attempting to determine what's in use, or if it has some how changed
|
||||
# again, just reverse order on failure and attempt again.
|
||||
if retried
|
||||
raise
|
||||
else
|
||||
retried = true
|
||||
solution.reverse!
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
full_vagrant_spec_list = Gem::Specification.find_all{true} +
|
||||
solution.map(&:full_spec)
|
||||
|
||||
if(defined?(::Bundler))
|
||||
::Bundler.rubygems.replace_entrypoints(full_vagrant_spec_list)
|
||||
end
|
||||
|
||||
Gem.post_reset do
|
||||
Gem::Specification.all = full_vagrant_spec_list
|
||||
end
|
||||
end
|
||||
|
||||
# Removes any temporary files created by init
|
||||
def deinit
|
||||
# If we weren't enabled, then we don't do anything.
|
||||
return if !@enabled
|
||||
|
||||
FileUtils.rm_rf(ENV["BUNDLE_APP_CONFIG"]) rescue nil
|
||||
FileUtils.rm_f(ENV["BUNDLE_CONFIG"]) rescue nil
|
||||
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]) rescue nil
|
||||
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]+".lock") rescue nil
|
||||
# no-op
|
||||
end
|
||||
|
||||
# Installs the list of plugins.
|
||||
|
@ -107,34 +140,19 @@ module Vagrant
|
|||
# @param [String] path Path to a local gem file.
|
||||
# @return [Gem::Specification]
|
||||
def install_local(path)
|
||||
# We have to do this load here because this file can be loaded
|
||||
# before RubyGems is actually loaded.
|
||||
require "rubygems/dependency_installer"
|
||||
begin
|
||||
require "rubygems/format"
|
||||
rescue LoadError
|
||||
# rubygems 2.x
|
||||
end
|
||||
|
||||
# If we're installing from a gem file, determine the name
|
||||
# based on the spec in the file.
|
||||
pkg = if defined?(Gem::Format)
|
||||
# RubyGems 1.x
|
||||
Gem::Format.from_file_by_path(path)
|
||||
else
|
||||
# RubyGems 2.x
|
||||
Gem::Package.new(path)
|
||||
end
|
||||
|
||||
# Install the gem manually. If the gem exists locally, then
|
||||
# Bundler shouldn't attempt to get it remotely.
|
||||
with_isolated_gem do
|
||||
installer = Gem::DependencyInstaller.new(
|
||||
document: [], prerelease: false)
|
||||
installer.install(path, "= #{pkg.spec.version}")
|
||||
end
|
||||
|
||||
pkg.spec
|
||||
installer = Gem::Installer.at(path,
|
||||
ignore_dependencies: true,
|
||||
install_dir: plugin_gem_path.to_s
|
||||
)
|
||||
installer.install
|
||||
new_spec = installer.spec
|
||||
plugin_info = {
|
||||
new_spec.name => {
|
||||
'gem_version' => new_spec.version.to_s
|
||||
}
|
||||
}
|
||||
internal_install(plugin_info, {})
|
||||
new_spec
|
||||
end
|
||||
|
||||
# Update updates the given plugins, or every plugin if none is given.
|
||||
|
@ -144,278 +162,114 @@ module Vagrant
|
|||
# empty or nil, all plugins will be updated.
|
||||
def update(plugins, specific)
|
||||
specific ||= []
|
||||
update = true
|
||||
update = { gems: specific } if !specific.empty?
|
||||
internal_install(plugins, update)
|
||||
end
|
||||
|
||||
# Clean removes any unused gems.
|
||||
def clean(plugins)
|
||||
gemfile = build_gemfile(plugins)
|
||||
lockfile = "#{gemfile.path}.lock"
|
||||
definition = ::Bundler::Definition.build(gemfile, lockfile, nil)
|
||||
root = File.dirname(gemfile.path)
|
||||
# Generate dependencies for all registered plugins
|
||||
plugin_deps = plugins.map do |name, info|
|
||||
Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'])
|
||||
end
|
||||
|
||||
with_isolated_gem do
|
||||
runtime = ::Bundler::Runtime.new(root, definition)
|
||||
runtime.clean
|
||||
# Load dependencies into a request set for resolution
|
||||
request_set = Gem::RequestSet.new(*plugin_deps)
|
||||
# Never allow dependencies to be remotely satisfied during cleaning
|
||||
request_set.remote = false
|
||||
|
||||
# Sets that we can resolve our dependencies from. Note that we only
|
||||
# resolve from the current set as all required deps are activated during
|
||||
# init.
|
||||
current_set = Gem::Resolver::CurrentSet.new
|
||||
|
||||
# Collect all plugin specifications
|
||||
plugin_specs = Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).map do |spec_path|
|
||||
Gem::Specification.load(spec_path)
|
||||
end
|
||||
|
||||
# Resolve the request set to ensure proper activation order
|
||||
solution = request_set.resolve(current_set)
|
||||
solution_specs = solution.map(&:full_spec)
|
||||
|
||||
# Find all specs installed to plugins directory that are not
|
||||
# found within the solution set
|
||||
plugin_specs.delete_if do |spec|
|
||||
solution.include?(spec)
|
||||
end
|
||||
|
||||
# Now delete all unused specs
|
||||
plugin_specs.each do |spec|
|
||||
Gem::Uninstaller.new(spec.name,
|
||||
version: spec.version,
|
||||
install_dir: plugin_gem_path,
|
||||
ignore: true
|
||||
).uninstall_gem(spec)
|
||||
end
|
||||
|
||||
solution.find_all do |spec|
|
||||
plugins.keys.include?(spec.name)
|
||||
end
|
||||
end
|
||||
|
||||
# During the duration of the yielded block, Bundler loud output
|
||||
# is enabled.
|
||||
def verbose
|
||||
@monitor.synchronize do
|
||||
begin
|
||||
old_ui = ::Bundler.ui
|
||||
require 'bundler/vendored_thor'
|
||||
::Bundler.ui = ::Bundler::UI::Shell.new
|
||||
yield
|
||||
ensure
|
||||
::Bundler.ui = old_ui
|
||||
end
|
||||
if block_given?
|
||||
initial_state = @verbose
|
||||
@verbose = true
|
||||
yield
|
||||
@verbose = initial_state
|
||||
else
|
||||
@verbose = true
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Builds a valid Gemfile for use with Bundler given the list of
|
||||
# plugins.
|
||||
#
|
||||
# @return [Tempfile]
|
||||
def build_gemfile(plugins)
|
||||
sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq
|
||||
|
||||
f = tempfile("vagrant-gemfile")
|
||||
f.tap do |gemfile|
|
||||
sources.each do |source|
|
||||
next if source == ""
|
||||
gemfile.puts(%Q[source "#{source}"])
|
||||
end
|
||||
|
||||
gemfile.puts(%Q[gem "vagrant", "= #{VERSION}"])
|
||||
|
||||
gemfile.puts("group :plugins do")
|
||||
plugins.each do |name, plugin|
|
||||
version = plugin["gem_version"]
|
||||
version = nil if version == ""
|
||||
|
||||
opts = {}
|
||||
if plugin["require"] && plugin["require"] != ""
|
||||
opts[:require] = plugin["require"]
|
||||
end
|
||||
|
||||
gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}])
|
||||
end
|
||||
gemfile.puts("end")
|
||||
gemfile.close
|
||||
end
|
||||
end
|
||||
|
||||
# This installs a set of plugins and optionally updates those gems.
|
||||
#
|
||||
# @param [Hash] plugins
|
||||
# @param [Hash, Boolean] update If true, updates all plugins, otherwise
|
||||
# can be a hash of options. See Bundler.definition.
|
||||
# @return [Array<Gem::Specification>]
|
||||
def internal_install(plugins, update, **extra)
|
||||
gemfile = build_gemfile(plugins)
|
||||
lockfile = "#{gemfile.path}.lock"
|
||||
definition = ::Bundler::Definition.build(gemfile, lockfile, update)
|
||||
root = File.dirname(gemfile.path)
|
||||
opts = {}
|
||||
opts["local"] = true if extra[:local]
|
||||
|
||||
with_isolated_gem do
|
||||
::Bundler::Installer.install(root, definition, opts)
|
||||
update = {} unless update.is_a?(Hash)
|
||||
|
||||
# Generate all required plugin deps
|
||||
plugin_deps = plugins.map do |name, info|
|
||||
if update == true || (update[:gems].respond_to?(:include?) && update[:gems].include?(name))
|
||||
gem_version = '> 0'
|
||||
else
|
||||
gem_version = info['gem_version'].to_s.empty? ? '> 0' : info['gem_version']
|
||||
end
|
||||
Gem::Dependency.new(name, gem_version)
|
||||
end
|
||||
|
||||
# TODO(mitchellh): clean gems here... for some reason when I put
|
||||
# it in on install, we get a GemNotFound exception. Gotta investigate.
|
||||
# Create the request set for the new plugins
|
||||
request_set = Gem::RequestSet.new(*plugin_deps)
|
||||
|
||||
definition.specs
|
||||
rescue ::Bundler::VersionConflict => e
|
||||
raise Errors::PluginInstallVersionConflict,
|
||||
conflicts: e.to_s.gsub("Bundler", "Vagrant")
|
||||
rescue ::Bundler::BundlerError => e
|
||||
if !::Bundler.ui.is_a?(BundlerUI)
|
||||
raise
|
||||
# Generate all existing deps within the "vagrant system"
|
||||
existing_deps = Gem::Specification.find_all{true}.map do |item|
|
||||
Gem::Dependency.new(item.name, item.version)
|
||||
end
|
||||
|
||||
# Add the warn/error level output from Bundler if we have any
|
||||
message = "#{e.message}"
|
||||
if ::Bundler.ui.output != ""
|
||||
message += "\n\n#{::Bundler.ui.output}"
|
||||
# Import constraints into the request set to prevent installing
|
||||
# gems that are incompatible with the core system
|
||||
request_set.import(existing_deps)
|
||||
|
||||
# Generate the required solution set for new plugins
|
||||
solution = request_set.resolve(Gem::Resolver::InstallerSet.new(:both))
|
||||
|
||||
# If any items in the solution set are local but not activated, turn them on
|
||||
solution.each do |activation_request|
|
||||
if activation_request.installed? && !activation_request.full_spec.activated?
|
||||
activation_request.full_spec.activate
|
||||
end
|
||||
end
|
||||
|
||||
raise ::Bundler::BundlerError, message
|
||||
# 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
|
||||
# be true)
|
||||
result = request_set.install_into(plugin_gem_path.to_s, true, ignore_dependencies: true)
|
||||
result.map(&:full_spec)
|
||||
end
|
||||
|
||||
def with_isolated_gem
|
||||
raise Errors::BundlerDisabled if !@enabled
|
||||
|
||||
tmp_gemfile = tempfile("vagrant-gemfile")
|
||||
tmp_gemfile.close
|
||||
|
||||
# Remove bundler settings so that Bundler isn't loaded when building
|
||||
# native extensions because it causes all sorts of problems.
|
||||
old_rubyopt = ENV["RUBYOPT"]
|
||||
old_gemfile = ENV["BUNDLE_GEMFILE"]
|
||||
ENV["BUNDLE_GEMFILE"] = tmp_gemfile.path
|
||||
ENV["RUBYOPT"] = (ENV["RUBYOPT"] || "").gsub(/-rbundler\/setup\s*/, "")
|
||||
|
||||
# Set the GEM_HOME so gems are installed only to our local gem dir
|
||||
ENV["GEM_HOME"] = Vagrant.user_data_path.join("gems").to_s
|
||||
|
||||
# Clear paths so that it reads the new GEM_HOME setting
|
||||
Gem.paths = ENV
|
||||
|
||||
# Reset the all specs override that Bundler does
|
||||
old_all = Gem::Specification._all
|
||||
|
||||
# WARNING: Seriously don't touch this without reading the comment attached
|
||||
# to the monkey-patch at the bottom of this file.
|
||||
Gem::Specification.vagrant_reset!
|
||||
|
||||
# /etc/gemrc and so on.
|
||||
old_config = nil
|
||||
begin
|
||||
old_config = Gem.configuration
|
||||
rescue Psych::SyntaxError
|
||||
# Just ignore this. This means that the ".gemrc" file has
|
||||
# an invalid syntax and can't be loaded. We don't care, because
|
||||
# when we set Gem.configuration to nil later, it'll force a reload
|
||||
# if it is needed.
|
||||
end
|
||||
Gem.configuration = NilGemConfig.new
|
||||
|
||||
# Use a silent UI so that we have no output
|
||||
Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do
|
||||
return yield
|
||||
end
|
||||
ensure
|
||||
tmp_gemfile.unlink rescue nil
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] = old_gemfile
|
||||
ENV["GEM_HOME"] = @gem_home
|
||||
ENV["RUBYOPT"] = old_rubyopt
|
||||
|
||||
Gem.configuration = old_config
|
||||
Gem.paths = ENV
|
||||
Gem::Specification.all = old_all
|
||||
end
|
||||
|
||||
# This method returns a proper "tempfile" on disk. Ruby's Tempfile class
|
||||
# would work really great for this, except GC can come along and remove
|
||||
# the file before we are done with it. This is because we "close" the file,
|
||||
# but we might be shelling out to a subprocess.
|
||||
#
|
||||
# @return [File]
|
||||
def tempfile(name)
|
||||
path = Dir::Tmpname.create(name) {}
|
||||
return File.open(path, "w+")
|
||||
end
|
||||
|
||||
# This is pretty hacky but it is a custom implementation of
|
||||
# Gem::ConfigFile so that we don't load any gemrc files.
|
||||
class NilGemConfig < Gem::ConfigFile
|
||||
def initialize
|
||||
# We _can not_ `super` here because that can really mess up
|
||||
# some other configuration state. We need to just set everything
|
||||
# directly.
|
||||
|
||||
@api_keys = {}
|
||||
@args = []
|
||||
@backtrace = false
|
||||
@bulk_threshold = 1000
|
||||
@hash = {}
|
||||
@update_sources = true
|
||||
@verbose = true
|
||||
end
|
||||
end
|
||||
|
||||
# This monkey patches Gem::Specification from RubyGems to add a new method,
|
||||
# `vagrant_reset!`. For some background, Vagrant needs to set the value
|
||||
# of these variables to nil to force new specs to be loaded. Previously,
|
||||
# this was accomplished by setting Gem::Specification.specs = nil. However,
|
||||
# newer versions of Rubygems try to map across that nil using a group_by
|
||||
# clause, breaking things.
|
||||
#
|
||||
# This generally never affected Vagrant users who were using the official
|
||||
# Vagrant installers because we lock to an older version of Rubygems that
|
||||
# does not have this issue. The users of the official debian packages,
|
||||
# however, experienced this issue because they float on Rubygems.
|
||||
#
|
||||
# In GH-7073, a number of Debian users reported this issue, but it was not
|
||||
# reproducible in the official installer for reasons described above. Commit
|
||||
# ba77d4b switched to using Gem::Specification.reset, but this actually
|
||||
# broke the ability to install gems locally (GH-7493) because it resets
|
||||
# the complete local cache, which is already built.
|
||||
#
|
||||
# The only solution that works with both new and old versions of Rubygems
|
||||
# is to provide our own function for JUST resetting all the stubs. Both
|
||||
# @@all and @@stubs must be set to a falsey value, so some of the
|
||||
# originally-suggested solutions of using an empty array do not work. Only
|
||||
# setting these values to nil (without clearing the cache), allows Vagrant
|
||||
# to install and manage plugins.
|
||||
class Gem::Specification < Gem::BasicSpecification
|
||||
def self.vagrant_reset!
|
||||
@@all = @@stubs = nil
|
||||
end
|
||||
end
|
||||
|
||||
if ::Bundler::UI.const_defined? :Silent
|
||||
class BundlerUI < ::Bundler::UI::Silent
|
||||
attr_reader :output
|
||||
|
||||
def initialize
|
||||
@output = ""
|
||||
end
|
||||
|
||||
def info(message, newline = nil)
|
||||
end
|
||||
|
||||
def confirm(message, newline = nil)
|
||||
end
|
||||
|
||||
def warn(message, newline = nil)
|
||||
@output += message
|
||||
@output += "\n" if newline
|
||||
end
|
||||
|
||||
def error(message, newline = nil)
|
||||
@output += message
|
||||
@output += "\n" if newline
|
||||
end
|
||||
|
||||
def debug(message, newline = nil)
|
||||
end
|
||||
|
||||
def debug?
|
||||
false
|
||||
end
|
||||
|
||||
def quiet?
|
||||
false
|
||||
end
|
||||
|
||||
def ask(message)
|
||||
end
|
||||
|
||||
def level=(name)
|
||||
end
|
||||
|
||||
def level(name = nil)
|
||||
"info"
|
||||
end
|
||||
|
||||
def trace(message, newline = nil)
|
||||
end
|
||||
|
||||
def silence
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -78,12 +78,13 @@ module Vagrant
|
|||
version: opts[:version],
|
||||
require: opts[:require],
|
||||
sources: opts[:sources],
|
||||
installed_gem_version: result.version
|
||||
)
|
||||
|
||||
result
|
||||
rescue ::Bundler::GemNotFound
|
||||
rescue Gem::GemNotFoundException
|
||||
raise Errors::PluginGemNotFound, name: name
|
||||
rescue ::Bundler::BundlerError => e
|
||||
rescue Gem::Exception => e
|
||||
raise Errors::BundlerError, message: e.to_s
|
||||
end
|
||||
|
||||
|
@ -102,14 +103,14 @@ module Vagrant
|
|||
|
||||
# Clean the environment, removing any old plugins
|
||||
Vagrant::Bundler.instance.clean(installed_plugins)
|
||||
rescue ::Bundler::BundlerError => e
|
||||
rescue Gem::Exception => e
|
||||
raise Errors::BundlerError, message: e.to_s
|
||||
end
|
||||
|
||||
# Updates all or a specific set of plugins.
|
||||
def update_plugins(specific)
|
||||
Vagrant::Bundler.instance.update(installed_plugins, specific)
|
||||
rescue ::Bundler::BundlerError => e
|
||||
rescue Gem::Exception => e
|
||||
raise Errors::BundlerError, message: e.to_s
|
||||
end
|
||||
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
# This file is to be loaded _before_ any RubyGems are loaded. This file
|
||||
# initializes the Bundler context so that Vagrant and its associated plugins
|
||||
# can load properly, and then execs out into Vagrant again.
|
||||
|
||||
require_relative "shared_helpers"
|
||||
|
||||
if defined?(Bundler)
|
||||
require "bundler/shared_helpers"
|
||||
if Bundler::SharedHelpers.in_bundle? && !Vagrant.very_quiet?
|
||||
puts "Vagrant appears to be running in a Bundler environment. Your "
|
||||
puts "existing Gemfile will be used. Vagrant will not auto-load any plugins"
|
||||
puts "installed with `vagrant plugin`. Vagrant will autoload any plugins in"
|
||||
puts "the 'plugins' group in your Gemfile. You can force Vagrant to take over"
|
||||
puts "with VAGRANT_FORCE_BUNDLER."
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "bundler"
|
||||
require_relative "plugin/manager"
|
||||
|
||||
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||
Vagrant::Bundler.instance.init!(plugins)
|
||||
|
||||
ENV["VAGRANT_INTERNAL_BUNDLERIZED"] = "1"
|
||||
|
||||
# If the VAGRANT_EXECUTABLE env is set, then we use that to point to a
|
||||
# Ruby file to directly execute. Otherwise, we just depend on PATH lookup.
|
||||
# This minor optimization can save hundreds of milliseconds on Windows.
|
||||
if ENV["VAGRANT_EXECUTABLE"]
|
||||
Kernel.exec("ruby", ENV["VAGRANT_EXECUTABLE"], *ARGV)
|
||||
else
|
||||
Kernel.exec("vagrant", *ARGV)
|
||||
end
|
|
@ -38,6 +38,13 @@ module Vagrant
|
|||
ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]
|
||||
end
|
||||
|
||||
# Should the plugin system be initialized
|
||||
#
|
||||
# @return [Boolean]
|
||||
def self.plugins_init?
|
||||
!ENV['VAGRANT_DISABLE_PLUGIN_INIT']
|
||||
end
|
||||
|
||||
# This returns whether or not 3rd party plugins should and can be loaded.
|
||||
#
|
||||
# @return [Boolean]
|
||||
|
|
|
@ -5,7 +5,9 @@ module Vagrant
|
|||
class Env
|
||||
def self.with_original_env
|
||||
original_env = ENV.to_hash
|
||||
ENV.replace(::Bundler::ORIGINAL_ENV) if defined?(::Bundler::ORIGINAL_ENV)
|
||||
if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV)
|
||||
ENV.replace(::Bundler::ORIGINAL_ENV)
|
||||
end
|
||||
ENV.update(Vagrant.original_env)
|
||||
yield
|
||||
ensure
|
||||
|
|
|
@ -297,7 +297,9 @@ module Vagrant
|
|||
def jailbreak(env = {})
|
||||
return if ENV.key?("VAGRANT_SKIP_SUBPROCESS_JAILBREAK")
|
||||
|
||||
env.replace(::Bundler::ORIGINAL_ENV) if defined?(::Bundler::ORIGINAL_ENV)
|
||||
if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV)
|
||||
env.replace(::Bundler::ORIGINAL_ENV)
|
||||
end
|
||||
env.merge!(Vagrant.original_env)
|
||||
|
||||
# Bundler does this, so I guess we should as well, since I think it
|
||||
|
|
|
@ -6,6 +6,12 @@ module VagrantPlugins
|
|||
module CommandPlugin
|
||||
module Action
|
||||
# This middleware sequence will install a plugin.
|
||||
def self.action_expunge
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use ExpungePlugins
|
||||
end
|
||||
end
|
||||
|
||||
def self.action_install
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use InstallGem
|
||||
|
@ -27,6 +33,13 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
# This middleware sequence will repair installed plugins.
|
||||
def self.action_repair
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use RepairPlugins
|
||||
end
|
||||
end
|
||||
|
||||
# This middleware sequence will uninstall a plugin.
|
||||
def self.action_uninstall
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
|
@ -44,10 +57,12 @@ module VagrantPlugins
|
|||
|
||||
# The autoload farm
|
||||
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
||||
autoload :ExpungePlugins, action_root.join("expunge_plugins")
|
||||
autoload :InstallGem, action_root.join("install_gem")
|
||||
autoload :LicensePlugin, action_root.join("license_plugin")
|
||||
autoload :ListPlugins, action_root.join("list_plugins")
|
||||
autoload :PluginExistsCheck, action_root.join("plugin_exists_check")
|
||||
autoload :RepairPlugins, action_root.join("repair_plugins")
|
||||
autoload :UninstallPlugin, action_root.join("uninstall_plugin")
|
||||
autoload :UpdateGems, action_root.join("update_gems")
|
||||
end
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
require "vagrant/plugin/manager"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPlugin
|
||||
module Action
|
||||
# This middleware removes user installed plugins by
|
||||
# removing:
|
||||
# * ~/.vagrant.d/plugins.json
|
||||
# * ~/.vagrant.d/gems
|
||||
# Usage should be restricted to when a repair is
|
||||
# unsuccessful and the only reasonable option remaining
|
||||
# is to re-install all plugins
|
||||
class ExpungePlugins
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if !env[:force]
|
||||
result = env[:ui].ask(
|
||||
I18n.t("vagrant.commands.plugin.expunge_confirm") +
|
||||
" [Y/N]:"
|
||||
)
|
||||
if result.to_s.downcase.strip != 'y'
|
||||
abort_action = true
|
||||
end
|
||||
end
|
||||
|
||||
if !abort_action
|
||||
plugins_json = File.join(env[:home_path], "plugins.json")
|
||||
plugins_gems = env[:gems_path]
|
||||
|
||||
if File.exist?(plugins_json)
|
||||
FileUtils.rm(plugins_json)
|
||||
end
|
||||
|
||||
if File.directory?(plugins_gems)
|
||||
FileUtils.rm_rf(plugins_gems)
|
||||
end
|
||||
|
||||
env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_complete"))
|
||||
|
||||
@app.call(env)
|
||||
else
|
||||
env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_aborted"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
require "vagrant/plugin/manager"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPlugin
|
||||
module Action
|
||||
# This middleware attempts to repair installed plugins.
|
||||
#
|
||||
# In general, if plugins are failing to properly load the
|
||||
# core issue will likely be one of two issues:
|
||||
# 1. manual modifications within ~/.vagrant.d/
|
||||
# 2. vagrant upgrade
|
||||
# Running an install on configured plugin set will most
|
||||
# likely fix these issues, which is all this action does.
|
||||
class RepairPlugins
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info(I18n.t("vagrant.commands.plugin.repairing"))
|
||||
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||
Vagrant::Bundler.instance.init!(plugins, :repair)
|
||||
env[:ui].info(I18n.t("vagrant.commands.plugin.repair_complete"))
|
||||
|
||||
# Continue
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
require 'optparse'
|
||||
|
||||
require_relative "base"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPlugin
|
||||
module Command
|
||||
class Expunge < Base
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant plugin expunge [-h]"
|
||||
|
||||
o.on("--force", "Do not prompt for confirmation") do |force|
|
||||
options[:force] = force
|
||||
end
|
||||
|
||||
o.on("--reinstall", "Reinstall current plugins after expunge") do |reinstall|
|
||||
options[:reinstall] = reinstall
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0
|
||||
|
||||
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||
|
||||
if !options[:reinstall] && !options[:force] && !plugins.empty?
|
||||
result = @env.ui.ask(
|
||||
I18n.t("vagrant.commands.plugin.expunge_request_reinstall") +
|
||||
" [Y/N]:"
|
||||
)
|
||||
options[:reinstall] = result.to_s.downcase.strip == "y"
|
||||
end
|
||||
|
||||
# Remove all installed user plugins
|
||||
action(Action.action_expunge, options)
|
||||
|
||||
if options[:reinstall]
|
||||
@env.ui.info(I18n.t("vagrant.commands.plugin.expunge_reinstall"))
|
||||
plugins.each do |plugin_name, plugin_info|
|
||||
plugin_info = Hash[
|
||||
plugin_info.map do |key, value|
|
||||
[key.to_sym, value]
|
||||
end
|
||||
]
|
||||
action(
|
||||
Action.action_install,
|
||||
plugin_info.merge(
|
||||
plugin_name: plugin_name
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Success, exit status 0
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
require 'optparse'
|
||||
|
||||
require_relative "base"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPlugin
|
||||
module Command
|
||||
class Repair < Base
|
||||
def execute
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant plugin repair [-h]"
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0
|
||||
|
||||
# Attempt to repair installed plugins
|
||||
action(Action.action_repair)
|
||||
|
||||
# Success, exit status 0
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,11 @@ module VagrantPlugins
|
|||
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
|
||||
|
||||
@subcommands = Vagrant::Registry.new
|
||||
@subcommands.register(:expunge) do
|
||||
require_relative "expunge"
|
||||
Expunge
|
||||
end
|
||||
|
||||
@subcommands.register(:install) do
|
||||
require_relative "install"
|
||||
Install
|
||||
|
@ -29,6 +34,11 @@ module VagrantPlugins
|
|||
List
|
||||
end
|
||||
|
||||
@subcommands.register(:repair) do
|
||||
require_relative "repair"
|
||||
Repair
|
||||
end
|
||||
|
||||
@subcommands.register(:update) do
|
||||
require_relative "update"
|
||||
Update
|
||||
|
|
|
@ -1576,6 +1576,24 @@ en:
|
|||
the comments in the Vagrantfile as well as documentation on
|
||||
`vagrantup.com` for more information on using Vagrant.
|
||||
plugin:
|
||||
expunge_confirm: |-
|
||||
|
||||
This command permanently deletes all currently installed user plugins. It
|
||||
should only be used when a repair command is unable to properly fix the
|
||||
system.
|
||||
|
||||
Continue?
|
||||
expunge_request_reinstall: |-
|
||||
Would you like Vagrant to attempt to reinstall current plugins?
|
||||
expunge_complete: |-
|
||||
|
||||
All user installed plugins have been removed from this Vagrant environment!
|
||||
expunge_reinstall: |-
|
||||
|
||||
Vagrant will now attempt to reinstall user plugins that were removed.
|
||||
expunge_aborted: |-
|
||||
|
||||
Vagrant expunge has been declined. Skipping removal of plugins.
|
||||
installed_license: |-
|
||||
The license for '%{name}' was successfully installed!
|
||||
installing_license: |-
|
||||
|
@ -1601,6 +1619,14 @@ en:
|
|||
post_install: |-
|
||||
Post install message from the '%{name}' plugin:
|
||||
|
||||
%{message}
|
||||
repairing: |-
|
||||
Repairing currently installed plugins. This may take a few minutes...
|
||||
repair_complete: |-
|
||||
Installed plugins successfully repaired!
|
||||
repair_failed: |-
|
||||
Failed to automatically repair installed Vagrant plugins. Failure message:
|
||||
|
||||
%{message}
|
||||
snapshot:
|
||||
not_supported: |-
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
require File.expand_path("../../../../../base", __FILE__)
|
||||
|
||||
describe VagrantPlugins::CommandPlugin::Action::ExpungePlugins do
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:home_path){ '/fake/file/path/.vagrant.d' }
|
||||
let(:gems_path){ "#{home_path}/gems" }
|
||||
let(:force){ true }
|
||||
let(:env) {{
|
||||
ui: Vagrant::UI::Silent.new,
|
||||
home_path: home_path,
|
||||
gems_path: gems_path,
|
||||
force: force
|
||||
}}
|
||||
|
||||
let(:manager) { double("manager") }
|
||||
|
||||
let(:expect_to_receive) do
|
||||
lambda do
|
||||
allow(File).to receive(:exist?).with(File.join(home_path, 'plugins.json')).and_return(true)
|
||||
allow(File).to receive(:directory?).with(gems_path).and_return(true)
|
||||
expect(FileUtils).to receive(:rm).with(File.join(home_path, 'plugins.json'))
|
||||
expect(FileUtils).to receive(:rm_rf).with(gems_path)
|
||||
expect(app).to receive(:call).with(env).once
|
||||
end
|
||||
end
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
before do
|
||||
Vagrant::Plugin::Manager.stub(instance: manager)
|
||||
end
|
||||
|
||||
describe "#call" do
|
||||
before do
|
||||
instance_exec(&expect_to_receive)
|
||||
end
|
||||
|
||||
it "should delete all plugins" do
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
describe "when force is false" do
|
||||
let(:force){ false }
|
||||
|
||||
it "should prompt user before deleting all plugins" do
|
||||
expect(env[:ui]).to receive(:ask).and_return("Y\n")
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
describe "when user declines prompt" do
|
||||
let(:expect_to_receive) do
|
||||
lambda do
|
||||
expect(app).not_to receive(:call)
|
||||
end
|
||||
end
|
||||
|
||||
it "should not delete all plugins" do
|
||||
expect(env[:ui]).to receive(:ask).and_return("N\n")
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,14 +45,14 @@ describe Vagrant::Plugin::Manager do
|
|||
end
|
||||
|
||||
it "masks GemNotFound with our error" do
|
||||
expect(bundler).to receive(:install).and_raise(Bundler::GemNotFound)
|
||||
expect(bundler).to receive(:install).and_raise(Gem::GemNotFoundException)
|
||||
|
||||
expect { subject.install_plugin("foo") }.
|
||||
to raise_error(Vagrant::Errors::PluginGemNotFound)
|
||||
end
|
||||
|
||||
it "masks bundler errors with our own error" do
|
||||
expect(bundler).to receive(:install).and_raise(Bundler::InstallError)
|
||||
expect(bundler).to receive(:install).and_raise(Gem::InstallError)
|
||||
|
||||
expect { subject.install_plugin("foo") }.
|
||||
to raise_error(Vagrant::Errors::BundlerError)
|
||||
|
@ -140,7 +140,7 @@ describe Vagrant::Plugin::Manager do
|
|||
end
|
||||
|
||||
it "masks bundler errors with our own error" do
|
||||
expect(bundler).to receive(:clean).and_raise(Bundler::InstallError)
|
||||
expect(bundler).to receive(:clean).and_raise(Gem::InstallError)
|
||||
|
||||
expect { subject.uninstall_plugin("foo") }.
|
||||
to raise_error(Vagrant::Errors::BundlerError)
|
||||
|
@ -182,7 +182,7 @@ describe Vagrant::Plugin::Manager do
|
|||
|
||||
describe "#update_plugins" do
|
||||
it "masks bundler errors with our own error" do
|
||||
expect(bundler).to receive(:update).and_raise(Bundler::InstallError)
|
||||
expect(bundler).to receive(:update).and_raise(Gem::InstallError)
|
||||
|
||||
expect { subject.update_plugins([]) }.
|
||||
to raise_error(Vagrant::Errors::BundlerError)
|
||||
|
|
|
@ -16,11 +16,6 @@ Gem::Specification.new do |s|
|
|||
s.required_rubygems_version = ">= 1.3.6"
|
||||
s.rubyforge_project = "vagrant"
|
||||
|
||||
# Do not update the Bundler constraint. Vagrant relies on internal Bundler
|
||||
# APIs, so even point releases can introduce breaking changes. These changes
|
||||
# are *untestable* until after a release is made because there is no way for
|
||||
# Bundler to exec into itself. Please do not update the Bundler constraint.
|
||||
s.add_dependency "bundler", "= 1.12.5"
|
||||
s.add_dependency "childprocess", "~> 0.5.0"
|
||||
s.add_dependency "erubis", "~> 2.7.0"
|
||||
s.add_dependency "i18n", ">= 0.6.0", "<= 0.8.0"
|
||||
|
|
Loading…
Reference in New Issue