diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb index af53d6a5d..b9739ffb0 100644 --- a/lib/vagrant/bundler.rb +++ b/lib/vagrant/bundler.rb @@ -52,25 +52,22 @@ module Vagrant # Installs the list of plugins. # + # @param [Hash] plugins # @return [Array] def install(plugins) - gemfile = build_gemfile(plugins) - lockfile = "#{gemfile.path}.lock" - definition = ::Bundler::Definition.build(gemfile, lockfile, nil) - root = File.dirname(gemfile.path) - opts = {} + internal_install(plugins, nil) + end - with_isolated_gem do - ::Bundler::Installer.install(root, definition, opts) - end - - # TODO(mitchellh): clean gems here... for some reason when I put - # it in on install, we get a GemNotFound exception. Gotta investigate. - - definition.specs - rescue ::Bundler::VersionConflict => e - raise Errors::PluginInstallVersionConflict, - conflicts: e.to_s.gsub("Bundler", "Vagrant") + # Update updates the given plugins, or every plugin if none is given. + # + # @param [Hash] plugins + # @param [Array] specific Specific plugin names to update. If + # empty or nil, all plugins will be updated. + def update(plugins, specific) + specific ||= [] + update = true + update = { gems: specific } if !specific.empty? + internal_install(plugins, update) end # Clean removes any unused gems. @@ -86,6 +83,8 @@ module Vagrant end end + protected + # Builds a valid Gemfile for use with Bundler given the list of # plugins. # @@ -115,7 +114,31 @@ module Vagrant end end - protected + # This installs a set of plugins and optionally updates those gems. + # + # @param [Hash] plugins + # @param [Hash, Boolean] update If true, updates all plugins, otherwise + # can be a hash of options. See Bundler.definition. + # @return [Array] + def internal_install(plugins, update) + gemfile = build_gemfile(plugins) + lockfile = "#{gemfile.path}.lock" + definition = ::Bundler::Definition.build(gemfile, lockfile, update) + root = File.dirname(gemfile.path) + opts = {} + + with_isolated_gem do + ::Bundler::Installer.install(root, definition, opts) + end + + # TODO(mitchellh): clean gems here... for some reason when I put + # it in on install, we get a GemNotFound exception. Gotta investigate. + + definition.specs + rescue ::Bundler::VersionConflict => e + raise Errors::PluginInstallVersionConflict, + conflicts: e.to_s.gsub("Bundler", "Vagrant") + end def with_isolated_gem # Remove bundler settings so that Bundler isn't loaded when building diff --git a/lib/vagrant/plugin/manager.rb b/lib/vagrant/plugin/manager.rb index b58e2ffd7..9cb016411 100644 --- a/lib/vagrant/plugin/manager.rb +++ b/lib/vagrant/plugin/manager.rb @@ -61,6 +61,11 @@ module Vagrant Vagrant::Bundler.instance.clean(installed_plugins) end + # Updates all or a specific set of plugins. + def update_plugins(specific) + Vagrant::Bundler.instance.update(installed_plugins, specific) + end + # This returns the list of plugins that should be enabled. # # @return [Hash] diff --git a/plugins/commands/plugin/action.rb b/plugins/commands/plugin/action.rb index 89b58e0c2..832bc3508 100644 --- a/plugins/commands/plugin/action.rb +++ b/plugins/commands/plugin/action.rb @@ -38,8 +38,7 @@ module VagrantPlugins # This middleware sequence will update a plugin. def self.action_update Vagrant::Action::Builder.new.tap do |b| - b.use PluginExistsCheck - b.use InstallGem + b.use UpdateGems end end @@ -50,6 +49,7 @@ module VagrantPlugins autoload :ListPlugins, action_root.join("list_plugins") autoload :PluginExistsCheck, action_root.join("plugin_exists_check") autoload :UninstallPlugin, action_root.join("uninstall_plugin") + autoload :UpdateGems, action_root.join("update_gems") end end end diff --git a/plugins/commands/plugin/action/update_gems.rb b/plugins/commands/plugin/action/update_gems.rb new file mode 100644 index 000000000..57414273f --- /dev/null +++ b/plugins/commands/plugin/action/update_gems.rb @@ -0,0 +1,51 @@ +require "vagrant/plugin/manager" + +module VagrantPlugins + module CommandPlugin + module Action + class UpdateGems + def initialize(app, env) + @app = app + end + + def call(env) + names = env[:plugin_name] || [] + + if names.empty? + env[:ui].info(I18n.t("vagrant.commands.plugin.updating")) + else + env[:ui].info(I18n.t("vagrant.commands.plugin.updating_specific", + names: names.join(", "))) + end + + manager = Vagrant::Plugin::Manager.instance + installed_specs = manager.installed_specs + new_specs = manager.update_plugins(names) + + updated = {} + installed_specs.each do |ispec| + new_specs.each do |uspec| + next if uspec.name != ispec.name + next if ispec.version >= uspec.version + next if updated[uspec.name] && updated[uspec.name].version >= uspec.version + + updated[uspec.name] = uspec + end + end + + if updated.empty? + env[:ui].success(I18n.t("vagrant.commands.plugin.up_to_date")) + end + + updated.values.each do |spec| + env[:ui].success(I18n.t("vagrant.commands.plugin.updated", + name: spec.name, version: spec.version.to_s)) + end + + # Continue + @app.call(env) + end + end + end + end +end diff --git a/plugins/commands/plugin/command/update.rb b/plugins/commands/plugin/command/update.rb index ca4bbb746..a0b832238 100644 --- a/plugins/commands/plugin/command/update.rb +++ b/plugins/commands/plugin/command/update.rb @@ -10,26 +10,18 @@ module VagrantPlugins include MixinInstallOpts def execute - options = {} - opts = OptionParser.new do |o| - o.banner = "Usage: vagrant plugin update [-h]" + o.banner = "Usage: vagrant plugin update [names...] [-h]" o.separator "" - build_install_opts(o, options) end # Parse the options argv = parse_options(opts) return if !argv - raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 # Update the gem action(Action.action_update, { - :plugin_entry_point => options[:entry_point], - :plugin_prerelease => options[:plugin_prerelease], - :plugin_version => options[:plugin_version], - :plugin_sources => options[:plugin_sources], - :plugin_name => argv[0] + :plugin_name => argv, }) # Success, exit status 0 diff --git a/templates/locales/en.yml b/templates/locales/en.yml index a80e8cb7f..b0c3a4b82 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -927,6 +927,14 @@ en: Installing the '%{name}' plugin. This can take a few minutes... uninstalling: |- Uninstalling the '%{name}' plugin... + up_to_date: |- + All plugins are up to date. + updated: |- + Updated '%{name}' to version '%{version}'! + updating: |- + Updating installed plugins... + updating_specific: |- + Updating plugins: %{names}. This may take a few minutes... post_install: |- Post install message from the '%{name}' plugin: