diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 32537023789c..a2b45550703a 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -198,7 +198,7 @@ def setup_domain!(options = {}) sources.cached! - if options[:add_checksums] || (!options[:local] && (install_needed? || refetch_needed?(options))) + if options[:add_checksums] || (!options[:local] && (install_needed? || refetch_needed?(options) || @locked_spec_with_empty_checksums)) sources.remote! true else @@ -650,7 +650,7 @@ def something_changed? @missing_lockfile_dep || @unlocking_bundler || @locked_spec_with_missing_checksums || - @locked_spec_with_empty_checksums || + empty_checksums_actionable? || @locked_spec_with_missing_deps || @locked_spec_with_invalid_deps end @@ -659,6 +659,16 @@ def resolve_needed? unlocking? || something_changed? end + # Only a remote fetch can fill an empty CHECKSUMS entry, so it justifies a + # resolution only when one is coming. Resolving locally for it would repeat + # on every `Bundler.setup` without changing the lockfile. Frozen mode still + # has to refuse the entry. + def empty_checksums_actionable? + return false unless @locked_spec_with_empty_checksums + + Bundler.frozen_bundle? || !sources.local_mode? + end + def should_add_extra_platforms? !lockfile_exists? && Bundler::MatchPlatform.generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform] end diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb index 3ee0705c652e..8294b1e9ca45 100644 --- a/spec/lock/lockfile_spec.rb +++ b/spec/lock/lockfile_spec.rb @@ -1682,6 +1682,36 @@ expect(the_bundle).not_to include_gems "myrack 0.9.1" end + it "fills empty CHECKSUMS entries when not frozen, even if every gem is already installed" do + system_gems "myrack-0.9.1", gem_repo: gem_repo2 + + lockfile <<-L + GEM + remote: https://gem.repo2/ + specs: + myrack (0.9.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + myrack + + CHECKSUMS + myrack (0.9.1) + + BUNDLED WITH + #{Bundler::VERSION} + L + + install_gemfile <<-G + source "https://gem.repo2" + gem "myrack" + G + + expect(lockfile).to include(" #{checksum_to_lock(gem_repo2, "myrack", "0.9.1")}\n") + end + it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do build_repo4 do build_gem "other_dep", "0.9" diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index 2f5426d4f3c7..c17662d05165 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -259,6 +259,24 @@ def clean_load_path(lp) expect(bundled_app_lock).to exist end + it "does not resolve again for the empty CHECKSUMS entries it locked itself" do + system_gems "myrack-1.0.0" + + gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + ruby "require 'bundler'; Bundler.setup" + expect(out).to include("Resolving dependencies...") + lockfile = File.read(bundled_app_lock) + expect(lockfile).to include("CHECKSUMS\n myrack (1.0.0)\n") + + ruby "require 'bundler'; Bundler.setup" + expect(out).not_to include("Resolving dependencies...") + expect(File.read(bundled_app_lock)).to eq(lockfile) + end + describe "$BUNDLE_GEMFILE" do context "user provides an absolute path" do it "uses BUNDLE_GEMFILE to locate the gemfile if present" do