rubygems / rubygems

Library packaging and distribution for Ruby.
https://rubygems.org/
Other
3.68k stars 1.75k forks source link

Binstub with bundle path configured loads wrong version of Bundler #6883

Open ngan opened 1 year ago

ngan commented 1 year ago

On the latest version of bundler (2.4.18), but I think this applies to all versions, we noticed a bug(?) wherein if you have path configured, a binstub (using require "bundler/setup") boots into the wrong version of Bundler. More specifically, it just uses your system bundler instead of the higher versioned bundler installed (into say, vendor/bundle) and specified by your Gemfile.lock.

We noticed this problem in our Docker image where Ruby 3.2.2 comes with Bundler 2.4.10. We install gems in our docker image to vendor/bundle. If you run bin/rails (or any binstub) and puts Bundler::VERSION inside the process, you'll get 2.4.10 instead of 2.4.18.

This is easily reproducible locally:

  1. bundle install --path vendor/bundle
  2. gem uninstall bundler (this is to uninstall the higher version of bundler from your system gems. It should be installed into vendor/bundle already).
  3. bin/rails runner "puts Bundler::VERSION"

I thought that this was by design since I don't even see how we would load Bundler (whichever one is available), then realize that we've installed gems into a custom path, then switch over to the right version of bundler all within the same process. But then I saw @indirect's blog post: Never bundle exec again and thought, maybe this is an oversight.

tubaxenor commented 1 year ago

Here is the minimum repo to reproduce the issue: https://github.com/tubaxenor/bundler-test

martinemde commented 1 year ago

I may be mistaken, but I don't think this is fixable. Bundler must run to setup the environment, which means it can't load itself at a different version after loading the environment. Bundler is one of the few gems that must be installed in the system in order to run any ruby program that uses a Gemfile.

deivid-rodriguez commented 1 year ago

Bundler has some trampolining behavior, where it will re'exec to the locked version of itself as soon as it finds that the locked version is different than the running version. But maybe this is not quite working for this particular case. I'd like to have a closer look at it, so let me reopen @martinemde if that's fine. I'll close if I find this is not fixable or not worth fixing.

martinemde commented 1 year ago

Ah yes, so I was mistaken. Thanks @deivid-rodriguez!

simi commented 1 year ago

this is to uninstall the higher version of bundler from your system gems. It should be installed into vendor/bundle already

@ngan Why do you think bundler should be installed in vendor/bundle when it is not part of the Gemfile itself? Bundler vendors only gems included in your bundle (listed in Gemfile.lock).

deivid-rodriguez commented 2 weeks ago

Bundler currently already installs the locked version of Bundler in the Gemfile.lock file into vendor/bundle (or whatever path is configured). That way it is able to automatically re-exec using that version the next time.

The problem is that in the particular case where the locked version of Bundler is already running because of being installed globally, this was not happen, so subsequent runs fail to run the proper version if the locked version stops being available globally.

8136 should fix the problem!