diff --git a/bundler/lib/bundler/plugin.rb b/bundler/lib/bundler/plugin.rb index e3f698ec43b3..95f9d3894672 100644 --- a/bundler/lib/bundler/plugin.rb +++ b/bundler/lib/bundler/plugin.rb @@ -39,8 +39,9 @@ def install(names, options) raise InvalidOption, "You cannot specify `--branch` and `--ref` at the same time." if options["branch"] && options["ref"] specs = Installer.new.install(names, options) + plugins = plugins_to_save(names, specs) - save_plugins names, specs + save_plugins(plugins, specs) rescue PluginError specs_to_delete = specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) } specs_to_delete.each_value {|spec| Bundler.rm_rf(spec.full_gem_path) } @@ -113,10 +114,10 @@ def gemfile_install(gemfile = nil, &inline) return if definition.dependencies.empty? - plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p } installed_specs = Installer.new.install_definition(definition) + plugins = plugins_to_save(definition.dependencies.map(&:name), installed_specs) - save_plugins plugins, installed_specs, builder.inferred_plugins + save_plugins(plugins, installed_specs, builder.inferred_plugins) end rescue RuntimeError => e unless e.is_a?(GemfileError) @@ -142,6 +143,15 @@ def root end end + # Return array of plugins that we need save or update the path + # based if was not installed with that version yet + def plugins_to_save(plugins, installed_specs) + plugins.reject do |p| + installed_spec = installed_specs[p] + installed_spec.nil? || index.version_already_installed?(p, installed_spec.version) + end + end + def local_root Bundler.app_config_path.join("plugin") end @@ -253,7 +263,7 @@ def loaded?(plugin) # @param [Array] names of inferred source plugins that can be ignored def save_plugins(plugins, specs, optional_plugins = []) plugins.each do |name| - next if index.installed?(name) + next if index.version_already_installed?(name, specs[name].version) spec = specs[name] diff --git a/bundler/lib/bundler/plugin/index.rb b/bundler/lib/bundler/plugin/index.rb index c2ab8f90da4f..724ee708a592 100644 --- a/bundler/lib/bundler/plugin/index.rb +++ b/bundler/lib/bundler/plugin/index.rb @@ -111,6 +111,15 @@ def command_plugin(command) @commands[command] end + # Plugin version is already installed? + def version_already_installed?(name, version) + plugin_path = @plugin_paths[name] + return false unless plugin_path + + File.basename(plugin_path) == "#{name}-#{version}" + end + + # Plugin is already installed? def installed?(name) @plugin_paths[name] end diff --git a/bundler/lib/bundler/plugin/installer.rb b/bundler/lib/bundler/plugin/installer.rb index c9ff12ce4b71..c26500f732b2 100644 --- a/bundler/lib/bundler/plugin/installer.rb +++ b/bundler/lib/bundler/plugin/installer.rb @@ -100,13 +100,24 @@ def install_from_specs(specs) paths = {} specs.each do |spec| - spec.source.install spec + next if version_already_installed?(spec.name, spec.version) + spec.source.install spec paths[spec.name] = spec end paths end + + # Check if the Plugin version is already installed or has a diff version to install + # + # @param [String] name of the plugin gem to search in the source + # @param [String] version of the gem to install + # + # @return [Boolean] true if plugin version already installed + def version_already_installed?(plugin, version) + Index.new.version_already_installed?(plugin, version) + end end end end diff --git a/bundler/spec/plugins/install_spec.rb b/bundler/spec/plugins/install_spec.rb index aec131f2eeb2..01707d20c71b 100644 --- a/bundler/spec/plugins/install_spec.rb +++ b/bundler/spec/plugins/install_spec.rb @@ -89,7 +89,7 @@ expect(out).to include("Installing foo 1.1") bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --verbose" - expect(out).to include("Using foo 1.1") + expect(out).to_not include("foo 1.1") end it "installs when --branch specified" do @@ -232,6 +232,117 @@ def exec(command, args) plugin_should_be_installed("foo") end + it "upgrade plugins version listed in gemfile" do + update_repo2 do + build_plugin "foo", "1.4.0" + build_plugin "foo", "1.5.0" + end + + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo', "1.4.0" + gem 'rack', "1.0.0" + G + + bundle "install" + + expect(out).to include("Installing foo 1.4.0") + expect(out).to include("Installed plugin foo") + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed_with_version("foo", "1.4.0") + + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo', "1.5.0" + gem 'rack', "1.0.0" + G + + bundle "install" + + expect(out).to include("Installing foo 1.5.0") + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed_with_version("foo", "1.5.0") + end + + it "downgrade plugins version listed in gemfile" do + update_repo2 do + build_plugin "foo", "1.4.0" + build_plugin "foo", "1.5.0" + end + + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo', "1.5.0" + gem 'rack', "1.0.0" + G + + bundle "install" + + expect(out).to include("Installing foo 1.5.0") + expect(out).to include("Installed plugin foo") + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed_with_version("foo", "1.5.0") + + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo', "1.4.0" + gem 'rack', "1.0.0" + G + + bundle "install" + + expect(out).to include("Installing foo 1.4.0") + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed_with_version("foo", "1.4.0") + end + + it "install only plugins not installed yet listed in gemfile" do + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo' + gem 'rack', "1.0.0" + G + + 2.times { bundle "install" } + + expect(out).to_not include("Installing foo") + expect(out).to_not include("Installed plugin foo") + + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed("foo") + + gemfile <<-G + source '#{file_uri_for(gem_repo2)}' + plugin 'foo' + plugin 'kung-foo' + gem 'rack', "1.0.0" + G + + bundle "install" + + expect(out).to include("Installing kung-foo") + expect(out).to include("Installed plugin kung-foo") + + expect(out).to_not include("Installing foo") + expect(out).to_not include("Installed plugin foo") + + expect(out).to include("Bundle complete!") + + expect(the_bundle).to include_gems("rack 1.0.0") + plugin_should_be_installed("foo") + plugin_should_be_installed("kung-foo") + end + it "accepts plugin version" do update_repo2 do build_plugin "foo", "1.1.0" diff --git a/bundler/spec/support/matchers.rb b/bundler/spec/support/matchers.rb index ea7c78468351..c2d2abd5321f 100644 --- a/bundler/spec/support/matchers.rb +++ b/bundler/spec/support/matchers.rb @@ -228,6 +228,14 @@ def plugin_should_be_installed(*names) end end + def plugin_should_be_installed_with_version(name, version) + expect(Bundler::Plugin).to be_installed(name) + path = Pathname.new(Bundler::Plugin.installed?(name)) + + expect(File.basename(path)).to eq("#{name}-#{version}") + expect(path + "plugins.rb").to exist + end + def plugin_should_not_be_installed(*names) names.each do |name| expect(Bundler::Plugin).not_to be_installed(name)