diff options
author | David RodrÃguez <deivid.rodriguez@riseup.net> | 2023-03-23 20:33:44 +0100 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2023-03-28 15:25:41 +0900 |
commit | ed868f9a71707d5f51caddbf345e0fde8c940c42 (patch) | |
tree | 6f9d624cf89106bcf9e455e6ae463d17aff49fed /lib/bundler/resolver | |
parent | eaddd386ecf3cf7efb0857e07dbcd66776dd4954 (diff) | |
download | ruby-ed868f9a71707d5f51caddbf345e0fde8c940c42.tar.gz |
[rubygems/rubygems] Fix unnecessary downgrade of top level dependency when unlocking
Bundler is very conservative by default, trying to preserve versions
from the lockfile as possible, and never downgrading them. However, when
it runs into a resolution error, it still tries to find a valid
resolution.
This fallback behavior was too "brute-force" though, completely
unrestricting any gem found in the resolution conflict, and that could
lead to direct dependencies being downgraded in some edge cases.
Instead, unlock things a bit more carefully:
* First try unlocking fully pinned indirect dependencies, but leave a
lower bound requirement in place to prevent downgrades.
* Then try unlocking any fully pinned dependency, also leaving a lower
bound requirement in place.
* Finally completely unrestrict dependencies if nothing else worked.
https://github.com/rubygems/rubygems/commit/7f55ed8302
Diffstat (limited to 'lib/bundler/resolver')
-rw-r--r-- | lib/bundler/resolver/base.rb | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb index ad19577971..e9ff43960c 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -51,10 +51,18 @@ module Bundler end def unlock_names(names) - names.each do |name| - @base.delete_by_name(name) + indirect_pins = indirect_pins(names) - @base_requirements.delete(name) + if indirect_pins.any? + loosen_names(indirect_pins) + else + pins = pins(names) + + if pins.any? + loosen_names(pins) + else + unrestrict_names(names) + end end end @@ -66,6 +74,30 @@ module Bundler private + def indirect_pins(names) + names.select {|name| @base_requirements[name].exact? && @requirements.none? {|dep| dep.name == name } } + end + + def pins(names) + names.select {|name| @base_requirements[name].exact? } + end + + def loosen_names(names) + names.each do |name| + version = @base_requirements[name].requirements.first[1] + + @base_requirements[name] = Gem::Requirement.new(">= #{version}") + + @base.delete_by_name(name) + end + end + + def unrestrict_names(names) + names.each do |name| + @base_requirements.delete(name) + end + end + def build_base_requirements base_requirements = {} @base.each do |ls| |