installing gems works

This commit is contained in:
Mitchell Hashimoto 2014-01-05 14:54:50 -08:00
parent 1eef75a715
commit 86610bf735
6 changed files with 129 additions and 44 deletions

View File

@ -18,7 +18,7 @@ require_relative "vagrant/shared_helpers"
if Vagrant.plugins_enabled?
# Initialize Bundler before we load _any_ RubyGems.
require_relative "vagrant/bundler"
require_relative "vagrant/plugin_manager"
require_relative "vagrant/plugin/manager"
Vagrant::Bundler.instance.init!(Vagrant::Plugin::Manager.instance.installed_plugins)
end

View File

@ -1,3 +1,4 @@
require "pathname"
require "tempfile"
require_relative "shared_helpers"
@ -12,6 +13,11 @@ module Vagrant
@bundler ||= self.new
end
def initialize
@gem_home = ENV["GEM_HOME"]
@gem_path = ENV["GEM_PATH"]
end
# Initializes Bundler and the various gem paths so that we can begin
# loading gems. This must only be called once.
def init!(plugins)
@ -24,23 +30,117 @@ module Vagrant
# 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 = Tempfile.new("vagrant-gemfile")
@gemfile.puts(%Q[gem "vagrant", "= #{Vagrant::VERSION}"])
plugins.each do |plugin|
@gemfile.puts(%Q[gem "#{plugin}"])
end
@gemfile.close
@gemfile = build_gemfile(plugins)
# Set the environmental variables for Bundler
ENV["BUNDLE_CONFIG"] = @configfile.path
ENV["BUNDLE_GEMFILE"] = @gemfile.path
ENV["GEM_PATH"] =
"#{Vagrant.user_data_path.join("gems")}#{::File::PATH_SEPARATOR}#{ENV["GEM_PATH"]}"
"#{Vagrant.user_data_path.join("gems")}#{::File::PATH_SEPARATOR}#{@gem_path}"
Gem.clear_paths
# Load Bundler and setup our paths
require "bundler"
::Bundler.setup
# Do some additional Bundler initialization
::Bundler.ui = ::Bundler::UI.new
if !::Bundler.ui.respond_to?(:silence)
ui = ::Bundler.ui
def ui.silence(*args)
yield
end
end
end
# Installs the list of plugins.
#
# @return [Array<Gem::Specification>]
def install(plugins)
gemfile = build_gemfile(plugins)
lockfile = "#{gemfile.path}.lock"
definition = ::Bundler::Definition.build(gemfile, lockfile, nil)
root = File.dirname(gemfile.path)
opts = {}
opts["update"] = true
with_isolated_gem do
::Bundler::Installer.install(root, definition, opts)
end
# Clean up any unused/old gems
runtime = ::Bundler::Runtime.new(root, definition)
runtime.clean
definition.specs
end
# Builds a valid Gemfile for use with Bundler given the list of
# plugins.
#
# @return [Tempfile]
def build_gemfile(plugins)
Tempfile.new("vagrant-gemfile").tap do |gemfile|
gemfile.puts(%Q[source "https://rubygems.org"])
gemfile.puts(%Q[source "http://gems.hashicorp.com"])
gemfile.puts(%Q[gem "vagrant", "= #{Vagrant::VERSION}"])
plugins.each do |plugin|
gemfile.puts(%Q[gem "#{plugin}"])
end
gemfile.close
end
end
protected
def with_isolated_gem
# 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"] = nil
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
# Set a custom configuration to avoid loading ~/.gemrc loads and
# /etc/gemrc and so on.
old_config = Gem.configuration
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
ENV["BUNDLE_GEMFILE"] = old_gemfile
ENV["GEM_HOME"] = @gem_home
ENV["RUBYOPT"] = old_rubyopt
Gem.configuration = old_config
Gem.paths = ENV
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
end
end

View File

@ -66,6 +66,7 @@ end
# We need these components always so instead of an autoload we
# just require them explicitly here.
require "vagrant/plugin"
require "vagrant/registry"
module Vagrant

View File

@ -1,4 +1,6 @@
require_relative "../bundler"
require_relative "../shared_helpers"
require_relative "state_file"
module Vagrant
module Plugin
@ -20,6 +22,20 @@ module Vagrant
@global_file = StateFile.new(global_file)
end
# Installs another plugin into our gem directory.
#
# @param [String] name Name of the plugin (gem)
def install_plugin(name)
result = nil
Vagrant::Bundler.instance.install(installed_plugins.push(name)).each do |spec|
next if spec.name != name
next if result && result.version >= spec.version
result = spec
end
result
end
# This returns the list of plugins that should be enabled.
#
# @return [Array<String>]

View File

@ -9,7 +9,6 @@ module VagrantPlugins
def self.action_install
Vagrant::Action::Builder.new.tap do |b|
b.use InstallGem
b.use PruneGems
end
end
@ -31,7 +30,6 @@ module VagrantPlugins
def self.action_uninstall
Vagrant::Action::Builder.new.tap do |b|
b.use UninstallPlugin
b.use PruneGems
end
end
@ -40,7 +38,6 @@ module VagrantPlugins
Vagrant::Action::Builder.new.tap do |b|
b.use PluginExistsCheck
b.use InstallGem
b.use PruneGems
end
end

View File

@ -8,6 +8,7 @@ rescue LoadError
end
require "log4r"
require "vagrant/plugin/manager"
module VagrantPlugins
module CommandPlugin
@ -49,40 +50,10 @@ module VagrantPlugins
plugin_name_label += " --version '#{version}'" if version
env[:ui].info(I18n.t("vagrant.commands.plugin.installing",
:name => plugin_name_label))
installed_gems = env[:gem_helper].with_environment do
# Override the list of sources by the ones set as a parameter if given
if env[:plugin_sources]
@logger.info("Custom plugin sources: #{env[:plugin_sources]}")
Gem.sources = env[:plugin_sources]
end
installer = Gem::DependencyInstaller.new(:document => [], :prerelease => prerelease)
# If we don't have a version, use the default version
version ||= Gem::Requirement.default
begin
installer.install(plugin_name, version)
rescue Gem::GemNotFoundException
raise Vagrant::Errors::PluginInstallNotFound,
:name => plugin_name
end
end
# The plugin spec is the last installed gem since RubyGems
# currently always installed the requested gem last.
@logger.debug("Installed #{installed_gems.length} gems.")
plugin_spec = installed_gems.find do |gem|
gem.name.downcase == find_plugin_name.downcase
end
# Store the installed name so we can uninstall it if things go
# wrong.
@installed_plugin_name = plugin_spec.name
# Mark that we installed the gem
@logger.info("Adding the plugin to the state file...")
env[:plugin_state_file].add_plugin(plugin_spec.name)
# TODO: support version, pre-release, custom sources
manager = Vagrant::Plugin::Manager.instance
plugin_spec = manager.install_plugin(plugin_name)
# Tell the user
env[:ui].success(I18n.t("vagrant.commands.plugin.installed",