Instead of shelling out, use the RubyGems API to install plugins

This gets us an accurate name of the gem even if it is installed
from a file.
This commit is contained in:
Mitchell Hashimoto 2013-02-03 13:59:43 -08:00
parent 8cde263a14
commit 1162c2dbfc
5 changed files with 55 additions and 35 deletions

View File

@ -340,6 +340,10 @@ module Vagrant
error_key(:plugin_gem_error)
end
class PluginInstallBadEntryPoint < VagrantError
error_key(:plugin_install_bad_entry_point)
end
class PluginLoadError < VagrantError
status_code(81)
error_key(:plugin_load_error)

View File

@ -1,5 +1,5 @@
require "rubygems"
require "rubygems/gem_runner"
require "rubygems/dependency_installer"
require "log4r"
@ -20,14 +20,43 @@ module VagrantPlugins
# Install the gem
env[:ui].info(I18n.t("vagrant.commands.plugin.installing",
:name => plugin_name))
env[:gem_helper].cli(["install", plugin_name, "--no-ri", "--no-rdoc"])
installed_gems = env[:gem_helper].with_environment do
installer = Gem::DependencyInstaller.new(:document => [])
installer.install(plugin_name)
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.last
# 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
env[:plugin_state_file].add_plugin(plugin_name)
@logger.info("Adding the plugin to the state file...")
env[:plugin_state_file].add_plugin(plugin_spec.name)
# Tell the user
env[:ui].success(I18n.t("vagrant.commands.plugin.installed",
:name => plugin_spec.name))
# Continue
@app.call(env)
end
def recover(env)
# If any error happens, we uninstall it and remove it from
# the state file. We can only do this if we successfully installed
# the gem in the first place.
if @installed_plugin_name
new_env = env.dup
new_env.delete(:interrupted)
new_env[:plugin_name] = @installed_plugin_name
new_env[:action_runner].run(Action.action_uninstall, new_env)
end
end
end
end
end

View File

@ -7,8 +7,16 @@ module VagrantPlugins
module Command
class Install < Base
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant plugin install <name> [-h]"
o.separator ""
o.on("--entry-point NAME", String,
"The name of the entry point file for loading the plugin.") do |entry_point|
options[:entry_point] = entry_point
end
end
# Parse the options
@ -17,7 +25,10 @@ module VagrantPlugins
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1
# Install the gem
action(Action.action_install, :plugin_name => argv[0])
action(Action.action_install, {
:plugin_entry_point => options[:entry_point],
:plugin_name => argv[0]
})
# Success, exit status 0
0

View File

@ -13,37 +13,6 @@ module VagrantPlugins
@logger = Log4r::Logger.new("vagrant::plugins::plugincommand::gemhelper")
end
# This executes the `gem` command with the given arguments. Under
# the covers this is actually using the RubyGems API directly,
# instead of shelling out, which allows for more fine-grained control.
#
# @param [Array<String>] argv The arguments to send to the `gem` command.
def cli(argv)
# Initialize the UI to use for RubyGems. This allows us to capture
# the stdout/stderr without actually going to the real STDOUT/STDERR.
# The final "false" here tells RubyGems we're not a TTY, so don't
# ask us things.
gem_ui = Gem::StreamUI.new(StringIO.new, StringIO.new, StringIO.new, false)
# Set the GEM_HOME so that it is installed into our local gems path
with_environment do
@logger.info("Calling gem with argv: #{argv.inspect}")
Gem::DefaultUserInteraction.use_ui(gem_ui) do
Gem::GemRunner.new.run(argv)
end
end
rescue Gem::SystemExitException => e
# This means that something forced an exit within RubyGems.
# We capture this to check whether it succeeded or not by
# checking the "exit_code"
raise Vagrant::Errors::PluginGemError,
:output => gem_ui.errs.string.chomp if e.exit_code != 0
ensure
# Log the output properly
@logger.debug("Gem STDOUT: #{gem_ui.outs.string}")
@logger.debug("Gem STDERR: #{gem_ui.errs.string}")
end
# This will yield the given block with the proper ENV setup so
# that RubyGems only sees the gems in the Vagrant-managed gem
# path.

View File

@ -170,6 +170,11 @@ en:
manage Vagrant plugins. The output of the errors are shown below:
%{output}
plugin_install_bad_entry_point: |-
Attempting to load the plugin '%{name}' failed, because
the entry point doesn't exist. The entry point attempted was
'%{entry_point}'. If this is not correct, please manually
specify an `--entry-point` when installing the plugin.
plugin_load_error: |-
The plugin "%{plugin}" could not be found. Please make sure that it is
properly installed via `vagrant plugin`.
@ -412,6 +417,8 @@ en:
plugin:
no_plugins: |-
No plugins installed.
installed: |-
Installed the '%{name}' plugin!
installing: |-
Installing the '%{name}' plugin. This can take a few minutes...
uninstalling: |-