From 891246c3865ed0af7e277ca50c079f466d035f7c Mon Sep 17 00:00:00 2001
From: Jarek Prokop <jprokop@redhat.com>
Date: Thu, 1 Jun 2023 13:22:24 +0200
Subject: [PATCH] Backport "Fix another issue of Bundler not falling back to an
 installable candidate"

In this case, when materializing a legacy lockfile using only "ruby"
platform, and in frozen mode.

=====

Commit adapted from: https://github.com/rubygems/rubygems/pull/6261
Specific PR's commit hash: 93af31aaaed75e9aa9cb182d2f0c2100918ed7ac
---
 bundler/lib/bundler/lazy_specification.rb     | 11 +++--
 .../install/gemfile/specific_platform_spec.rb | 41 ++++++++++++-------
 2 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/bundler/lib/bundler/lazy_specification.rb b/bundler/lib/bundler/lazy_specification.rb
index e8bee25ab..a65020e6c 100644
--- a/bundler/lib/bundler/lazy_specification.rb
+++ b/bundler/lib/bundler/lazy_specification.rb
@@ -85,7 +85,7 @@ def materialize_for_installation
 
                      installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
 
-                     specification = __materialize__(installable_candidates)
+                     specification = __materialize__(installable_candidates, :fallback_to_non_installable => false)
                      return specification unless specification.nil?
 
                      if target_platform != platform
@@ -98,13 +98,18 @@ def materialize_for_installation
       __materialize__(candidates)
     end
 
-    def __materialize__(candidates)
+    # If in frozen mode, we fallback to a non-installable candidate because by
+    # doing this we avoid re-resolving and potentially end up changing the
+    # lock file, which is not allowed. In that case, we will give a proper error
+    # about the mismatch higher up the stack, right before trying to install the
+    # bad gem.
+    def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
       search = candidates.reverse.find do |spec|
         spec.is_a?(StubSpecification) ||
           (spec.matches_current_ruby? &&
            spec.matches_current_rubygems?)
       end
-      if search.nil? && Bundler.frozen_bundle?
+      if search.nil? && fallback_to_non_installable
         search = candidates.last
       else
         search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
diff --git a/bundler/spec/install/gemfile/specific_platform_spec.rb b/bundler/spec/install/gemfile/specific_platform_spec.rb
index a29446305..e35d8bc16 100644
--- a/bundler/spec/install/gemfile/specific_platform_spec.rb
+++ b/bundler/spec/install/gemfile/specific_platform_spec.rb
@@ -104,40 +104,53 @@
       L
     end
 
-    it "still installs the generic RUBY variant if necessary even when running on a legacy lockfile locked only to RUBY" do
-      build_repo4 do
-        build_gem "nokogiri", "1.3.10"
-        build_gem "nokogiri", "1.3.10" do |s|
-          s.platform = "arm64-darwin"
-          s.required_ruby_version = "< #{Gem.ruby_version}"
+    context "when running on a legacy lockfile locked only to RUBY" do
+      around do |example|
+        build_repo4 do
+          build_gem "nokogiri", "1.3.10"
+          build_gem "nokogiri", "1.3.10" do |s|
+            s.platform = "arm64-darwin"
+            s.required_ruby_version = "< #{Gem.ruby_version}"
+          end
+
+          build_gem "bundler", "2.1.4"
         end
 
-        build_gem "bundler", "2.1.4"
-      end
-
-      gemfile <<~G
+        gemfile <<~G
         source "#{file_uri_for(gem_repo4)}"
+
         gem "nokogiri"
-      G
+        G
 
-      lockfile <<-L
+        lockfile <<-L
         GEM
           remote: #{file_uri_for(gem_repo4)}/
           specs:
             nokogiri (1.3.10)
+
         PLATFORMS
           ruby
+
         DEPENDENCIES
           nokogiri
+
         RUBY VERSION
           2.5.3p105
+
         BUNDLED WITH
            2.1.4
-      L
+        L
 
-      simulate_platform "arm64-darwin-22" do
+        simulate_platform "arm64-darwin-22", &example
+      end
+
+      it "still installs the generic RUBY variant if necessary" do
         bundle "update --bundler", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
       end
+
+      it "still installs the generic RUBY variant if necessary, even in frozen mode" do
+        bundle "update --bundler", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" }
+      end
     end
 
     it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
-- 
2.41.0.rc1

