Skip to content
Merged
74 changes: 72 additions & 2 deletions .github/workflows/ruby-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ jobs:
sudo apt-get update -q || :
sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev bison autoconf
- name: Build Ruby
id: build
run: |
export GNUMAKEFLAGS="-j$((1 + $(nproc)))"
./autogen.sh
./configure -C --disable-install-doc
make
working-directory: ruby/ruby
continue-on-error: true
- name: Skip tests when ruby/ruby does not build
run: echo "::warning::ruby/ruby ${BRANCH} failed to build, so the tests were skipped"
env:
BRANCH: ${{ matrix.branch }}
if: steps.build.outcome == 'failure'
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: rubygems/rubygems
Expand Down Expand Up @@ -84,13 +91,75 @@ jobs:
- name: Test RubyGems
run: make -s test-all TESTS="--no-retry -j$((1 + $(nproc)))"
working-directory: ruby/ruby
if: matrix.target == 'Rubygems'
if: steps.build.outcome == 'success' && matrix.target == 'Rubygems'
- name: Test Bundler
run: |
git add .
make test-bundler-parallel
working-directory: ruby/ruby
if: matrix.target == 'Bundler'
if: steps.build.outcome == 'success' && matrix.target == 'Bundler'

ruby_core_master:
name: ${{ matrix.suite.name }} on a ruby-core master build${{ matrix.box && ' (RUBY_BOX=1)' || '' }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
suite:
- { name: Rubygems, deps: setup, task: test:parallel }
- { name: Bundler, deps: spec:deps, task: spec:regular, rubyopt: --disable-gems }
box: [false, true]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ruby/ruby
path: ruby/ruby
ref: master
persist-credentials: false
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: 3.4
bundler: none
- name: Install libraries
run: |
set -x
sudo apt-get update -q || :
sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev bison autoconf
- name: Build and install Ruby
id: build
run: |
export GNUMAKEFLAGS="-j$((1 + $(nproc)))"
./autogen.sh
./configure -C --disable-install-doc --prefix="$RUNNER_TEMP/ruby"
make
make install
echo "$RUNNER_TEMP/ruby/bin" >> "$GITHUB_PATH"
working-directory: ruby/ruby
continue-on-error: true
- name: Skip tests when ruby/ruby does not build
run: echo "::warning::ruby/ruby master failed to build, so the tests were skipped"
if: steps.build.outcome == 'failure'
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: rubygems
persist-credentials: false
- name: Install dependencies
run: |
ruby -v
bin/rake ${{ matrix.suite.deps }}
env:
RUBYOPT: ${{ matrix.suite.rubyopt }}
working-directory: rubygems
if: steps.build.outcome == 'success'
- name: Run Test
run: bin/rake ${{ matrix.suite.task }}
env:
RUBYOPT: ${{ matrix.suite.rubyopt }}
RUBY_BOX: ${{ matrix.box && '1' || '' }}
working-directory: rubygems
if: steps.build.outcome == 'success'

timeout-minutes: 90

all-pass:
name: All ruby-core jobs pass
Expand All @@ -99,6 +168,7 @@ jobs:

needs:
- ruby_core
- ruby_core_master

runs-on: ubuntu-latest

Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/gem_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def sh_with_status(cmd, &block)
Bundler.ui.debug(cmd)
SharedHelpers.chdir(base) do
outbuf = IO.popen(cmd, err: [:child, :out], &:read)
status = $?
status = Process.last_status
block&.call(outbuf) if status.success?
[outbuf, status]
end
Expand Down
4 changes: 3 additions & 1 deletion lib/rubygems/ext/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ def self.run(command, results, command_name = nil, dir = Dir.pwd, env = {})

require "open3"
# Set $SOURCE_DATE_EPOCH for the subprocess.
# Under Ruby::Box mkmf makes RbConfig.expand recurse until SystemStackError.
# Under Ruby::Box defined?($gvar) does not see assignments made inside the
# box, so mkmf have_devel? never memoizes and recurses until SystemStackError
# (https://bugs.ruby-lang.org/issues/22283).
# Drop $RUBY_BOX last so no caller can restore it.
build_env = { "SOURCE_DATE_EPOCH" => Gem.source_date_epoch_string }.merge(env).merge("RUBY_BOX" => nil)
# A single-element command would be parsed as a shell command line,
Expand Down
4 changes: 3 additions & 1 deletion lib/rubygems/source/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,11 @@ def rev_parse # :nodoc:
hash = Gem::Util.popen(git_command, "rev-parse", @reference).strip
end

# Process.last_status instead of $?, which Ruby::Box leaves uninitialized
# (https://bugs.ruby-lang.org/issues/22280)
raise Gem::Exception,
"unable to find reference #{@reference} in #{@repository}" unless
$?.success?
Process.last_status.success?

hash
end
Expand Down
6 changes: 6 additions & 0 deletions spec/bundler/plugin_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@
end

it "executes the hook" do
skip "Ruby::Box ignores $stdout reassignment (https://bugs.ruby-lang.org/issues/21867)" if defined?(Ruby::Box) && Ruby::Box.enabled?

expect do
Plugin.hook(Bundler::Plugin::Events::EVENT1)
end.to output("hook for event 1\n").to_stdout
Expand All @@ -331,6 +333,8 @@
RUBY

it "evals plugins.rb once" do
skip "Ruby::Box ignores $stdout reassignment (https://bugs.ruby-lang.org/issues/21867)" if defined?(Ruby::Box) && Ruby::Box.enabled?

expect do
Plugin.hook(Bundler::Plugin::Events::EVENT1)
Plugin.hook(Bundler::Plugin::Events::EVENT2)
Expand All @@ -344,6 +348,8 @@
RUBY

it "is passed to the hook" do
skip "Ruby::Box ignores $stdout reassignment (https://bugs.ruby-lang.org/issues/21867)" if defined?(Ruby::Box) && Ruby::Box.enabled?

expect do
Plugin.hook(Bundler::Plugin::Events::EVENT1) { puts "win" }
end.to output("win\n").to_stdout
Expand Down
9 changes: 7 additions & 2 deletions spec/bundler/shared_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,11 @@

before do
ENV["RUBYOPT"] = "-r#{install_path}/bundler/setup"
allow(File).to receive(:expand_path).and_return("#{install_path}/bundler/setup")
# Only fake the resolution of bundler/setup itself. A blanket stub
# breaks unrelated RubyGems path lookups triggered lazily inside the
# example, see #set_rubyopt.
allow(File).to receive(:expand_path).and_call_original
allow(File).to receive(:expand_path).with("setup", anything).and_return("#{install_path}/bundler/setup")
allow(Gem).to receive(:bin_path).and_return("#{install_path}/bundler/setup")
end

Expand All @@ -403,7 +407,8 @@
let(:install_path) { "/opt/ruby with space/lib" }

before do
allow(File).to receive(:expand_path).and_return("#{install_path}/bundler/setup")
allow(File).to receive(:expand_path).and_call_original
allow(File).to receive(:expand_path).with("setup", anything).and_return("#{install_path}/bundler/setup")
allow(Gem).to receive(:bin_path).and_return("#{install_path}/bundler/setup")
end

Expand Down
4 changes: 4 additions & 0 deletions spec/commands/install_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2065,6 +2065,10 @@ def gem_make_out
end

it "preserves bundled native extensions when BUNDLE_CLEAN removes another gem" do
# The command-line/RUBYOPT -r bypasses gem activation under RUBY_BOX=1
# (https://bugs.ruby-lang.org/issues/22295)
skip "-r cannot activate gems under Ruby::Box" if defined?(Ruby::Box) && Ruby::Box.enabled?

build_repo4 do
build_gem "native_child", "1.0", &:add_c_extension
build_gem "native_parent", "1.0" do |s|
Expand Down
1 change: 1 addition & 0 deletions spec/install/gemfile/content_addressable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@
RSpec.describe "bundle install with content-addressable gems invisible to pre-4.1 RubyGems clients", :compact_index, rubygems: ">= 4.1.0.a" do
before do
skip "Gem::ContentAddress not available" if ruby_core?
skip "A prerelease Ruby does not satisfy the ~> X.Y.0 ABI pin of content-addressed gems" if Gem.ruby_version.prerelease?
end

let(:current_abi) { "#{Gem.ruby_version.segments[0]}.#{Gem.ruby_version.segments[1]}" }
Expand Down
12 changes: 6 additions & 6 deletions spec/runtime/env_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,13 @@ def run_bundler_script(env, script)
create_file("source.rb", <<-'RUBY')
Bundler.original_system("ruby", "-e", "exit(42) if ENV['BUNDLE_FOO'] == 'bar'")

exit $?.exitstatus
exit Process.last_status.exitstatus
RUBY
end

it "runs system inside with_original_env" do
run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
expect($?.exitstatus).to eq(42)
expect(Process.last_status.exitstatus).to eq(42)
end
end

Expand All @@ -182,13 +182,13 @@ def run_bundler_script(env, script)
create_file("source.rb", <<-'RUBY')
Bundler.unbundled_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'")

exit $?.exitstatus
exit Process.last_status.exitstatus
RUBY
end

it "runs system inside with_unbundled_env" do
run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
expect($?.exitstatus).to eq(42)
expect(Process.last_status.exitstatus).to eq(42)
end
end

Expand All @@ -209,7 +209,7 @@ def run_bundler_script(env, script)
skip "Fork not implemented" if Gem.win_platform?

run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
expect($?.exitstatus).to eq(0)
expect(Process.last_status.exitstatus).to eq(0)
end
end

Expand All @@ -230,7 +230,7 @@ def run_bundler_script(env, script)
skip "Fork not implemented" if Gem.win_platform?

run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
expect($?.exitstatus).to eq(1)
expect(Process.last_status.exitstatus).to eq(1)
end
end
end
15 changes: 14 additions & 1 deletion spec/support/command_execution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

module Spec
class CommandExecution
# Under RUBY_BOX, every spawned ruby prints an experimental warning to
# stderr, breaking specs that assert clean stderr.
RUBY_BOX_WARNING = Regexp.union(
/^[^\n]*: warning: Ruby::Box is experimental, and the behavior may change in the future!\n?/,
%r{^See https://docs\.ruby-lang\.org/\S+ for known issues, etc\.\n?}
)

def initialize(command, timeout:)
@command = command
@timeout = timeout
Expand Down Expand Up @@ -72,7 +79,13 @@ def failure?
attr_reader :failure_reason

def normalize(string)
string.dup.force_encoding(Encoding::UTF_8).scrub.strip.gsub("\r\n", "\n")
string = string.dup.force_encoding(Encoding::UTF_8).scrub.gsub("\r\n", "\n")
string = string.gsub(RUBY_BOX_WARNING, "") if ruby_box_enabled?
string.strip
end

def ruby_box_enabled?
defined?(Ruby::Box) && Ruby::Box.enabled?
end
end
end
21 changes: 21 additions & 0 deletions test/rubygems/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ def setup
ENV["BUNDLE_COOLDOWN"] = nil
ENV["RUBYGEMS_PREVENT_UPDATE_SUGGESTION"] = "true"

# Child ruby processes inherit RUBY_BOX and print an experimental
# warning on startup, breaking assertions on subprocess stderr.
ENV["RUBYOPT"] = [ENV["RUBYOPT"], "-W:no-experimental"].compact.join(" ") if ruby_box_enabled?

@current_dir = Dir.pwd
@fetcher = nil

Expand Down Expand Up @@ -1440,6 +1444,23 @@ def ruby_repo?
!ENV["GEM_COMMAND"].nil?
end

##
# Is this test running under Ruby::Box (RUBY_BOX=1)?

def ruby_box_enabled?
defined?(Ruby::Box) && Ruby::Box.enabled?
end

##
# Ruby::Box gives each box detached copies of the stdio globals, so
# reassigning $stdout/$stderr cannot capture output written by Kernel#warn,
# Kernel#puts or subprocesses. Pends until the ruby-core fix for
# https://bugs.ruby-lang.org/issues/21867 lands.

def pend_for_ruby_box_stdio_capture
pend "Ruby::Box breaks $stdout/$stderr capture (https://bugs.ruby-lang.org/issues/21867)" if ruby_box_enabled?
end

##
# Returns the make command for the current platform. For versions of Ruby
# built on MS Windows with VC++ or Borland it will return 'nmake'. On all
Expand Down
3 changes: 3 additions & 0 deletions test/rubygems/test_deprecate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def test_deprecated_method_calls_the_old_method
end

def test_deprecated_method_outputs_a_warning
pend_for_ruby_box_stdio_capture
out, err = capture_output do
thing = Thing.new
thing.foo
Expand Down Expand Up @@ -165,6 +166,7 @@ def execute
end

def test_deprecated_method_outputs_a_warning_old_way
pend_for_ruby_box_stdio_capture
out, err = capture_output do
thing = OtherThing.new
thing.foo
Expand All @@ -180,6 +182,7 @@ def test_deprecated_method_outputs_a_warning_old_way
end

def test_deprecated_method_when_class_overrides_format
pend_for_ruby_box_stdio_capture
out, err = capture_output do
thing = ThingWithFormat.new
thing.foo
Expand Down
3 changes: 2 additions & 1 deletion test/rubygems/test_exit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
class TestGemExit < Gem::TestCase
def test_exit
system(*ruby_with_rubygems_in_load_path, "-e", "raise Gem::SystemExitException.new(2)")
assert_equal 2, $?.exitstatus
# Process.last_status instead of $?, which Ruby::Box leaves uninitialized
assert_equal 2, Process.last_status.exitstatus
end

def test_status
Expand Down
2 changes: 2 additions & 0 deletions test/rubygems/test_gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@ def test_self_try_activate_missing_prerelease
end

def test_self_try_activate_missing_extensions
pend_for_ruby_box_stdio_capture
spec = util_spec "ext", "1" do |s|
s.extensions = %w[ext/extconf.rb]
s.installed_by_version = v("2.2")
Expand Down Expand Up @@ -1352,6 +1353,7 @@ def test_setting_paths_does_not_mutate_parameter_object
end

def test_deprecated_paths=
pend_for_ruby_box_stdio_capture
stdout, stderr = capture_output do
Gem.paths = { "GEM_HOME" => Gem.paths.home,
"GEM_PATH" => [Gem.paths.home, "foo"] }
Expand Down
1 change: 1 addition & 0 deletions test/rubygems/test_gem_commands_build_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ def test_execute_strict_with_warnings
end

def test_execute_bad_spec
pend_for_ruby_box_stdio_capture
@gem.date = "2010-11-08"

gemspec_file = File.join(@tempdir, @gem.spec_name)
Expand Down
1 change: 1 addition & 0 deletions test/rubygems/test_gem_commands_open_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def gem(name, version = "1.0")
end

def test_execute
pend_for_ruby_box_stdio_capture
omit "JRuby on Windows spawns the editor with a different cwd" if Gem.win_platform? && Gem.java_platform?

@cmd.options[:args] = %w[foo]
Expand Down
1 change: 1 addition & 0 deletions test/rubygems/test_gem_config_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ def test_handle_arguments_backtrace
end

def test_handle_arguments_debug
pend_for_ruby_box_stdio_capture
assert_equal false, $DEBUG

args = %w[--debug]
Expand Down
Loading