diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 37554a066..72f5e0748 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -411,6 +411,7 @@ module Vagrant :box_collection => boxes, :global_config => config_global, :host => host, + :gems_path => gems_path, :root_path => root_path, :tmp_path => tmp_path, :ui => @ui diff --git a/plugins/commands/gem/command.rb b/plugins/commands/gem/command.rb index f9b47b842..b3c35828e 100644 --- a/plugins/commands/gem/command.rb +++ b/plugins/commands/gem/command.rb @@ -15,7 +15,7 @@ module VagrantPlugins if defined?(Bundler) require 'bundler/shared_helpers' if Bundler::SharedHelpers.in_bundle? - raise Errors::GemCommandInBundler + raise Vagrant::Errors::GemCommandInBundler end end diff --git a/plugins/commands/plugin/action.rb b/plugins/commands/plugin/action.rb new file mode 100644 index 000000000..a38608ac4 --- /dev/null +++ b/plugins/commands/plugin/action.rb @@ -0,0 +1,22 @@ +require "pathname" + +require "vagrant/action/builder" + +module VagrantPlugins + module CommandPlugin + module Action + # This middleware sequence will install a plugin. + def self.action_install + Vagrant::Action::Builder.new.tap do |b| + b.use BundlerCheck + b.use InstallGem + end + end + + # The autoload farm + action_root = Pathname.new(File.expand_path("../action", __FILE__)) + autoload :BundlerCheck, action_root.join("bundler_check") + autoload :InstallGem, action_root.join("install_gem") + end + end +end diff --git a/plugins/commands/plugin/action/bundler_check.rb b/plugins/commands/plugin/action/bundler_check.rb new file mode 100644 index 000000000..b53a83c5d --- /dev/null +++ b/plugins/commands/plugin/action/bundler_check.rb @@ -0,0 +1,25 @@ +module VagrantPlugins + module CommandPlugin + module Action + class BundlerCheck + def initialize(app, env) + @app = app + end + + def call(env) + # Bundler sets up its own custom gem load paths such that our + # own gems are never loaded. Therefore, give an error if a user + # tries to install gems while within a Bundler-managed environment. + if defined?(Bundler) + require 'bundler/shared_helpers' + if Bundler::SharedHelpers.in_bundle? + raise Vagrant::Errors::GemCommandInBundler + end + end + + @app.call(env) + end + end + end + end +end diff --git a/plugins/commands/plugin/action/install_gem.rb b/plugins/commands/plugin/action/install_gem.rb new file mode 100644 index 000000000..1abd010cf --- /dev/null +++ b/plugins/commands/plugin/action/install_gem.rb @@ -0,0 +1,47 @@ +require "rubygems" +require "rubygems/gem_runner" + +require "log4r" + +module VagrantPlugins + module CommandPlugin + module Action + # This action takes the `:plugin_name` variable in the environment + # and installs it using the RubyGems API. + class InstallGem + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::installgem") + end + + def call(env) + plugin_name = env[:plugin_name] + + # First, install the gem + begin + # Set the GEM_HOME so that it is installed into our local gems path + old_gem_home = ENV["GEM_HOME"] + ENV["GEM_HOME"] = env[:gems_path].to_s + p ENV["GEM_PATH"] + @logger.debug("Set GEM_HOME to: #{ENV["GEM_HOME"]}") + + @logger.info("Installing gem: #{plugin_name}") + env[:ui].info( + I18n.t("vagrant.commands.plugin.installing", :name => plugin_name)) + Gem.clear_paths + Gem::GemRunner.new.run( + ["install", plugin_name, "--no-ri", "--no-rdoc"]) + ensure + ENV["GEM_HOME"] = old_gem_home + end + + # Mark that we installed the gem + env[:plugin_state_file].add_plugin(plugin_name) + + # Continue + @app.call(env) + end + end + end + end +end diff --git a/plugins/commands/plugin/command/install.rb b/plugins/commands/plugin/command/install.rb new file mode 100644 index 000000000..ffa137672 --- /dev/null +++ b/plugins/commands/plugin/command/install.rb @@ -0,0 +1,29 @@ +require 'optparse' + +module VagrantPlugins + module CommandPlugin + module Command + class Install < Vagrant.plugin("2", :command) + def execute + opts = OptionParser.new do |o| + o.banner = "Usage: vagrant plugin install [-h]" + end + + # Parse the options + argv = parse_options(opts) + return if !argv + raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 + + # Install the gem + @env.action_runner.run(Action.action_install, { + :plugin_name => argv[0], + :plugin_state_file => StateFile.new(@env.data_dir.join("plugins.json")) + }) + + # Success, exit status 0 + 0 + end + end + end + end +end diff --git a/plugins/commands/plugin/command/root.rb b/plugins/commands/plugin/command/root.rb index bb10557d5..d2fc4b059 100644 --- a/plugins/commands/plugin/command/root.rb +++ b/plugins/commands/plugin/command/root.rb @@ -10,6 +10,10 @@ module VagrantPlugins @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new + @subcommands.register(:install) do + require_relative "install" + Install + end end def execute diff --git a/plugins/commands/plugin/plugin.rb b/plugins/commands/plugin/plugin.rb index 4739cfab1..c7cf5e24f 100644 --- a/plugins/commands/plugin/plugin.rb +++ b/plugins/commands/plugin/plugin.rb @@ -14,5 +14,8 @@ DESC Command::Root end end + + autoload :Action, File.expand_path("../action", __FILE__) + autoload :StateFile, File.expand_path("../state_file", __FILE__) end end diff --git a/plugins/commands/plugin/state_file.rb b/plugins/commands/plugin/state_file.rb new file mode 100644 index 000000000..5282ee480 --- /dev/null +++ b/plugins/commands/plugin/state_file.rb @@ -0,0 +1,32 @@ +require "json" + +module VagrantPlugins + module CommandPlugin + # This is a helper to deal with the plugin state file that Vagrant + # uses to track what plugins are installed and activated and such. + class StateFile + def initialize(path) + @path = path + + @data = {} + @data = JSON.parse(@path.read) if @path.exist? + end + + # Add a plugin that is installed to the state file. + # + # @param [String] name The name of the plugin + def add_plugin(name) + @data["installed"] ||= [] + @data["installed"] << name + save! + end + + # This saves the state back into the state file. + def save! + @path.open("w+") do |f| + f.write(JSON.dump(@data)) + end + end + end + end +end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 2c243c2dd..1ce45e7e6 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -411,6 +411,9 @@ en: ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. + plugin: + installing: |- + Installing the '%{name}' plugin... status: aborted: |- The VM is in an aborted state. This means that it was abruptly