Support force_version of gems to override dependency conflicts#4178
Support force_version of gems to override dependency conflicts#4178lukaso wants to merge 5 commits into
Conversation
|
Thanks for opening a pull request and helping make RubyGems and Bundler better! Someone from the RubyGems team will take a look at your pull request shortly and leave any feedback. Please make sure that your pull request has tests for any changes or added functionality. We use GitHub Actions to test and make sure your change works functionally and uses acceptable conventions, you can review the current progress of GitHub Actions in the PR status window below. If you have any questions or concerns that you wish to ask, feel free to leave a comment in this PR or join our #rubygems or #bundler channel on Slack. For more information about contributing to the RubyGems project feel free to review our CONTRIBUTING guide |
|
I will look to add a message in the |
b29379a to
7783eac
Compare
Link to POC rubygems/bundler#5670 Co-authored-by: Samuel Giddins <segiddins@segiddins.me>
7783eac to
6244fed
Compare
6244fed to
4fb53e8
Compare
I feel like this is a much improved version of the PR (thanks for the invaluable feedback @simi). |
|
Maybe passing the exact version as the gem "minitest", "~> 5.14", force_version: "5.14.2" |
| @gem_version_promoter.prerelease_specified = @prerelease_specified = {} | ||
| requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? } | ||
|
|
||
| @forced_requirements = requirements.select(&:force_version?).map {|dep| [dep.name, dep] }.to_h |
There was a problem hiding this comment.
I think, if I understand this new approach, you'd need to handle required_ruby_version and required_rubygems_version specially. Probably by changing them to be unconstrained? Otherwise even if all the requirements of the forced version are prioritized, they would still not be met in the case of required_ruby_version and required_rubygems_version. Right?
This makes me wonder whether @simi's suggestion of providing a way of "monkeypatching requirements" of a gem would be better:
gem "minitest", "~> 5.14" do |s|
s.required_ruby_version = ">= 2.5"
endIt would also make it possible to use the feature multiple times in a Gemfile which I believe it could currently cause issues with this approach if they have conflicting requirements?
There was a problem hiding this comment.
I don't like the monkey patch syntax because it requires bringing all the parents into the equation in the force_version case and I don't think adds enough extra information to make it worth it. Also, I'm a bit wracking my brain in how to implement.
Here is an implementation of two additional flags to gem, :override_ruby_version and `:override_rubygems_version: lukaso#7 which I can fold into this PR.
The usage example is:
gem 'rutabaga', git: 'git@github.com:simplybusiness/rutabaga.git', branch: 'ruby-24-or-less', override_ruby_version: true, override_rubygems_version: trueThere is no easy or clear place to patch into the loading of gemspecs that I could find, so I had to place to overrides in a number of strategic places.
There was a problem hiding this comment.
I'm not sure about the implementation either, I can see how it could get super tricky, but if we decided to do this, I was expecting to ship a single feature to accomplish this. Either force_version: <exact_version> as in: "ignore what you have to ignore during resolution to make sure you give me this specific version", or a more finer grained approach of being able to monkeypatch the gemspec constraints used for resolution.
I'll think about it more.
I don't think this helps because in general as there wouldn't be an original requirement in your Example: Gemfile: gem "sfmc-fuelsdk-ruby", "~> 1.3.0"
gem "jwt", "~> 2.2", force_version: true # added here to override gemspec in sfmc-fuelsdk-rubysfmc-fuelsdk-ruby.gemspec: spec.add_dependency "jwt", "~>1.0",">= 1.0.0"Or do you have another situation in mind? |
|
@lukaso I believe my proposal is also better when forcing indirect dependencies? It still makes it more clear that it's a temporary hack and forces you to update the In any case, your current implementation doesn't work with the example you gave, right? Doesn't it raise a "Cannot use force_version for inexact version requirement" error? |
|
I’ve got reservations about requiring an exact version. Imagine if a gem author used A better approach would be to use the existing constraints system. Allow gem authors to specify a looser constraint as an override. This would allow the dependency to continue to develop and the end users of the gem will be able to get the newer versions of the dependency independently of updates to this gem. Perfect! Just imagine if a security patch couldn’t be accessed because a dependency forced an exact version. 😥 |
What was the end-user or developer problem that led to this PR?
As described in the RFC: rubygems/rfcs#13, gems are often abandoned with the authors failing to update dependencies. This allows a user to force a dependency to a specific version so that they are not forced to fork and maintain a gem, which can be a substantial overhead.
To quote the RFC:
"This feature has been requested many times since 2011.
Project authors want the ability to specify newer versions of gems in their Gemfile even if the version is being constrained by another gem.
There several causes for this issue:
The version dependency specified in the gemspec is unnecessarily strict: s.add_dependency 'json', '= 1.7.7'
The gem is slow to update or is no-longer maintained to the point that version constraints do not keep up with the version updates of their dependencies: s.add_dependency 'json', '>= 1.7.7', '<= 2'
The common approach to this problem is to fork the problematic gem, relax the dependencies specified in the gemspec, and reference the fork in the Gemfile. This is a very heavyweight and time intensive solution to a problem that could be easily solved by a well-designed solution to override dependencies from within the Gemfile itself.
Obviously, overriding version constraints of gems would be AT YOUR OWN RISK, however, a properly defined DSL and output messages should mitigate and address these issues.
Most of the time, this kind of override should NOT be a permanent solution, and as such, SHOULD be accompanied with some type of message that is additionally displayed to indicate why the override has occurred."
What is your fix for the problem, implemented in this PR?
This code is a port of a proof of concept done by @segiddins in rubygems/bundler#5670, which has been adjusted to work on latest master.
Here is sample output:
Here is the
Gemfile:This means I no longer have to maintain a forked version of
sfmc-fuelsdk-ruby.The run:
Here is the
Gemfile.lock:Make sure the following tasks are checked