From ead790b126616dcd3f47f266240941d140133017 Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Tue, 8 Jun 2021 15:09:30 +0100 Subject: [PATCH 1/6] Replace `active_sentry_environments` with `enabled_environments` The custom option `active_sentry_environments` was added in https://github.com/alphagov/govuk_app_config/pull/168. However, it turns out there is already a native option for this: `enabled_environments`. See https://docs.sentry.io/platforms/ruby/configuration/options/ This allows us to simplify our code a fair bit. I've also removed the stubbed `sending_allowed?` method as it was interfering with some tests and giving a false sense of security. We're now providing all of the necessary configuration for the `Sentry::Configuration` instance to return `true` on its `valid?` method, so no need for the hack. Each config option is documented in an inline comment as otherwise it can be quite obtuse. --- README.md | 2 +- .../govuk_error/configuration.rb | 11 ++----- lib/govuk_app_config/govuk_error/configure.rb | 2 +- spec/govuk_error/configuration_spec.rb | 33 +++++++++++-------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e8f8e10a..408ad794 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ You can add your environment to the list of active Sentry environments like so: ```ruby GovukError.configure do |config| - config.active_sentry_environments << "my-test-environment" + config.enabled_environments << "my-test-environment" end ``` diff --git a/lib/govuk_app_config/govuk_error/configuration.rb b/lib/govuk_app_config/govuk_error/configuration.rb index 30e54615..60c5fd30 100644 --- a/lib/govuk_app_config/govuk_error/configuration.rb +++ b/lib/govuk_app_config/govuk_error/configuration.rb @@ -3,17 +3,14 @@ module GovukError class Configuration < SimpleDelegator - attr_reader :data_sync, :sentry_environment - attr_accessor :active_sentry_environments, :data_sync_excluded_exceptions + attr_reader :data_sync + attr_accessor :data_sync_excluded_exceptions def initialize(_sentry_configuration) super - @sentry_environment = ENV["SENTRY_CURRENT_ENV"] @data_sync = GovukDataSync.new(ENV["GOVUK_DATA_SYNC_PERIOD"]) - self.active_sentry_environments = [] self.data_sync_excluded_exceptions = [] @before_send_callbacks = [ - ignore_exceptions_if_not_in_active_sentry_env, ignore_excluded_exceptions_in_data_sync, increment_govuk_statsd_counters, ] @@ -26,10 +23,6 @@ def before_send=(closure) protected - def ignore_exceptions_if_not_in_active_sentry_env - ->(event, _hint) { event if active_sentry_environments.include?(sentry_environment) } - end - def ignore_excluded_exceptions_in_data_sync lambda { |event, hint| data_sync_ignored_error = data_sync_excluded_exceptions.any? do |exception_to_ignore| diff --git a/lib/govuk_app_config/govuk_error/configure.rb b/lib/govuk_app_config/govuk_error/configure.rb index df5fa737..f7895a1e 100644 --- a/lib/govuk_app_config/govuk_error/configure.rb +++ b/lib/govuk_app_config/govuk_error/configure.rb @@ -3,7 +3,7 @@ # ENV variable) where we want to capture Sentry errors. If # `SENTRY_CURRENT_ENV` isn't in this list, or isn't defined, then # don't capture the error. - config.active_sentry_environments = %w[ + config.enabled_environments = %w[ integration-blue-aws staging production diff --git a/spec/govuk_error/configuration_spec.rb b/spec/govuk_error/configuration_spec.rb index 39ac8dec..ba68ca82 100644 --- a/spec/govuk_error/configuration_spec.rb +++ b/spec/govuk_error/configuration_spec.rb @@ -3,8 +3,12 @@ require "govuk_app_config/govuk_error/configuration" RSpec.describe GovukError::Configuration do + let(:dummy_dsn) { "http://12345:67890@sentry.localdomain/sentry/42" } + before :each do stub_const("GovukStatsd", double(Module, increment: nil)) + stub_request(:post, "http://sentry.localdomain/sentry/api/42/envelope/") + .to_return(status: 200) end describe ".initialize" do @@ -26,7 +30,7 @@ it "ignores errors if they happen in an environment we don't care about" do ClimateControl.modify SENTRY_CURRENT_ENV: "some-temporary-environment" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" sentry_client = send_exception_to_sentry(StandardError.new, configuration) expect(sentry_client.transport.events).to be_empty end @@ -34,7 +38,7 @@ it "captures errors if they happen in an environment we care about" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" sentry_client = send_exception_to_sentry(StandardError.new, configuration) expect(sentry_client.transport.events.count).to eq(1) end @@ -42,7 +46,7 @@ it "allows string messages to be sent (rather than exceptions)" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" configuration.data_sync_excluded_exceptions << "SomeError" sentry_client = Sentry::Client.new(optimise_configuration_for_testing(configuration)) sentry_hub = Sentry::Hub.new(sentry_client, Sentry::Scope.new) @@ -55,7 +59,7 @@ context "during the data sync" do around do |example| ClimateControl.modify SENTRY_CURRENT_ENV: "production", GOVUK_DATA_SYNC_PERIOD: "22:00-08:00" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" travel_to(Time.current.change(hour: 23)) do example.run end @@ -115,7 +119,7 @@ context "outside of the data sync" do around do |example| ClimateControl.modify SENTRY_CURRENT_ENV: "production", GOVUK_DATA_SYNC_PERIOD: "22:00-08:00" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" travel_to(Time.current.change(hour: 21)) do example.run end @@ -133,7 +137,7 @@ context "when the before_send lambda has not been overridden" do it "increments the appropriate counters" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" expect(GovukStatsd).to receive(:increment).exactly(1).times.with("errors_occurred") expect(GovukStatsd).to receive(:increment).exactly(1).times.with("error_types.standard_error") send_exception_to_sentry(StandardError.new, configuration) @@ -144,7 +148,7 @@ context "when the before_send lambda has been overridden" do it "increments the appropriate counters" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" expect(GovukStatsd).to receive(:increment).exactly(1).times.with("errors_occurred") expect(GovukStatsd).to receive(:increment).exactly(1).times.with("error_types.standard_error") expect(GovukStatsd).to receive(:increment).exactly(1).times.with("hello_world") @@ -162,7 +166,7 @@ context "when the before_send lambda has been overridden several times, all take effect" do it "increments the appropriate counters" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" expect(GovukStatsd).to receive(:increment).exactly(1).times.with("errors_occurred") expect(GovukStatsd).to receive(:increment).exactly(1).times.with("error_types.standard_error") expect(GovukStatsd).to receive(:increment).exactly(1).times.with("hello_world") @@ -185,7 +189,7 @@ context "when a message rather than an exception is sent to Sentry" do it "does not increment the error counters" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do - configuration.active_sentry_environments << "production" + configuration.enabled_environments << "production" sentry_client = Sentry::Client.new(optimise_configuration_for_testing(configuration)) sentry_hub = Sentry::Hub.new(sentry_client, Sentry::Scope.new) @@ -202,7 +206,7 @@ it "Allows apps to add their own `before_send` callback, that is evaluated alongside the default. If all return their parameter, then the chain continues, but if any returns `nil`, then it ends and the error is dropped" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do sentry_configurator = GovukError::Configuration.new(Sentry::Configuration.new) - sentry_configurator.active_sentry_environments << "production" + sentry_configurator.enabled_environments << "production" stub_const("CustomError", Class.new(StandardError)) sentry_configurator.before_send = lambda do |event, hint| event if hint[:exception].is_a?(CustomError) @@ -218,7 +222,7 @@ it "does not increment the counters if the callback returns nil" do ClimateControl.modify SENTRY_CURRENT_ENV: "production" do sentry_configurator = GovukError::Configuration.new(Sentry::Configuration.new) - sentry_configurator.active_sentry_environments << "production" + sentry_configurator.enabled_environments << "production" sentry_configurator.before_send = lambda do |_error_or_event, _hint| nil end @@ -241,8 +245,11 @@ def send_exception_to_sentry(event, configuration) def optimise_configuration_for_testing(configuration) # prevent the sending happening in a separate worker, which would cause async results configuration.background_worker_threads = 0 - # force configuration to allow sending - allow(configuration).to receive(:sending_allowed?).and_return(true) + # allows us to debug which events have been sent to Sentry + # https://github.com/getsentry/sentry-ruby/blob/b9aa6ca8ad2bb1965ca58c7f8fc0dd16b5df310b/sentry-ruby/lib/sentry/transport/dummy_transport.rb#L7-L11 + configuration.transport.transport_class = Sentry::DummyTransport + # required for `Sentry::Configuration`'s `valid?` method to return `true` + configuration.dsn = dummy_dsn configuration end end From 540771937329177b5c4c1100fec995cd85101e8a Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Wed, 9 Jun 2021 09:22:19 +0100 Subject: [PATCH 2/6] Call `Sentry.init` via `GovukError.configure` This is required for sentry-ruby to work properly. We used to proxy our `configure` method through to `Sentry.configure`, but this method is deprecated as of v4 of the gem. Moreover, the `Sentry.init` method clears any previous configuration, rather than layering additional configuration on top. In subsequent commits, we will re-architect GovukError to force only one `configure` call. --- lib/govuk_app_config.rb | 1 - lib/govuk_app_config/govuk_error.rb | 6 +- .../govuk_error/configuration.rb | 67 ++++++++++++++++++- lib/govuk_app_config/govuk_error/configure.rb | 62 ----------------- spec/govuk_error/configure_spec.rb | 9 --- 5 files changed, 70 insertions(+), 75 deletions(-) delete mode 100644 lib/govuk_app_config/govuk_error/configure.rb delete mode 100644 spec/govuk_error/configure_spec.rb diff --git a/lib/govuk_app_config.rb b/lib/govuk_app_config.rb index 6c8939ec..4ee13076 100644 --- a/lib/govuk_app_config.rb +++ b/lib/govuk_app_config.rb @@ -1,7 +1,6 @@ require "govuk_app_config/version" require "govuk_app_config/govuk_statsd" require "govuk_app_config/govuk_error" -require "govuk_app_config/govuk_error/configure" require "govuk_app_config/govuk_healthcheck" require "govuk_app_config/govuk_i18n" # This require is deprecated and should be removed on next major version bump diff --git a/lib/govuk_app_config/govuk_error.rb b/lib/govuk_app_config/govuk_error.rb index 448e6bb5..213b31c3 100644 --- a/lib/govuk_app_config/govuk_error.rb +++ b/lib/govuk_app_config/govuk_error.rb @@ -18,7 +18,9 @@ def self.notify(exception_or_message, args = {}) end def self.configure - @configuration ||= Configuration.new(Sentry::Configuration.new) - yield @configuration + Sentry.init do |sentry_config| + config = Configuration.new(sentry_config) + yield config if block_given? + end end end diff --git a/lib/govuk_app_config/govuk_error/configuration.rb b/lib/govuk_app_config/govuk_error/configuration.rb index 60c5fd30..eafeb133 100644 --- a/lib/govuk_app_config/govuk_error/configuration.rb +++ b/lib/govuk_app_config/govuk_error/configuration.rb @@ -9,11 +9,76 @@ class Configuration < SimpleDelegator def initialize(_sentry_configuration) super @data_sync = GovukDataSync.new(ENV["GOVUK_DATA_SYNC_PERIOD"]) - self.data_sync_excluded_exceptions = [] + set_up_defaults + end + + def set_up_defaults + # These are the environments (described by the `SENTRY_CURRENT_ENV` + # ENV variable) where we want to capture Sentry errors. If + # `SENTRY_CURRENT_ENV` isn't in this list, or isn't defined, then + # don't capture the error. + self.enabled_environments = %w[ + integration-blue-aws + staging + production + ] + + self.excluded_exceptions = [ + # Default ActionDispatch rescue responses + "ActionController::RoutingError", + "AbstractController::ActionNotFound", + "ActionController::MethodNotAllowed", + "ActionController::UnknownHttpMethod", + "ActionController::NotImplemented", + "ActionController::UnknownFormat", + "Mime::Type::InvalidMimeType", + "ActionController::MissingExactTemplate", + "ActionController::InvalidAuthenticityToken", + "ActionController::InvalidCrossOriginRequest", + "ActionDispatch::Http::Parameters::ParseError", + "ActionController::BadRequest", + "ActionController::ParameterMissing", + "Rack::QueryParser::ParameterTypeError", + "Rack::QueryParser::InvalidParameterError", + # Default ActiveRecord rescue responses + "ActiveRecord::RecordNotFound", + "ActiveRecord::StaleObjectError", + "ActiveRecord::RecordInvalid", + "ActiveRecord::RecordNotSaved", + # Additional items + "ActiveJob::DeserializationError", + "CGI::Session::CookieStore::TamperedWithCookie", + "GdsApi::HTTPIntermittentServerError", + "GdsApi::TimedOutException", + "Mongoid::Errors::DocumentNotFound", + "Sinatra::NotFound", + "Slimmer::IntermittentRetrievalError", + ] + + # This will exclude exceptions that are triggered by one of the ignored + # exceptions. For example, when any exception occurs in a template, + # Rails will raise a ActionView::Template::Error, instead of the original error. + self.inspect_exception_causes_for_exclusion = true + + # List of exceptions to ignore if they take place during the data sync. + # Some errors are transient in nature, e.g. PostgreSQL databases being + # unavailable, and add little value. In fact, their presence can greatly + # increase the number of errors being sent and risk genuine errors being + # rate-limited by Sentry. + self.data_sync_excluded_exceptions = [ + "PG::Error", + "GdsApi::ContentStore::ItemNotFound", + ] + @before_send_callbacks = [ ignore_excluded_exceptions_in_data_sync, increment_govuk_statsd_counters, ] + # Need to invoke an arbitrary `before_send=` in order to trigger the + # `before_send_callbacks` behaviour + self.before_send = lambda { |error_or_event, _hint| + error_or_event + } end def before_send=(closure) diff --git a/lib/govuk_app_config/govuk_error/configure.rb b/lib/govuk_app_config/govuk_error/configure.rb deleted file mode 100644 index f7895a1e..00000000 --- a/lib/govuk_app_config/govuk_error/configure.rb +++ /dev/null @@ -1,62 +0,0 @@ -GovukError.configure do |config| - # These are the environments (described by the `SENTRY_CURRENT_ENV` - # ENV variable) where we want to capture Sentry errors. If - # `SENTRY_CURRENT_ENV` isn't in this list, or isn't defined, then - # don't capture the error. - config.enabled_environments = %w[ - integration-blue-aws - staging - production - ] - - config.excluded_exceptions = [ - # Default ActionDispatch rescue responses - "ActionController::RoutingError", - "AbstractController::ActionNotFound", - "ActionController::MethodNotAllowed", - "ActionController::UnknownHttpMethod", - "ActionController::NotImplemented", - "ActionController::UnknownFormat", - "Mime::Type::InvalidMimeType", - "ActionController::MissingExactTemplate", - "ActionController::InvalidAuthenticityToken", - "ActionController::InvalidCrossOriginRequest", - "ActionDispatch::Http::Parameters::ParseError", - "ActionController::BadRequest", - "ActionController::ParameterMissing", - "Rack::QueryParser::ParameterTypeError", - "Rack::QueryParser::InvalidParameterError", - # Default ActiveRecord rescue responses - "ActiveRecord::RecordNotFound", - "ActiveRecord::StaleObjectError", - "ActiveRecord::RecordInvalid", - "ActiveRecord::RecordNotSaved", - # Additional items - "ActiveJob::DeserializationError", - "CGI::Session::CookieStore::TamperedWithCookie", - "GdsApi::HTTPIntermittentServerError", - "GdsApi::TimedOutException", - "Mongoid::Errors::DocumentNotFound", - "Sinatra::NotFound", - "Slimmer::IntermittentRetrievalError", - ] - - # This will exclude exceptions that are triggered by one of the ignored - # exceptions. For example, when any exception occurs in a template, - # Rails will raise a ActionView::Template::Error, instead of the original error. - config.inspect_exception_causes_for_exclusion = true - - # List of exceptions to ignore if they take place during the data sync. - # Some errors are transient in nature, e.g. PostgreSQL databases being - # unavailable, and add little value. In fact, their presence can greatly - # increase the number of errors being sent and risk genuine errors being - # rate-limited by Sentry. - config.data_sync_excluded_exceptions = [ - "PG::Error", - "GdsApi::ContentStore::ItemNotFound", - ] - - config.before_send = lambda { |error_or_event, _hint| - error_or_event - } -end diff --git a/spec/govuk_error/configure_spec.rb b/spec/govuk_error/configure_spec.rb deleted file mode 100644 index 5495c479..00000000 --- a/spec/govuk_error/configure_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require "spec_helper" -require "sentry-ruby" -require "govuk_app_config/govuk_error" - -RSpec.describe "GovukError.configure" do - it "should contain only valid Sentry config" do - require "govuk_app_config/govuk_error/configure" - end -end From 75cde97c0ca9f7caa3810a5a7321ab0e097cfd4c Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Thu, 10 Jun 2021 09:27:02 +0100 Subject: [PATCH 3/6] Prevent multiple calls to `GovukError.configure` The way we initialise the Sentry client has changed; Sentry now takes an `init` method, which overwrites any previous configuration. This means we can no longer have a 'default' `GovukError.configure` that is called prior to the application's `GovukError.configure`, nor can the application make multiple calls itself. To prevent confusion, we're now raising an exception if any app attempts to call the method twice. Due to Sentry being global, and a singleton, and closed for modification, unfortunately we can't split the test up into smaller ones. However, the resulting test is still reasonably concise. --- lib/govuk_app_config/govuk_error.rb | 12 ++++++++++++ spec/lib/govuk_error_spec.rb | 19 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/govuk_app_config/govuk_error.rb b/lib/govuk_app_config/govuk_error.rb index 213b31c3..c36101cb 100644 --- a/lib/govuk_app_config/govuk_error.rb +++ b/lib/govuk_app_config/govuk_error.rb @@ -5,6 +5,12 @@ require "govuk_app_config/version" module GovukError + class AlreadyInitialised < StandardError + def initialize(msg = "You can only call GovukError.configure once!") + super + end + end + def self.notify(exception_or_message, args = {}) # Allow users to use `parameters` as a key like the Airbrake # client, allowing easy upgrades. @@ -17,7 +23,13 @@ def self.notify(exception_or_message, args = {}) Sentry.capture_exception(exception_or_message, args) end + def self.is_configured? + Sentry.get_current_client != nil + end + def self.configure + raise GovukError::AlreadyInitialised if is_configured? + Sentry.init do |sentry_config| config = Configuration.new(sentry_config) yield config if block_given? diff --git a/spec/lib/govuk_error_spec.rb b/spec/lib/govuk_error_spec.rb index 2e1dbcd6..e3995b55 100644 --- a/spec/lib/govuk_error_spec.rb +++ b/spec/lib/govuk_error_spec.rb @@ -29,9 +29,26 @@ end describe ".configure" do - it "configures Sentry via the Configuration" do + it "configures Sentry via the Configuration, and raises exception for subsequent calls" do expect { |b| GovukError.configure(&b) } .to yield_with_args(instance_of(GovukError::Configuration)) + + expect { GovukError.configure { |_config| } } + .to raise_exception(GovukError::AlreadyInitialised) + end + end + + describe ".is_configured?" do + it "returns false if not configured" do + allow(Sentry).to receive(:get_current_client).and_return(nil) + + expect(GovukError.is_configured?).to eq(false) + end + + it "returns true if configured" do + allow(Sentry).to receive(:get_current_client).and_return(double("Sentry::Client")) + + expect(GovukError.is_configured?).to eq(true) end end end From 30d44e2b16d282cdfebdc27ee1d275897ac88833 Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Thu, 10 Jun 2021 09:29:58 +0100 Subject: [PATCH 4/6] Automatically call `GovukError.configure` in Rails apps If apps haven't provided their own custom configuration, then they would not ever trigger the `GovukError.configure` call without this Railtie. Non-Rails apps will be required to call `GovukError.configure` manually in order to initialise Sentry. --- lib/govuk_app_config/railtie.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/govuk_app_config/railtie.rb b/lib/govuk_app_config/railtie.rb index 6d74f706..16b2dbde 100644 --- a/lib/govuk_app_config/railtie.rb +++ b/lib/govuk_app_config/railtie.rb @@ -3,5 +3,9 @@ class Railtie < Rails::Railtie config.before_initialize do GovukLogging.configure if Rails.env.production? end + + config.after_initialize do + GovukError.configure unless GovukError.is_configured? + end end end From 1ddb9cd9ab283841b5ba29d9c62036109473774b Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Thu, 10 Jun 2021 09:37:19 +0100 Subject: [PATCH 5/6] Allow sending strings via `GovukError.notify` This is no longer possible when done via Sentry's `capture_exception` call, which raises an `ArgumentError` if an `Exception` is not passed: https://github.com/getsentry/sentry-ruby/blob/2ccfcf3f93057f4f0a3c98dc5e3ace3868dca509/sentry-ruby/lib/sentry/hub.rb#L95 --- lib/govuk_app_config/govuk_error.rb | 6 +++++- spec/lib/govuk_error_spec.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/govuk_app_config/govuk_error.rb b/lib/govuk_app_config/govuk_error.rb index c36101cb..8b8c6f39 100644 --- a/lib/govuk_app_config/govuk_error.rb +++ b/lib/govuk_app_config/govuk_error.rb @@ -20,7 +20,11 @@ def self.notify(exception_or_message, args = {}) args[:tags] ||= {} args[:tags][:govuk_app_config_version] = GovukAppConfig::VERSION - Sentry.capture_exception(exception_or_message, args) + if exception_or_message.is_a?(String) + Sentry.capture_message(exception_or_message, args) + else + Sentry.capture_exception(exception_or_message, args) + end end def self.is_configured? diff --git a/spec/lib/govuk_error_spec.rb b/spec/lib/govuk_error_spec.rb index e3995b55..ad2f4b89 100644 --- a/spec/lib/govuk_error_spec.rb +++ b/spec/lib/govuk_error_spec.rb @@ -11,6 +11,14 @@ expect(Sentry).to have_received(:capture_exception) end + it "forwards the string" do + allow(Sentry).to receive(:capture_message) + + GovukError.notify("Foo") + + expect(Sentry).to have_received(:capture_message) + end + it "allows Airbrake-style parameters" do allow(Sentry).to receive(:capture_exception) From f3e4fae0890c578e0e5c58bcd09c4aa9d07ddf15 Mon Sep 17 00:00:00 2001 From: ChrisBAshton Date: Thu, 10 Jun 2021 09:52:22 +0100 Subject: [PATCH 6/6] Bump to 4.0.0.pre.4 --- CHANGELOG.md | 6 ++++++ lib/govuk_app_config/version.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 100254e4..d0253d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 4.0.0.pre.4 + +- Fix Sentry client initialisation ([#205](https://github.com/alphagov/govuk_app_config/pull/205)). +- BREAKING: non-Rails apps will need to manually call `GovukError.configure` in order to initialise Sentry. +- BREAKING: `GovukError.configure` can only be called once by the downstream application. + # 4.0.0.pre.3 - Include [sentry-rails](https://github.com/getsentry/sentry-ruby/tree/master/sentry-rails) by default ([#203](https://github.com/alphagov/govuk_app_config/pull/203)). diff --git a/lib/govuk_app_config/version.rb b/lib/govuk_app_config/version.rb index 5b88ab24..617cde6b 100644 --- a/lib/govuk_app_config/version.rb +++ b/lib/govuk_app_config/version.rb @@ -1,3 +1,3 @@ module GovukAppConfig - VERSION = "4.0.0.pre.3".freeze + VERSION = "4.0.0.pre.4".freeze end