Shawn Neal 0a2992b92d Fixed issue 3285
When using prerelease gems we need to ensure we store the gem version otherwise Bundler won't use the correct gem version since prerelease gems are not available by default. By storing the gem version for prerelease gems, we allow Bundler to properly resolve prerelease gems and not give potentially confusing errors to Vagrant users using prerelease plugins.
2014-03-21 08:15:24 -07:00

163 lines
4.8 KiB
Ruby

require "pathname"
require "set"
require_relative "../bundler"
require_relative "../shared_helpers"
require_relative "state_file"
module Vagrant
module Plugin
# The Manager helps with installing, listing, and initializing plugins.
class Manager
# Returns the path to the [StateFile] for user plugins.
#
# @return [Pathname]
def self.user_plugins_file
Vagrant.user_data_path.join("plugins.json")
end
# Returns the path to the [StateFile] for system plugins.
def self.system_plugins_file
dir = Vagrant.installer_embedded_dir
return nil if !dir
Pathname.new(dir).join("plugins.json")
end
def self.instance
@instance ||= self.new(user_plugins_file)
end
# @param [Pathname] user_file
def initialize(user_file)
@user_file = StateFile.new(user_file)
system_path = self.class.system_plugins_file
@system_file = nil
@system_file = StateFile.new(system_path) if system_path && system_path.file?
end
# Installs another plugin into our gem directory.
#
# @param [String] name Name of the plugin (gem)
# @return [Gem::Specification]
def install_plugin(name, **opts)
local = false
if name =~ /\.gem$/
# If this is a gem file, then we install that gem locally.
local_spec = Vagrant::Bundler.instance.install_local(name)
name = local_spec.name
opts[:version] = local_spec.version.to_s
local = true
end
plugins = installed_plugins
plugins[name] = {
"require" => opts[:require],
"gem_version" => opts[:version],
"sources" => opts[:sources],
}
result = nil
install_lambda = lambda do
Vagrant::Bundler.instance.install(plugins, local).each do |spec|
next if spec.name != name
next if result && result.version >= spec.version
result = spec
end
end
if opts[:verbose]
Vagrant::Bundler.instance.verbose(&install_lambda)
else
install_lambda.call
end
# If the version constraint is just a specific (non prerelease) version, don't
# store the constraint.
if opts[:version] && opts[:version] =~ /^\d/ && !Gem::Requirement.parse(opts[:version])[1].prerelease?
opts.delete(:version)
end
# Add the plugin to the state file
@user_file.add_plugin(
result.name,
version: opts[:version],
require: opts[:require],
sources: opts[:sources],
)
result
rescue ::Bundler::GemNotFound
raise Errors::PluginGemNotFound, name: name
rescue ::Bundler::BundlerError => e
raise Errors::BundlerError, message: e.to_s
end
# Uninstalls the plugin with the given name.
#
# @param [String] name
def uninstall_plugin(name)
if @system_file
if !@user_file.has_plugin?(name) && @system_file.has_plugin?(name)
raise Errors::PluginUninstallSystem,
name: name
end
end
@user_file.remove_plugin(name)
# Clean the environment, removing any old plugins
Vagrant::Bundler.instance.clean(installed_plugins)
rescue ::Bundler::BundlerError => 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
raise Errors::BundlerError, message: e.to_s
end
# This returns the list of plugins that should be enabled.
#
# @return [Hash]
def installed_plugins
system = {}
if @system_file
@system_file.installed_plugins.each do |k, v|
system[k] = v.merge("system" => true)
end
end
system.merge(@user_file.installed_plugins)
end
# This returns the list of plugins that are installed as
# Gem::Specifications.
#
# @return [Array<Gem::Specification>]
def installed_specs
installed = Set.new(installed_plugins.keys)
# Go through the plugins installed in this environment and
# get the latest version of each.
installed_map = {}
Gem::Specification.find_all.each do |spec|
# Ignore specs that aren't in our installed list
next if !installed.include?(spec.name)
# If we already have a newer version in our list of installed,
# then ignore it
next if installed_map.has_key?(spec.name) &&
installed_map[spec.name].version >= spec.version
installed_map[spec.name] = spec
end
installed_map.values
end
end
end
end