From 1162c2dbfcb198d0547b89f50324760cbc7422d9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 3 Feb 2013 13:59:43 -0800 Subject: [PATCH] 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. --- lib/vagrant/errors.rb | 4 +++ plugins/commands/plugin/action/install_gem.rb | 35 +++++++++++++++++-- plugins/commands/plugin/command/install.rb | 13 ++++++- plugins/commands/plugin/gem_helper.rb | 31 ---------------- templates/locales/en.yml | 7 ++++ 5 files changed, 55 insertions(+), 35 deletions(-) diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 22ea2e09e..d3823ba5e 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -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) diff --git a/plugins/commands/plugin/action/install_gem.rb b/plugins/commands/plugin/action/install_gem.rb index 71be71b0c..6f5f30473 100644 --- a/plugins/commands/plugin/action/install_gem.rb +++ b/plugins/commands/plugin/action/install_gem.rb @@ -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 diff --git a/plugins/commands/plugin/command/install.rb b/plugins/commands/plugin/command/install.rb index 295285cc2..10bcca11e 100644 --- a/plugins/commands/plugin/command/install.rb +++ b/plugins/commands/plugin/command/install.rb @@ -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 [-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 diff --git a/plugins/commands/plugin/gem_helper.rb b/plugins/commands/plugin/gem_helper.rb index 2dc260e5e..3fa45b358 100644 --- a/plugins/commands/plugin/gem_helper.rb +++ b/plugins/commands/plugin/gem_helper.rb @@ -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] 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. diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 0e90b0438..f00f7e0d9 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -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: |-