Exempt lockfile versions from cooldown on every resolution path#9619
Exempt lockfile versions from cooldown on every resolution path#9619hsbt wants to merge 2 commits into
Conversation
bundle update GEM computes its target versions with an auxiliary full-update resolution that carries no prevent-downgrade floor, so the floor-based exemption from #9599 never applied there. A locked gem whose every published version sits inside the cooldown window then resolved to an empty candidate set, failing updates that never touched it. Key the exemption off the lockfile contents, which every resolution path can see; gems explicitly named on the command line stay subject to cooldown. #9613 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a Bundler cooldown edge case where a gem already pinned in Gemfile.lock could be filtered out during certain resolution passes (notably the auxiliary full-update pass used to compute bundle update GEM targets), leading to an empty candidate set and failing updates unrelated to that gem. The fix makes the cooldown exemption key off lockfile contents (while keeping explicitly-updated gems subject to cooldown), and adds a regression spec.
Changes:
- Add a new end-to-end spec covering the “locked gem entirely within cooldown window” failure mode during an unrelated
--update. - Thread “explicitly unlocked gems” metadata into the resolver base so cooldown logic can distinguish command-line update targets.
- Change cooldown filtering to exempt versions present in the lockfile (except for explicitly updated gems), independent of the prevent-downgrade floor behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| spec/install/cooldown_spec.rb | Adds an integration regression test ensuring lockfile-pinned “fresh” gems don’t cause unrelated updates to fail under cooldown. |
| bundler/lib/bundler/resolver/base.rb | Plumbs explicit_unlocks into the resolver base and exposes explicitly_unlocked? for cooldown decisions. |
| bundler/lib/bundler/resolver.rb | Switches cooldown exemption logic to be lockfile-based (and respects explicitly unlocked gems). |
| bundler/lib/bundler/definition.rb | Passes @explicit_unlocks into resolver base creation so all resolution paths can apply the same cooldown exemption rules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def locked_by_lockfile?(spec) | ||
| return false unless defined?(@base) && @base | ||
| requirement = base_requirements[spec.name] | ||
| return false unless requirement && !requirement.exact? | ||
| requirement.requirements.any? {|op, version| op == ">=" && version == spec.version } | ||
| return false if @base.explicitly_unlocked?(spec.name) | ||
| @base.locked_specs[spec.name].any? {|locked| locked.version == spec.version } | ||
| end |
bundle update GEM already pins that an explicitly updated gem stays subject to cooldown and moves back to the newest aged version. Pin the same behavior through bundle lock --update, which shares the explicit-unlock machinery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
hi, thanks @hsbt for quick fix i can confirm that it works in my scenario, checked like this: # 1. Get the PR (gh resolves the fork remote)
git clone --depth 1 https://github.com/ruby/rubygems.git /tmp/rubygems-9619
cd /tmp/rubygems-9619 && gh pr checkout 9619
# 2. Build & install bundler from the PR
cd bundler && gem build bundler.gemspec && gem install ./bundler-*.gem
PRV=$(ruby -e 'load "lib/bundler/version.rb"; puts Bundler::VERSION')
echo "PR bundler version: $PRV"
# 3. Run THAT bundler against the command that fails on 4.0.14, cooldown ON
cd /path/to/my/project
bundle _"$PRV"_ lock --update sentry-rails --conservative # success
grep 'anyway_app_config (' Gemfile.lock # 0.3.2 kepthoping to see it soon in next bundler release |
What was the end-user or developer problem that led to this PR?
bundle update GEMcomputes its target versions with an auxiliary full-update resolution that carries no prevent-downgrade floor, so the floor-based exemption from #9599 never applied there.A locked gem whose every published version sits inside the cooldown window then resolved to an empty candidate set, failing updates that never touched it.
Fixes #9613
What is your fix for the problem, implemented in this PR?
Key the exemption off the lockfile contents, which every resolution path can see; gems explicitly named on the command line stay subject to cooldown.
Make sure the following tasks are checked