Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change bundle install with a lockfile to respect the BUNDLED WITH bundler version #4076

Merged
merged 17 commits into from
Dec 20, 2021

Conversation

duckinator
Copy link
Member

@duckinator duckinator commented Nov 20, 2020

This implements part of the Bundler Version Locking RFC.

What was the end-user or developer problem that led to this PR?

It's hard to ensure that all developers and environments of an application use the exact same bundler version. There's many reasons why that's a good thing, for example, wanting to enforce that a security release is used, so that nobody is vulnerable to a security issue.

What is your fix for the problem, implemented in this PR?

My fix is to respect the BUNDLED WITH version if there is a lockfile. This section was originally introduced for this exact purpose but was never actually enforced.

The strategy is that when bundle install is run, if the running version does not match the BUNDLED WITH version, bundler will first install the BUNDLED WITH version, and then re-exec using that version.

This strategy is implemented as conservatively as we could:

  • Other versions can still be specific with the BUNDLER_VERSION environment variable or with bundle _<version>_.
  • If anything goes wrong when installing the BUNDLED WITH version, bundler will go on using the running version as a fallback.
  • This feature is only applied when bundler is run in combination with the latest rubygems.

Make sure the following tasks are checked

@duckinator duckinator closed this Nov 20, 2020
@duckinator duckinator reopened this Nov 20, 2020
@duckinator duckinator changed the title [WIP] Implement the Bundler Version Locking RFC. [WIP/draft] Implement the Bundler Version Locking RFC. Nov 20, 2020
@duckinator duckinator marked this pull request as draft November 20, 2020 20:30
@duckinator duckinator changed the title [WIP/draft] Implement the Bundler Version Locking RFC. Implement the Bundler Version Locking RFC. Nov 20, 2020
@duckinator

This comment has been minimized.

@deivid-rodriguez

This comment has been minimized.

@duckinator

This comment has been minimized.

@deivid-rodriguez

This comment has been minimized.

@duckinator
Copy link
Member Author

Either way, here's a screenshot showing this PR working! :3

Screenshot_20201120_154127

@duckinator duckinator force-pushed the bundler-version-locking branch from 9bee17a to d56c8a0 Compare November 20, 2020 20:46
@duckinator
Copy link
Member Author

Fixed some linting failures. (I'm having trouble getting rubocop to run locally, so had to rely on CI for that, heh.)

@duckinator duckinator force-pushed the bundler-version-locking branch from d56c8a0 to 63c3171 Compare November 21, 2020 05:49
@duckinator
Copy link
Member Author

I fixed the linter errors but introduced new ones, the first time. I think it's right, now. 😂

@duckinator
Copy link
Member Author

I have no idea what's causing those CI errors; will have to look into it another day.

@duckinator
Copy link
Member Author

Getting back into this again. Rebased off master.

@duckinator
Copy link
Member Author

Rebased off master. Also realized I finished an item from the TODO checklist at some point already, so checked that off.

@duckinator
Copy link
Member Author

I may be able to switch from Kernel.exec(...) to Bundler.original_exec(...). According to the function documentation, this should address the problem @simi mentioned in rubygems/rfcs#29 (comment):

# Run a `Kernel.exec` to a subcommand with the environment present before Bundler was activated
def original_exec(*args)
with_original_env { Kernel.exec(*args) }
end

@duckinator
Copy link
Member Author

I'm a bit stuck atm because I'm not sure how bundle _VERSION_ ... is implemented, and can't find it from poking around.

@simi
Copy link
Member

simi commented Oct 23, 2021

@duckinator that's feature of generated binstub by gem installer.

File.open generated_bin, 'rb' do |io|
next unless io.gets =~ /^#!/ # shebang
io.gets # blankline
# TODO detect a specially formatted comment instead of trying
# to run a regexp against Ruby code.
next unless io.gets =~ /This file was generated by RubyGems/
ruby_executable = true
existing = io.read.slice(%r{
^\s*(
gem \s |
load \s Gem\.bin_path\( |
load \s Gem\.activate_bin_path\(
)
(['"])(.*?)(\2),
}x, 3)
end

example of rails binstub

[retro@retro  git]❤ cat ~/.gem/ruby/3.0.2/bin/rails 
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'railties' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

str = ARGV.first
if str
  str = str.b[/\A_(.*)_\z/, 1]
  if str and Gem::Version.correct?(str)
    version = str
    ARGV.shift
  end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('railties', 'rails', version)
else
gem "railties", version
load Gem.bin_path("railties", "rails", version)
end

@duckinator
Copy link
Member Author

Huh, okay, so there's no actual way to determine whether it was started using that mechanism or not. That makes me wonder if this all needs to be dragged up to the Bundler binstub.

Thanks for the info, @simi. I'll have to think a bit on how to approach this.

deivid-rodriguez and others added 17 commits December 19, 2021 23:56
This is what all the other specs do, and for what's being tested here,
the bundler version is not relevant.
If something inside bundler used the original environment, it would be
reset to what it was before, which is most likely the global `GEM_HOME`,
and most likely unintended.
This does not make any difference for the spec that added it, but the
correct way to set sources is through the setter because `@sources`
should be a `Gem::SourceList` instance.
If an exception happened during the setup or teardown of a pending
spec, the runner would crash.
If an explicit RUBYOPT was passed to our test command helpers, the
global RUBYOPT in the environment would be lost. This seems important on
Windows since it has a global `-Eutf-8` that seems to cause issues when
lost.

This commit fixes that by properly merging both the explicit environment
given and the one globally set.
With this in place, bundler can be managed in a local path just like any
other gem.
Show a discreet warning and continue.
@deivid-rodriguez deivid-rodriguez merged commit f9636cc into rubygems:master Dec 20, 2021
@ngan
Copy link
Contributor

ngan commented Dec 20, 2021

this is awesome. Thanks @duckinator and @deivid-rodriguez

@duckinator
Copy link
Member Author

@deivid-rodriguez thank you for making sure this PR got wrapped up and merged. I appreciate it a lot!

arjun024 added a commit to cloudfoundry/ruby-buildpack that referenced this pull request Jan 5, 2022
See rubygems/rubygems#4076
rubygems/bundler started respecting BUNDLED WITH directive
with newer versions.
arjun024 added a commit to cloudfoundry/ruby-buildpack that referenced this pull request Jan 5, 2022
This check was added in 655c172 when bundler didn't
fully enforce the BUNDLE WITH directive.
See rubygems/rubygems#4076
bundle commands now respect this directive -
so move the compatability logic well before any bundle
commands.
arjun024 added a commit to cloudfoundry/ruby-buildpack that referenced this pull request Jan 6, 2022
arjun024 added a commit to cloudfoundry/ruby-buildpack that referenced this pull request Jan 6, 2022
@ysbaddaden
Copy link

Is it possible to DISABLE or OPT-OUT from that feature?

Bundler keeps on downgrading itself in my Docker images. I just want to use the bundler that is already available, not downgrade to the version from an older image 😭

@simi
Copy link
Member

simi commented Feb 6, 2023

Is it possible to DISABLE or OPT-OUT from that feature?

Bundler keeps on downgrading itself in my Docker images. I just want to use the bundler that is already available, not downgrade to the version from an older image sob

The proper way to handle is to update the bundler in Gemfile.lock to the one you use, in your case to the one you have in docker image.

@duckinator duckinator deleted the bundler-version-locking branch February 7, 2023 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants