diff --git a/CHANGELOG.md b/CHANGELOG.md index 128f89e76c..0c5f125798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # New Relic Ruby Agent Release Notes +## dev + +- **Bugfix: Incompatibility with Bootstrap** + +Version fixes an incompatibility between the agent and the [Bootstrap](https://github.com/twbs/bootstrap-rubygem) gem caused by agent v9.10.0's introduction of a `lib/bootstrap.rb` file. Thank you to [@dorner](https://github.com/dorner) for reporting the bug and identifying the 'bootstrap' name collision as the root cause. [BUG#2675](https://github.com/newrelic/newrelic-ruby-agent/issues/2675) [PR#2676](https://github.com/newrelic/newrelic-ruby-agent/pull/2676) + ## v9.10.0 Version 9.10.0 introduces instrumentation for DynamoDB, adds a new feature to automatically apply nonces from the Rails content security policy, fixes a bug that would cause an expected error to negatively impact a transaction's Apdex, and fixes the agent's autostart logic so that by default `rails runner` and `rails db` commands will not cause the agent to start. diff --git a/lib/bootstrap.rb b/lib/boot/strap.rb similarity index 80% rename from lib/bootstrap.rb rename to lib/boot/strap.rb index 557d7ef91a..7b6ac650f8 100644 --- a/lib/bootstrap.rb +++ b/lib/boot/strap.rb @@ -27,7 +27,7 @@ # - Next, use the "RUBYOPT" environment variable to require ("-r") this # file (note that the ".rb" extension is dropped): # ``` -# export RUBYOPT="-r /newrelic/lib/bootstrap" +# export RUBYOPT="-r /newrelic/lib/boot/strap" # ``` # - Add your New Relic license key as an environment variable. # ``` @@ -50,7 +50,7 @@ def require(*_groups) end def require_newrelic - lib = File.dirname(__FILE__) + lib = File.expand_path('../..', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) Kernel.require NR_AGENT_GEM end @@ -65,40 +65,36 @@ def self.patch check_for_rubyopt check_for_bundler Bundler::Runtime.prepend(NRBundlerPatch) + rescue StandardError => e + Kernel.warn "New Relic entrypoint at #{__FILE__} encountered an issue:\n #{e.message}" end private def self.check_for_require - warn_and_exit "#{__FILE__} is meant to be required, not invoked directly" if $PROGRAM_NAME == __FILE__ + raise "#{__FILE__} is meant to be required, not invoked directly" if $PROGRAM_NAME == __FILE__ end def self.check_for_rubyopt unless ENV[RUBYOPT].to_s.match?("-r #{__FILE__.rpartition('.').first}") - warn_and_exit "#{__FILE__} is meant to be required via the RUBYOPT env var" + raise "#{__FILE__} is meant to be required via the RUBYOPT env var" end end def self.check_for_bundler require_bundler - warn_and_exit 'Required Ruby Bundler class Bundler::Runtime not defined!' unless defined?(Bundler::Runtime) + raise 'Required Ruby Bundler class Bundler::Runtime not defined!' unless defined?(Bundler::Runtime) unless Bundler::Runtime.method_defined?(:require) - warn_and_exit "The active Ruby Bundler instance doesn't offer Bundler::Runtime#require" + raise "The active Ruby Bundler instance doesn't offer Bundler::Runtime#require" end end def self.require_bundler require BUNDLER rescue LoadError => e - warn_and_exit "Required Ruby library '#{BUNDLER}' could not be required - #{e}" - end - - def self.warn_and_exit(msg) - warn "New Relic entrypoint at #{__FILE__} encountered an issue:\n\t#{msg}" - - exit 1 + raise "Required Ruby library '#{BUNDLER}' could not be required - #{e}" end end diff --git a/test/new_relic/bootstrap_test.rb b/test/new_relic/boot/strap_test.rb similarity index 67% rename from test/new_relic/bootstrap_test.rb rename to test/new_relic/boot/strap_test.rb index 51a8332ff2..f22d880421 100644 --- a/test/new_relic/bootstrap_test.rb +++ b/test/new_relic/boot/strap_test.rb @@ -2,7 +2,7 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -require_relative '../test_helper' +require_relative '../../test_helper' class NewRelicBootstrapTest < Minitest::Test class PhonyBundler @@ -34,13 +34,10 @@ def test_check_for_require opn = $PROGRAM_NAME $PROGRAM_NAME = bootstrap_file - msg = '' - NRBundlerPatcher.stub :warn_and_exit, proc { |m| msg = m } do + + assert_raises(RuntimeError, 'meant to be required, not invoked') do NRBundlerPatcher.check_for_require end - - assert_match(/meant to be required, not invoked/, msg, - 'Expected check_for_require to complain when bootstrap is invoked directly') ensure $PROGRAM_NAME = opn end @@ -61,13 +58,9 @@ def test_check_for_bundler_class_not_defined NRBundlerPatcher.stub :require_bundler, nil do Bundler.send(:remove_const, :Runtime) - msg = '' - NRBundlerPatcher.stub :warn_and_exit, proc { |m| msg = m; Bundler.send(:const_set, :Runtime, oruntime) } do + assert_raises(RuntimeError, 'class Bundler::Runtime not defined!') do NRBundlerPatcher.check_for_bundler end - - assert_match(/class Bundler::Runtime not defined!/, msg, - 'Expected check_for_bundler to complain if Bundler::Runtime is not defined') end ensure Bundler.send(:const_set, :Runtime, oruntime) @@ -78,13 +71,9 @@ def test_check_for_bundler_method_not_defined NRBundlerPatcher.stub :require_bundler, nil do Bundler::Runtime.stub :method_defined?, false, [:require] do - msg = '' - NRBundlerPatcher.stub :warn_and_exit, proc { |m| msg = m } do + assert_raises(RuntimeError, "doesn't offer Bundler::Runtime#require") do NRBundlerPatcher.check_for_bundler end - - assert_match(/doesn't offer Bundler::Runtime#require/, msg, - 'Expected check_for_bundler to complain if Bundler::Runtime#require is not defined') end end end @@ -93,26 +82,26 @@ def test_require_bundler skip_unless_minitest5_or_above NRBundlerPatcher.stub :require, proc { |_gem| raise LoadError }, ['bundler'] do - msg = '' - NRBundlerPatcher.stub :warn_and_exit, proc { |m| msg = m } do + assert_raises(RuntimeError, 'could not be required') do NRBundlerPatcher.check_for_bundler end - - assert_match(/could not be required/, msg, - 'Expected require_bundler to complain if Bundler could not be required') end end private - # Load the bootstrap file and anticipate the `warn` and `exit` calls - # with assertions + # Load the bootstrap file and anticipate the `warn` call def require_bootstrap - assert_raises SystemExit do - assert_output(/New Relic entrypoint/) do - require_relative '../../lib/bootstrap' - end + msg = '' + loaded = nil + Kernel.stub :warn, proc { |m| msg = m } do + loaded = require_relative '../../../lib/boot/strap' end + + return unless loaded + + assert_match(/New Relic entrypoint/, msg, + 'Expected the initial requiring of boot/strap to generate a warning') end # Have the patcher patch our phony Bundler instead of the real one @@ -129,6 +118,6 @@ def monkeypatch_phony_bundler end def bootstrap_file - @bootstrap_file ||= File.expand_path('../../../lib/bootstrap.rb', __FILE__) + @bootstrap_file ||= File.expand_path('../../../../lib/boot/strap.rb', __FILE__) end end