mattbrictson / bundle_update_interactive

A stylish interactive mode for Bundler, inspired by `yarn upgrade-interactive`
MIT License
137 stars 3 forks source link

Add `--latest` option that modifies Gemfile to allow latest updates #42

Closed mattbrictson closed 3 weeks ago

mattbrictson commented 3 weeks ago

For this PR I am introducing a new option: --latest. Similar yarn upgrade-interactive --latest, it relaxes Gemfile version requirements to allow gems to be updated to their latest versions. This feature was originally requested in #13.


Normally update-interactive only makes changes to your Gemfile.lock. It honors the version restrictions ("pins") in your Gemfile and will not update your Gemfile.lock to have versions that are not allowed. However with the --latest flag, update-interactive can update the version pins in your Gemfile as well. Consider the following Gemfile:

gem "rails", "~> 7.1.0"

Normally running bundle update-interactive will report that Rails is held back and therefore cannot be updated to the latest version. However, if you pass the --latest option like this:

bundle update-interactive --latest

Now Rails will be allowed to update. If you select to update Rails to the latest version (e.g. 7.2.0), update-interactive will modify the version requirement in your Gemfile to look like this:

gem "rails", "~> 7.2.0"

In terms of implementation, this PR makes a refactor to introduce an Updater class. I consolidated the core of the reporting and bundle-updating logic in this single class. See https://github.com/mattbrictson/bundle_update_interactive/pull/42/commits/65197475517676917591bf91a81077a03966a643 for details.

Next, I created a Latest::Updater subclass of Updater that tweaks that logic for allowing latest versions. It does this by wrapping the core logic with a GemfileEditor that modifies the Gemfile to relax its version requirements prior to bundle lock --update and bundle update being called.

Once bundle update completes, I modify the Gemfile once more to "shift" the version requirements to match the updated versions. E.g. ~> 7.1.0 becomes ~> 7.2.0.

I put the latest logic, GemfileEditor, etc. code all into a Latest module, to prevent its complexity from leaking into other parts of the codebase. The only time this code is referenced outside the module is a 1-liner in the CLI based on the --latest flag:

updater_class = options.latest? ? Latest::Updater : Updater

Although there is a lot of code in this PR, most of it comes from the --update-gemfile feature that I previously built and tested in my bundleup project here: https://github.com/mattbrictson/bundleup/pull/87 .