diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 08f6c37de9..8659c64849 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -144,6 +144,8 @@ module Bundler @dependency_changes = converge_dependencies @local_changes = converge_locals + + @incomplete_lockfile = check_missing_lockfile_specs end def gem_version_promoter @@ -458,7 +460,7 @@ module Bundler private :sources def nothing_changed? - !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes + !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@incomplete_lockfile end def unlocking? @@ -603,6 +605,7 @@ module Bundler [@new_platform, "you added a new platform to your gemfile"], [@path_changes, "the gemspecs for path gems changed"], [@local_changes, "the gemspecs for git local gems changed"], + [@incomplete_lockfile, "your lock file is missing some gems"], ].select(&:first).map(&:last).join(", ") end @@ -657,6 +660,14 @@ module Bundler !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty? end + def check_missing_lockfile_specs + all_locked_specs = @locked_specs.map(&:name) << "bundler" + + @locked_specs.any? do |s| + s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) } + end + end + def converge_paths sources.path_sources.any? do |source| specs_changed?(source) diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index 5b6680e5e1..dce7133769 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -53,10 +53,6 @@ module Bundler @dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep } end - def missing_lockfile_dependencies(all_spec_names) - dependencies.reject {|dep| all_spec_names.include? dep.name } - end - # Represents all dependencies def all_dependencies @spec.dependencies @@ -84,8 +80,6 @@ module Bundler end def call - check_for_corrupt_lockfile - if @rake do_install(@rake, 0) Gem::Specification.reset @@ -128,31 +122,6 @@ module Bundler Bundler.ui.warn(warning.join("\n")) end - def check_for_corrupt_lockfile - missing_dependencies = @specs.map do |s| - [ - s, - s.missing_lockfile_dependencies(@specs.map(&:name)), - ] - end.reject {|a| a.last.empty? } - return if missing_dependencies.empty? - - warning = [] - warning << "Your lockfile was created by an old Bundler that left some things out." - if @size != 1 - warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time." - @size = 1 - end - warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile." - warning << "The missing gems are:" - - missing_dependencies.each do |spec, missing| - warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}" - end - - Bundler.ui.warn(warning.join("\n")) - end - private def failed_specs diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb index e680633862..f67d03356d 100644 --- a/spec/bundler/bundler/installer/parallel_installer_spec.rb +++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb @@ -11,40 +11,6 @@ RSpec.describe Bundler::ParallelInstaller do subject { described_class.new(installer, all_specs, size, standalone, force) } - context "when dependencies that are not on the overall installation list are the only ones not installed" do - let(:all_specs) do - [ - build_spec("alpha", "1.0") {|s| s.runtime "a", "1" }, - ].flatten - end - - it "prints a warning" do - expect(Bundler.ui).to receive(:warn).with(<<-W.strip) -Your lockfile was created by an old Bundler that left some things out. -You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile. -The missing gems are: -* a depended upon by alpha - W - subject.check_for_corrupt_lockfile - end - - context "when size > 1" do - let(:size) { 500 } - - it "prints a warning and sets size to 1" do - expect(Bundler.ui).to receive(:warn).with(<<-W.strip) -Your lockfile was created by an old Bundler that left some things out. -Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time. -You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile. -The missing gems are: -* a depended upon by alpha - W - subject.check_for_corrupt_lockfile - expect(subject.size).to eq(1) - end - end - end - context "when the spec set is not a valid resolution" do let(:all_specs) do [ diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 29f8863591..cd603e336a 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1221,6 +1221,59 @@ RSpec.describe "the lockfile format" do and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.") end + it "auto-heals when the lockfile is missing specs" do + build_repo4 do + build_gem "minitest-bisect", "1.6.0" do |s| + s.add_dependency "path_expander", "~> 1.1" + end + + build_gem "path_expander", "1.1.1" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "minitest-bisect" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + minitest-bisect (1.6.0) + path_expander (~> 1.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + minitest-bisect + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install --verbose" + expect(out).to include("re-resolving dependencies because your lock file is missing some gems") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + minitest-bisect (1.6.0) + path_expander (~> 1.1) + path_expander (1.1.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + minitest-bisect + + BUNDLED WITH + #{Bundler::VERSION} + L + end + describe "a line ending" do def set_lockfile_mtime_to_known_value time = Time.local(2000, 1, 1, 0, 0, 0)