aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Moore <tmoore@incrementalism.net>2014-07-06 18:35:31 +1000
committerTim Moore <tmoore@incrementalism.net>2014-07-30 14:16:35 +1000
commitab78e7449ba1c1024de22ff798c6a32fb581abca (patch)
tree30e77a8e8f10b423874e6c713f1446d803bf8037
parent9af2172e6dc6b29dc24f92a27b7a385faa952ba3 (diff)
downloadbundler-ab78e7449ba1c1024de22ff798c6a32fb581abca.tar.gz
Detect ambiguous gems.
-rw-r--r--lib/bundler/cli/install.rb9
-rw-r--r--lib/bundler/index.rb18
-rw-r--r--lib/bundler/installer.rb9
-rw-r--r--lib/bundler/source/rubygems.rb12
-rw-r--r--spec/install/gems/sources_spec.rb61
5 files changed, 88 insertions, 21 deletions
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index 2f5b490b..64f4803a 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -93,6 +93,15 @@ module Bundler
Bundler.ui.confirm "Post-install message from #{name}:"
Bundler.ui.info msg
end
+ Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
+ Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources."
+ Bundler.ui.error "Installed from: #{installed_from_uri}"
+ Bundler.ui.error "Also found in:"
+ also_found_in_uris.each { |uri| Bundler.ui.error " * #{uri}" }
+ Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source."
+ Bundler.ui.error "For example:"
+ Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'"
+ end
if Bundler.settings[:clean] && Bundler.settings[:path]
require "bundler/cli/clean"
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index 493ac8fa..5af70668 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -10,13 +10,14 @@ module Bundler
i
end
- attr_reader :specs, :sources
- protected :specs
+ attr_reader :specs, :all_specs, :sources
+ protected :specs, :all_specs
def initialize
@sources = []
@cache = {}
@specs = Hash.new { |h,k| h[k] = [] }
+ @all_specs = Hash.new { |h,k| h[k] = [] }
end
def initialize_copy(o)
@@ -24,10 +25,14 @@ module Bundler
@sources = @sources.dup
@cache = {}
@specs = Hash.new { |h,k| h[k] = [] }
+ @all_specs = Hash.new { |h,k| h[k] = [] }
o.specs.each do |name, array|
@specs[name] = array.dup
end
+ o.all_specs.each do |name, array|
+ @all_specs[name] = array.dup
+ end
end
def inspect
@@ -39,6 +44,14 @@ module Bundler
true
end
+ def search_all(name)
+ all_matches = @all_specs[name] + local_search(name)
+ @sources.each do |source|
+ all_matches.concat(source.search_all(name))
+ end
+ all_matches
+ end
+
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
def search(query, base = nil)
@@ -105,6 +118,7 @@ module Bundler
return unless other
other.each do |s|
if (dupes = search_by_spec(s)) && dupes.any?
+ @all_specs[s.name] = [s] + dupes
next unless override_dupes
@specs[s.name] -= dupes
end
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index fe03b615..ed743d40 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -5,7 +5,10 @@ require 'bundler/parallel_workers'
module Bundler
class Installer < Environment
class << self
- attr_accessor :post_install_messages
+ attr_accessor :post_install_messages, :ambiguous_gems
+
+ Installer.post_install_messages = {}
+ Installer.ambiguous_gems = []
end
# Begins the installation process for Bundler.
@@ -75,10 +78,6 @@ module Bundler
unless local
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
end
- # Must install gems in the order that the resolver provides
- # as dependencies might actually affect the installation of
- # the gem.
- Installer.post_install_messages = {}
# the order that the resolver provides is significant, since
# dependencies might actually affect the installation of a gem.
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 5e708793..203473ed 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -72,6 +72,12 @@ module Bundler
# Download the gem to get the spec, because some specs that are returned
# by rubygems.org are broken and wrong.
if spec.source_uri
+ # Check for this spec from other sources
+ uris = [spec.source_uri]
+ uris += source_uris_for_spec(spec)
+ uris.uniq!
+ Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
+
s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"])
spec.__swap__(s)
end
@@ -163,6 +169,12 @@ module Bundler
true
end
+ protected
+
+ def source_uris_for_spec(spec)
+ specs.search_all(spec.name).map{|s| s.source_uri }
+ end
+
private
def cached_gem(spec)
diff --git a/spec/install/gems/sources_spec.rb b/spec/install/gems/sources_spec.rb
index 946eb9d2..632a7ac3 100644
--- a/spec/install/gems/sources_spec.rb
+++ b/spec/install/gems/sources_spec.rb
@@ -4,24 +4,57 @@ describe "bundle install with gems on multiple sources" do
# repo1 is built automatically before all of the specs run
# it contains rack-obama 1.0.0 and rack 0.9.1 & 1.0.0 amongst other gems
- it "searches gem sources from last to first" do
- # Oh no! Someone evil is trying to hijack rack :(
- # need this to be broken to check for correct source ordering
- build_repo gem_repo3 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ context "without source affinity" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", repo3_rack_version do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
end
end
- gemfile <<-G
- source "file://#{gem_repo3}"
- source "file://#{gem_repo1}"
- gem "rack-obama"
- gem "rack"
- G
+ context "when the same version of the same gem is in multiple sources" do
+ let(:repo3_rack_version) { "1.0.0" }
- bundle :install
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack"
+ G
+ end
+
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first" do
+ bundle :install
+
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include("Installed from: file:#{gem_repo1}")
+ should_be_installed("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "when different versions of the same gem are in multiple sources" do
+ let(:repo3_rack_version) { "1.2" }
- should_be_installed("rack-obama 1.0.0", "rack 1.0.0")
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0" # force it to install the working version in repo1
+ G
+ end
+
+ it "warns about ambiguous gems, but installs anyway" do
+ bundle :install
+
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include("Installed from: file:#{gem_repo1}")
+ should_be_installed("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
end
end