Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the bugsnag-ruby test and dependency setup to run unit/integration specs on Ruby 4.0, including tweaks to fixtures and spec assertions to account for Ruby/Bundler behavior changes.
Changes:
- Add Ruby 4.0 to the GitHub Actions test matrix and adjust dependency installation steps for newer Bundler behavior.
- Add
ostruct(and related fixture dependencies) for Ruby 4.0+ compatibility in the main Gemfile and fixture app Gemfiles. - Update specs to tolerate minor Ruby-version differences (e.g., stacktrace frame count) and to handle Bundler env isolation changes.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
spec/report_spec.rb |
Loosens a stacktrace-frame assertion to accommodate Ruby version differences. |
spec/integrations/logger_spec.rb |
Updates fixture app/script execution to support newer Bundler env methods and install flows. |
spec/fixtures/apps/rails-no-config/Gemfile |
Adjusts fixture dependencies for Ruby 4.0+ (logger/bigdecimal/ostruct/etc.). |
spec/fixtures/apps/rails-invalid-initializer-config/Gemfile |
Same as above for the invalid-initializer fixture. |
Gemfile |
Adds ostruct for Ruby 4.0+ in the dev/test dependency set. |
CHANGELOG.md |
Notes the CI matrix enhancement (currently added without a version header). |
.github/workflows/test-package.yml |
Adds Ruby 4.0 to CI matrix and splits bundler install behavior by Ruby version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
spec/integrations/logger_spec.rb
Outdated
| # Run Ruby script with environment variables | ||
| # Quote environment variable values to handle spaces and special characters | ||
| env_str = @env.map { |k, v| "#{k}='#{v}'" }.join(' ') | ||
| IO.popen("#{env_str} bundle exec ruby #{name}.rb 2>&1") do |io| |
There was a problem hiding this comment.
execute_script builds a shell command string from @env and name and passes it to IO.popen, which re-introduces shell parsing/quoting issues (and potential command injection if any value contains quotes/whitespace). Prefer the array form of IO.popen with an env hash (as previously used) so Ruby bypasses the shell and handles escaping safely.
| # Run Ruby script with environment variables | |
| # Quote environment variable values to handle spaces and special characters | |
| env_str = @env.map { |k, v| "#{k}='#{v}'" }.join(' ') | |
| IO.popen("#{env_str} bundle exec ruby #{name}.rb 2>&1") do |io| | |
| # Run Ruby script with environment variables, avoiding shell interpolation | |
| # Use the env-hash + array form so Ruby bypasses the shell and handles escaping | |
| IO.popen(@env, ['bundle', 'exec', 'ruby', "#{name}.rb"], err: [:child, :out]) do |io| |
| pid = Process.spawn(@env, 'bundle exec rackup config.ru', | ||
| out: out_writer.fileno, | ||
| err: out_writer.fileno) | ||
| sleep(2) | ||
| Process.kill('TERM', pid) | ||
| end |
There was a problem hiding this comment.
After Process.kill('TERM', pid) the code never waits for the rackup process to exit. This can leave a zombie process and/or cause flaky specs due to lingering servers/locked resources. Consider waiting on the PID (optionally with a timeout and fallback SIGKILL) after sending TERM.
| ### Enhancements | ||
|
|
||
| * Add Ruby 4.0 to the unit test matrix for CI | ||
| | [PLAT-15923](https://smartbear.atlassian.net/browse/PLAT-15923) |
There was a problem hiding this comment.
The changelog entry is added as a top-level "### Enhancements" section without a version heading, which breaks the existing format where entries are grouped under ## vX.Y.Z (date) sections. Consider adding this bullet under the appropriate release section or introducing a clearly-labeled unreleased section (consistent with the project's changelog conventions).
Gemfile
Outdated
| if ruby_version >= Gem::Version.new("4.0") # not shipped by default in Ruby 4+ | ||
| gem 'ostruct' |
There was a problem hiding this comment.
This introduces the only double-quoted version string in the Gemfile (elsewhere uses single quotes) and also leaves trailing whitespace on the gem 'ostruct' line. Consider switching to single quotes and removing the trailing whitespace for consistency.
| if ruby_version >= Gem::Version.new("4.0") # not shipped by default in Ruby 4+ | |
| gem 'ostruct' | |
| if ruby_version >= Gem::Version.new('4.0') # not shipped by default in Ruby 4+ | |
| gem 'ostruct' |
spec/report_spec.rb
Outdated
| # - Report.generate_exceptions_list | raw_exceptions.map | ||
| # - Report.generate_exceptions_list | raw_exceptions.map | block | ||
| expect(bugsnag_count).to eq(6) | ||
| expect(bugsnag_count).to be_between(5, 6).inclusive |
There was a problem hiding this comment.
Trailing whitespace after .inclusive can cause noisy diffs and may be flagged by editors/linters. Consider removing the trailing space.
| expect(bugsnag_count).to be_between(5, 6).inclusive | |
| expect(bugsnag_count).to be_between(5, 6).inclusive |
| else | ||
| gem 'logger', '>= 1.7', '< 2.0' | ||
| gem 'bigdecimal', '>= 4.0.0', '< 5.0.0' | ||
| gem 'ostruct' |
There was a problem hiding this comment.
There is trailing whitespace after gem 'ostruct', which can lead to noisy diffs and editor/linter warnings. Consider removing the trailing space.
| gem 'ostruct' | |
| gem 'ostruct' |
| else | ||
| gem 'logger', '>= 1.7', '< 2.0' | ||
| gem 'bigdecimal', '>= 4.0.0', '< 5.0.0' | ||
| gem 'ostruct' |
There was a problem hiding this comment.
There is trailing whitespace after gem 'ostruct', which can lead to noisy diffs and editor/linter warnings. Consider removing the trailing space.
| gem 'ostruct' | |
| gem 'ostruct' |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| begin | ||
| # Wait up to 5 seconds for the process to exit | ||
| Timeout.timeout(5) { Process.waitpid(pid) } | ||
| rescue Timeout::Error | ||
| # If still running, force kill | ||
| Process.kill('KILL', pid) rescue nil | ||
| Process.waitpid(pid) rescue nil |
There was a problem hiding this comment.
Timeout.timeout is used here, but this spec file (and spec/spec_helper.rb) never requires the timeout stdlib, so this will raise NameError: uninitialized constant Timeout on a clean Ruby process. Add require 'timeout' (ideally near the top of this spec) or avoid Timeout entirely.
| def execute_bundle_and_app(name, out_writer) | ||
| # Handle Bundler install for different Ruby versions | ||
| ruby_version = Gem::Version.new(RUBY_VERSION.dup) | ||
|
|
There was a problem hiding this comment.
execute_bundle_and_app accepts name but never uses it. Either remove the parameter to avoid confusion, or use it if there’s fixture-specific behavior intended.
| Dir.chdir(File.join(File.dirname(__FILE__), "../fixtures/apps/#{name}")) do | ||
| Bundler.with_clean_env do | ||
| pid = Process.spawn('bundle install', | ||
| out: out_writer.fileno, | ||
| err: out_writer.fileno) | ||
| Process.waitpid(pid, 0) | ||
| pid = Process.spawn(@env, 'bundle exec rackup config.ru', | ||
| out: out_writer.fileno, | ||
| err: out_writer.fileno) | ||
| sleep(2) | ||
| Process.kill(1, pid) | ||
| # Determine which Bundler env method to use based on availability | ||
| # Ruby 4.0+ uses with_unbundled_env, Ruby 2-3.x uses with_clean_env, Ruby 1.9.2 has no env isolation | ||
| if Bundler.respond_to?(:with_unbundled_env) | ||
| Bundler.with_unbundled_env do | ||
| execute_bundle_and_app(name, out_writer) | ||
| end |
There was a problem hiding this comment.
Ruby 4.0 runs of this spec will still execute the rails-initializer-config fixture (see below), but spec/fixtures/apps/rails-initializer-config/Gemfile still pins logger 1.6.5/bigdecimal 3.1.9 for Ruby >= 3.4. That is inconsistent with the other updated fixtures and is likely to break bundler resolution on Ruby 4.0; update that fixture Gemfile similarly.
| ### Enhancements | ||
|
|
||
| * Add Ruby 4.0 to the unit test matrix for CI | ||
| | [PLAT-15923](https://smartbear.atlassian.net/browse/PLAT-15923) | ||
|
|
||
| ## v6.29.0 (21 January 2026) |
There was a problem hiding this comment.
This new entry is not under a version heading, whereas the rest of the changelog is organized by ## vX.Y.Z (date) sections. Consider adding a new version section (or an explicit “Unreleased”) and placing this bullet under it to keep the changelog structure consistent.
| # For Ruby < 3.4, use the legacy flags which are still supported | ||
| - name: Install dependencies (Ruby < 3.4) | ||
| if: ${{ !startsWith(matrix.ruby-version, '3.4') && !startsWith(matrix.ruby-version, '4.') }} | ||
| run: bundle install --with "${{ matrix.optional-groups }}" --binstubs |
There was a problem hiding this comment.
There appears to be trailing whitespace at the end of this line. Please remove it to avoid whitespace-only diffs and potential YAML lint failures.
| run: bundle install --with "${{ matrix.optional-groups }}" --binstubs | |
| run: bundle install --with "${{ matrix.optional-groups }}" --binstubs |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Process.kill('TERM', pid) | ||
| begin | ||
| # Wait up to 5 seconds for the process to exit | ||
| Timeout.timeout(5) { Process.waitpid(pid) } | ||
| rescue Timeout::Error |
There was a problem hiding this comment.
Timeout.timeout is used here but this spec file does not require 'timeout'. Without requiring the stdlib, Ruby will raise NameError: uninitialized constant Timeout when this path runs. Add require 'timeout' near the top (or load it via spec_helper) before referencing Timeout.
Goal
Add Ruby 4.0 support to the bugsnag-ruby by updating dependencies and test configurations to ensure compatibility with the latest Ruby version.
Design
ostructgem for Ruby 4.0+Changeset
ostructgem for Ruby 4.0+ (not shipped by default in Ruby 4+).github/workflows/test-package.ymlto include Ruby 4.0 in the test matrixspec/fixtures/apps/*/Gemfileto conditionally specify logger versions based on Ruby versionspec/report_spec.rbto allow a range of bugsnag frames (5-6) for stack trace compatibilityTesting