Skip to content

Bundler cannot evaluate local gemspecs with RUBY_BOX=1 #9740

Description

@katsyoshi

Description

Bundler fails to evaluate a local gemspec when Ruby Box is enabled with
RUBY_BOX=1.

The failure occurs before the requested command runs:

Bundler::GemspecError:
There was an error while loading `foo.gemspec`:
uninitialized constant Gem::Specification

This reproduces with an unmodified gem project generated by bundle gem. I
also reproduced the same problem in two unrelated existing gem projects,
Caotral and Oj.

Minimal reproduction

Generate a new gem using Bundler:

bundle gem foo
cd foo
bundle install

Running a command normally succeeds:

$ bundle exec ruby -e 'puts :ok'
ok

Enable Ruby Box and run the same command:

$ RUBY_BOX=1 bundle exec ruby -e 'puts :ok'

Bundler fails while evaluating the generated, unmodified foo.gemspec:

ruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
See https://docs.ruby-lang.org/en/4.0/Ruby/Box.html for known issues, etc.

[!] There was an error while loading `foo.gemspec`:
uninitialized constant Gem::Specification. Bundler cannot continue.

 #  from /path/to/foo/foo.gemspec:5
 #  -------------------------------------------
 #
 >  Gem::Specification.new do |spec|
 #    spec.name = "foo"
 #  -------------------------------------------

Expected behavior

Bundler evaluates the gemspec and runs the requested command:

ok

Actual behavior

Bundler raises Bundler::GemspecError because Gem::Specification is not
available while evaluating the gemspec under Ruby Box.

Subprocess reproduction

The same issue occurs in Oj when its build invokes a Ruby subprocess:

RUBY_BOX=1 bundle exec rake

The outer Rake process starts, but the Ruby subprocess spawned for
extconf.rb fails:

/path/to/ruby -I. ../../../../ext/oj/extconf.rb

[!] There was an error while loading `oj.gemspec`:
uninitialized constant Gem::Specification. Bundler cannot continue.

 #  from /path/to/oj/oj.gemspec:6
 #  -------------------------------------------
 #
 >  Gem::Specification.new do |s|
 #    s.name = "oj"
 #  -------------------------------------------

This suggests that Ruby subprocesses inheriting both the Bundler environment
and RUBY_BOX=1 are affected as well.

Environment

The minimal bundle gem foo reproduction was confirmed with:

Ruby: 4.0.6
RubyGems: 4.0.17
Bundler: 4.0.17
OS: Linux

The same failure was also reproduced in Caotral with Bundler 4.0.12.

Attempted workarounds

Adding this to the gemspec does not fix the problem:

require "rubygems/specification"

It proceeds further but then fails because Gem::Deprecate is unavailable.

Adding this instead also does not provide a usable workaround:

require "rubygems"

It causes a circular require, after which Bundler fails to materialize the
local gemspec.

I confirmed that the application test itself works if Bundler is started
without Ruby Box and a child Ruby process is started separately with:

  • RUBY_BOX=1
  • Bundler's resolved $LOAD_PATH passed through RUBYLIB
  • inherited RUBYOPT cleared
  • --disable-gems

For example:

env = {
  "RUBY_BOX" => "1",
  "RUBYOPT" => nil,
  "RUBYLIB" => $LOAD_PATH.join(File::PATH_SEPARATOR),
}

exec(
  env,
  RbConfig.ruby,
  "--disable-gems",
  "-Ilib",
  "path/to/test.rb",
)

This avoids evaluating the gemspec inside Ruby Box, but it is not a practical
general workaround for bundle exec.

Additional context

Ruby Box must be enabled when the Ruby process starts. It cannot be enabled
after Bundler has initialized:

https://docs.ruby-lang.org/en/4.0/Ruby/Box.html

Is Bundler expected to support running under RUBY_BOX=1? If so, it appears
that the RubyGems constants required by Bundler.eval_gemspec are not
initialized in the relevant box.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions