summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rodríguez <[email protected]>2025-02-13 19:08:16 +0100
committerHiroshi SHIBATA <[email protected]>2025-02-18 12:12:54 +0900
commit507de2226bcc75b1d0d8c1736cdbe62e46a97916 (patch)
tree7dd7d53468a94956a81d5d784a1d295d9321369d
parent203a570f68b43ce591c5ef1784112725fa0692cd (diff)
[rubygems/rubygems] Fix Bundler incorrectly downgrading direct dependencies
There's no reason to call `converge_specs` when adding additional lower bound requirements to prevent downgrades, and it actually causes the extra requirements to be missed sometimes. Loop over the originally locked specs directly, adding the additional precaution of not adding the requirement if the Gemfile dependency has changed and it no longer matches the locked spec. https://github.com/rubygems/rubygems/commit/5154506912
-rw-r--r--lib/bundler/definition.rb14
-rw-r--r--spec/bundler/commands/update_spec.rb66
2 files changed, 75 insertions, 5 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 61add4be10..24dae86493 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -938,7 +938,7 @@ module Bundler
def converge_dependencies
@missing_lockfile_dep = nil
- changes = false
+ @changed_dependencies = []
current_dependencies.each do |dep|
if dep.source
@@ -960,10 +960,10 @@ module Bundler
end
end
- changes ||= dep_changed
+ @changed_dependencies << name if dep_changed
end
- changes
+ @changed_dependencies.any?
end
# Remove elements from the locked specs that are expired. This will most
@@ -1095,9 +1095,13 @@ module Bundler
def additional_base_requirements_to_prevent_downgrades(resolution_packages)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
- converge_specs(@originally_locked_specs).each do |locked_spec|
+ @originally_locked_specs.each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
- resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
+
+ name = locked_spec.name
+ next if @changed_dependencies.include?(name)
+
+ resolution_packages.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
end
resolution_packages
end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 058cd2ec82..f6d0188794 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -428,6 +428,72 @@ RSpec.describe "bundle update" do
expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
end
+ it "does not downgrade direct dependencies unnecessarily" do
+ build_repo4 do
+ build_gem "redis", "4.8.1"
+ build_gem "redis", "5.3.0"
+
+ build_gem "sidekiq", "6.5.5" do |s|
+ s.add_dependency "redis", ">= 4.5.0"
+ end
+
+ build_gem "sidekiq", "6.5.12" do |s|
+ s.add_dependency "redis", ">= 4.5.0", "< 5"
+ end
+
+ # one version of sidekiq above Gemfile's range is needed to make the
+ # resolver choose `redis` first and trying to upgrade it, reproducing
+ # the accidental sidekiq downgrade as a result
+ build_gem "sidekiq", "7.0.0 " do |s|
+ s.add_dependency "redis", ">= 4.2.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.0" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.4" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "redis"
+ gem "sidekiq", "~> 6.5"
+ gem "sentry-sidekiq"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ redis (4.8.1)
+ sentry-sidekiq (5.22.0)
+ sidekiq (>= 3.0)
+ sidekiq (6.5.12)
+ redis (>= 4.5.0, < 5)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ redis
+ sentry-sidekiq
+ sidekiq (~> 6.5)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ bundle "lock --update sentry-sidekiq"
+
+ expect(lockfile).to eq(original_lockfile.sub("sentry-sidekiq (5.22.0)", "sentry-sidekiq (5.22.4)"))
+ end
+
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|