lib/bundler/definition.rb in bundler-1.12.6 vs lib/bundler/definition.rb in bundler-1.13.0.pre.1

- old
+ new

@@ -83,14 +83,21 @@ @locked_sources = [] end @unlock[:gems] ||= [] @unlock[:sources] ||= [] + @unlock[:ruby] ||= if @ruby_version && @locked_ruby_version + unless locked_ruby_version_object = RubyVersion.from_string(@locked_ruby_version) + raise LockfileError, "Failed to create a `RubyVersion` object from " \ + "`#{@locked_ruby_version}` found in #{lockfile} -- try running `bundle update --ruby`." + end + @ruby_version.diff(locked_ruby_version_object) + end + @unlocking ||= @unlock[:ruby] || (!@locked_ruby_version ^ !@ruby_version) current_platform = Bundler.rubygems.platforms.map {|p| generic(p) }.compact.last - @new_platform = !@platforms.include?(current_platform) - @platforms |= [current_platform] + add_platform(current_platform) @path_changes = converge_paths eager_unlock = expand_dependencies(@unlock[:gems]) @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name) @@ -135,12 +142,21 @@ # 2. After that it tries and fetches gemspec of resolved dependencies # # @return [Bundler::SpecSet] def specs @specs ||= begin - specs = resolve.materialize(Bundler.settings[:cache_all_platforms] ? dependencies : requested_dependencies) - + begin + specs = resolve.materialize(Bundler.settings[:cache_all_platforms] ? dependencies : requested_dependencies) + rescue GemNotFound => e # Handle yanked gem + gem_name, gem_version = extract_gem_info(e) + locked_gem = @locked_specs[gem_name].last + raise if locked_gem.nil? || locked_gem.version.to_s != gem_version + raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \ + "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \ + "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \ + "to a different version of #{locked_gem} that hasn't been removed in order to install." + end unless specs["bundler"].any? local = Bundler.settings[:frozen] ? rubygems_index : index bundler = local.search(Gem::Dependency.new("bundler", VERSION)).last specs["bundler"] = bundler if bundler end @@ -165,10 +181,16 @@ missing = [] resolve.materialize(requested_dependencies, missing) missing end + def missing_dependencies + missing = [] + resolve.materialize(current_dependencies, missing) + missing + end + def requested_specs @requested_specs ||= begin groups = requested_groups groups.map!(&:to_sym) specs_for(groups) @@ -192,13 +214,15 @@ # @return [SpecSet] resolved dependencies def resolve @resolve ||= begin last_resolve = converge_locked_specs if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?) + Bundler.ui.debug("Found no changes, using resolution from the lockfile") last_resolve else # Run a resolve against the locally available gems + Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies") last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, ruby_version) end end end @@ -208,11 +232,11 @@ sources.all_sources.each do |source| source.dependency_names = dependency_names.dup idx.add_source source.specs dependency_names -= pinned_spec_names(source.specs) - dependency_names.push(*source.unmet_deps).uniq! + dependency_names.concat(source.unmet_deps).uniq! end end end # used when frozen is enabled so we can find the bundler @@ -350,28 +374,34 @@ added = [] deleted = [] changed = [] gemfile_sources = sources.lock_sources - if @locked_sources != gemfile_sources - new_sources = gemfile_sources - @locked_sources - deleted_sources = @locked_sources - gemfile_sources + new_sources = gemfile_sources - @locked_sources + deleted_sources = @locked_sources - gemfile_sources + + new_deps = @dependencies - @locked_deps + deleted_deps = @locked_deps - @dependencies + + # Check if it is possible that the source is only changed thing + if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?) + new_sources.reject! {|source| source.is_a_path? && source.path.exist? } + deleted_sources.reject! {|source| source.is_a_path? && source.path.exist? } + end + + if @locked_sources != gemfile_sources if new_sources.any? added.concat new_sources.map {|source| "* source: #{source}" } end if deleted_sources.any? deleted.concat deleted_sources.map {|source| "* source: #{source}" } end end - new_deps = @dependencies - @locked_deps - deleted_deps = @locked_deps - @dependencies - added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any? - if deleted_deps.any? deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } end both_sources = Hash.new {|h, k| h[k] = [] } @@ -416,10 +446,15 @@ raise RubyVersionMismatch, msg end end + def add_platform(platform) + @new_platform ||= !@platforms.include?(platform) + @platforms |= [platform] + end + attr_reader :sources private :sources private @@ -445,20 +480,27 @@ unlocking = @locked_specs.any? do |locked_spec| locked_spec.source.class == locked.class && locked_spec.source != locked end end - !locked || unlocking || dependencies_for_source_changed?(locked) || source.specs != locked.specs + !locked || unlocking || dependencies_for_source_changed?(source) || specs_for_source_changed?(source) end def dependencies_for_source_changed?(source) deps_for_source = @dependencies.select {|s| s.source == source } locked_deps_for_source = @locked_deps.select {|s| s.source == source } - deps_for_source != locked_deps_for_source + Set.new(deps_for_source) != Set.new(locked_deps_for_source) end + def specs_for_source_changed?(source) + locked_index = Index.new + locked_index.use(@locked_specs.select {|s| source.can_lock?(s) }) + + source.specs != locked_index + end + # Get all locals and override their matching sources. # Return true if any of the locals changed (for example, # they point to a new revision) or depend on new specs. def converge_locals locals = [] @@ -521,11 +563,19 @@ changes end def converge_dependencies (@dependencies + @locked_deps).each do |dep| - dep.source = sources.get(dep.source) if dep.source + locked_source = @locked_deps.select {|d| d.name == dep.name }.last + # This is to make sure that if bundler is installing in deployment mode and + # after locked_source and sources don't match, we still use locked_source. + if Bundler.settings[:frozen] && !locked_source.nil? && + locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist? + dep.source = locked_source.source + elsif dep.source + dep.source = sources.get(dep.source) + end end Set.new(@dependencies) != Set.new(@locked_deps) end # Remove elements from the locked specs that are expired. This will most @@ -570,11 +620,11 @@ # XXX This is a backwards-compatibility fix to preserve the ability to # unlock a single gem by passing its name via `--source`. See issue #3759 next if s.source.nil? || @unlock[:sources].include?(s.name) # If the spec is from a path source and it doesn't exist anymore - # then we just unlock it. + # then we unlock it. # Path sources have special logic if s.source.instance_of?(Source::Path) other = s.source.specs[s].first @@ -681,8 +731,14 @@ whitespace_cleanup = /\n{2,}/ current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip proposed = proposed.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip end current == proposed + end + + def extract_gem_info(error) + # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources" + # to an array. The first element will be the gem name (e.g. foo), the second will be the version number. + error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten end end end