Skip to content

bundler/inline: perform installation from a forked child - #7941

Merged
hsbt merged 3 commits into
ruby:masterfrom
casperisfine:inline-fork
Mar 11, 2026
Merged

bundler/inline: perform installation from a forked child#7941
hsbt merged 3 commits into
ruby:masterfrom
casperisfine:inline-fork

Conversation

@casperisfine

Copy link
Copy Markdown

Unless of course fork isn't available.

Alternate: #7930, #7933
Fix: #7930, #7933

When bundler inline has to install gems, it loads more dependencies than when it goes through the fast path of all gems being installed.

One of them is securerandom so if trying to use bundler/inline with a gem that have a dependency on securerandom that don't match the default version, the script fails with Gem::LoadError.

This can be preproduced on Ruby 3.2.x, after making sure to gem uninstall securerandom so only the default gem remains, and then running the following script:

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'activesupport', '7.2.0' # depends on securerandom >= 0.3
end

require 'securerandom'

Comment thread bundler/lib/bundler/inline.rb Outdated
Comment on lines +73 to +82
# If the install succeeded, we need to refresh gem info
Bundler.reset!

builder = Bundler::Dsl.new
builder.instance_eval(&gemfile)
builder.check_primary_source_safety

definition = builder.to_definition(nil, true)
def definition.lock(*); end
definition.validate_runtime!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course it's harder than I expected, because since the install is done in a subprocess the parent need to refresh the gem info. I'm super un-familiar with rubygems/bundler do I don't know what the clean way of doing this, but it seems to work except for native gems :/

There is 3 failing specs left I don't know how to fix:

rspec ./spec/runtime/inline_spec.rb:193 # bundler/inline#gemfile installs subdependencies quietly if necessary when the install option is not set, and multiple sources used
rspec ./spec/runtime/inline_spec.rb:218 # bundler/inline#gemfile installs quietly from git if necessary when the install option is not set
rspec ./spec/runtime/inline_spec.rb:323 # bundler/inline#gemfile installs gems with native extensions in later gemfile calls

This comment was marked as spam.

else
do_install.call
end
end

This comment was marked as spam.

@hsbt
hsbt force-pushed the inline-fork branch 2 times, most recently from 413e8d3 to 11962a7 Compare January 14, 2026 01:37
byroot and others added 2 commits March 11, 2026 12:17
Unless of course fork isn't available.

Alternate: ruby#7930, ruby#7933
Fix: ruby#7930, ruby#7933

When bundler inline has to install gems, it loads more dependencies than when it
goes through the fast path of all gems being installed.

One of them is `securerandom` so if trying to use bundler/inline with a gem that
have a dependency on securerandom that don't match the default version, the script
fails with `Gem::LoadError`.

This can be preproduced on Ruby 3.2.x, after making sure to `gem uninstall securerandom`
so only the default gem remains, and then running the following script:

```ruby
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'activesupport', '7.2.0' # depends on securerandom >= 0.3
end

require 'securerandom'
```
@hsbt hsbt self-assigned this Mar 11, 2026
@hsbt
hsbt force-pushed the inline-fork branch 2 times, most recently from bf83056 to 21586e7 Compare March 11, 2026 06:04
@hsbt

hsbt commented Mar 11, 2026

Copy link
Copy Markdown
Member

@casperisfine @byroot Sorry to noisy notification. I finally resolved the CI failure and rebased with the current master branch. Maybe Ruby 3.2 will also reach EOL and this issue will no longer be an issue.

However, the default gems will continue to remain in rubygems and bundler, so I will merge this.

@hsbt
hsbt merged commit 6fd37f4 into ruby:master Mar 11, 2026
95 checks passed
@byroot

byroot commented Mar 11, 2026

Copy link
Copy Markdown
Member

No worries, I'm not notified on pushes. Thanks for the merge!

pirj added a commit to rspec/rspec-rails that referenced this pull request Aug 17, 2026
`script/run_snippets.sh` runs the snippets through `bundler/inline`, which
installs the gems and sets up the runtime in one process. Since bundler
4.0.15 that produces a `$LOAD_PATH` built from specs that never learned their
real `require_paths`, so every gem is assumed to use the default `lib`.
`concurrent-ruby`, an ActiveSupport dependency, uses `lib/concurrent-ruby`,
and the first snippet dies before running a single example:

    Running avoid_fixture_name_collision.rb
    There was an error while trying to load the gem 'rails'.
    Gem Load Error is: cannot load such file -- concurrent/map

CI hits this on every run because its gem home starts out empty, so the
snippets are always the ones installing those gems. Bisecting the released
gems against a fresh gem home puts the break at exactly 4.0.15, which shipped
on 2026-06-24 -- the day after the last green build on `main`.

The cause is ruby/rubygems#9618: `Installer#install` now calls
`release_resolution_memory!`, discarding the memoized remote index before
installing. `Source::Rubygems#install` annotates the resolved
`EndpointSpecification` in place rather than swapping it, and
`EndpointSpecification#load_paths` only reports the gemspec's real
`require_paths` while that annotation is present. Any re-resolution after the
install therefore rebuilds pristine specs that report `<gem>/lib`. It is still
present in 4.0.18; `master` avoids it only incidentally, by installing in a
forked child (ruby/rubygems#7941, never backported to 4.0).

Pin until there is a release we can move to.
pirj added a commit to rspec/rspec-rails that referenced this pull request Aug 17, 2026
`script/run_snippets.sh` runs the snippets through `bundler/inline`, which
installs the gems and sets up the runtime in one process. Since bundler
4.0.15 that produces a `$LOAD_PATH` built from specs that never learned their
real `require_paths`, so every gem is assumed to use the default `lib`.
`concurrent-ruby`, an ActiveSupport dependency, uses `lib/concurrent-ruby`,
and the first snippet dies before running a single example:

    Running avoid_fixture_name_collision.rb
    There was an error while trying to load the gem 'rails'.
    Gem Load Error is: cannot load such file -- concurrent/map

CI hits this on every run because its gem home starts out empty, so the
snippets are always the ones installing those gems. Bisecting the released
gems against a fresh gem home puts the break at exactly 4.0.15, which shipped
on 2026-06-24 -- the day after the last green build on `main`.

The cause is ruby/rubygems#9618: `Installer#install` now calls
`release_resolution_memory!`, discarding the memoized remote index before
installing. `Source::Rubygems#install` annotates the resolved
`EndpointSpecification` in place rather than swapping it, and
`EndpointSpecification#load_paths` only reports the gemspec's real
`require_paths` while that annotation is present. Any re-resolution after the
install therefore rebuilds pristine specs that report `<gem>/lib`. It is still
present in 4.0.18; `master` avoids it only incidentally, by installing in a
forked child (ruby/rubygems#7941, never backported to 4.0).

Reported upstream as ruby/rubygems#9781. Pin until there is a release we
can move to.
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.

4 participants