celerbrake 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/lib/celerbrake/capistrano/capistrano2.rb +40 -0
  3. data/lib/celerbrake/capistrano/capistrano3.rb +23 -0
  4. data/lib/celerbrake/capistrano.rb +8 -0
  5. data/lib/celerbrake/delayed_job.rb +64 -0
  6. data/lib/celerbrake/logger.rb +105 -0
  7. data/lib/celerbrake/rack/context_filter.rb +69 -0
  8. data/lib/celerbrake/rack/http_headers_filter.rb +44 -0
  9. data/lib/celerbrake/rack/http_params_filter.rb +27 -0
  10. data/lib/celerbrake/rack/instrumentable.rb +140 -0
  11. data/lib/celerbrake/rack/middleware.rb +102 -0
  12. data/lib/celerbrake/rack/request_body_filter.rb +33 -0
  13. data/lib/celerbrake/rack/request_store.rb +34 -0
  14. data/lib/celerbrake/rack/route_filter.rb +51 -0
  15. data/lib/celerbrake/rack/session_filter.rb +25 -0
  16. data/lib/celerbrake/rack/user.rb +74 -0
  17. data/lib/celerbrake/rack/user_filter.rb +25 -0
  18. data/lib/celerbrake/rack.rb +39 -0
  19. data/lib/celerbrake/rails/action_cable/notify_callback.rb +22 -0
  20. data/lib/celerbrake/rails/action_cable.rb +37 -0
  21. data/lib/celerbrake/rails/action_controller.rb +40 -0
  22. data/lib/celerbrake/rails/action_controller_notify_subscriber.rb +32 -0
  23. data/lib/celerbrake/rails/action_controller_performance_breakdown_subscriber.rb +51 -0
  24. data/lib/celerbrake/rails/action_controller_route_subscriber.rb +33 -0
  25. data/lib/celerbrake/rails/active_job.rb +50 -0
  26. data/lib/celerbrake/rails/active_record.rb +36 -0
  27. data/lib/celerbrake/rails/active_record_subscriber.rb +46 -0
  28. data/lib/celerbrake/rails/app.rb +78 -0
  29. data/lib/celerbrake/rails/backtrace_cleaner.rb +23 -0
  30. data/lib/celerbrake/rails/curb.rb +32 -0
  31. data/lib/celerbrake/rails/event.rb +93 -0
  32. data/lib/celerbrake/rails/excon_subscriber.rb +25 -0
  33. data/lib/celerbrake/rails/http.rb +18 -0
  34. data/lib/celerbrake/rails/http_client.rb +16 -0
  35. data/lib/celerbrake/rails/net_http.rb +18 -0
  36. data/lib/celerbrake/rails/railtie.rb +54 -0
  37. data/lib/celerbrake/rails/railties/action_controller_tie.rb +90 -0
  38. data/lib/celerbrake/rails/railties/active_record_tie.rb +74 -0
  39. data/lib/celerbrake/rails/railties/middleware_tie.rb +62 -0
  40. data/lib/celerbrake/rails/typhoeus.rb +16 -0
  41. data/lib/celerbrake/rails.rb +32 -0
  42. data/lib/celerbrake/rake/tasks.rb +112 -0
  43. data/lib/celerbrake/rake.rb +66 -0
  44. data/lib/celerbrake/resque.rb +62 -0
  45. data/lib/celerbrake/shoryuken.rb +52 -0
  46. data/lib/celerbrake/sidekiq/retryable_jobs_filter.rb +53 -0
  47. data/lib/celerbrake/sidekiq.rb +53 -0
  48. data/lib/celerbrake/sneakers.rb +72 -0
  49. data/lib/celerbrake/version.rb +7 -0
  50. data/lib/celerbrake.rb +32 -0
  51. data/lib/generators/celerbrake_generator.rb +22 -0
  52. data/lib/generators/celerbrake_initializer.rb.erb +80 -0
  53. metadata +380 -0
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
6
+ class BacktraceCleaner
7
+ # @return [Regexp]
8
+ CELERBRAKE_FRAME_PATTERN = %r{/celerbrake/lib/celerbrake/}.freeze
9
+
10
+ def self.clean(backtrace)
11
+ ::Rails.backtrace_cleaner.clean(backtrace).first(1)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ if defined?(Rails)
18
+ # Silence own frames to let the cleaner proceed to the next line (and probably
19
+ # find the correct call-site coming from the app code rather this library).
20
+ Rails.backtrace_cleaner.add_silencer do |line|
21
+ line =~ Celerbrake::Rails::BacktraceCleaner::CELERBRAKE_FRAME_PATTERN
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Allows measuring request timing.
6
+ module CurlEasy
7
+ def http(verb)
8
+ Celerbrake::Rack.capture_timing(:http) do
9
+ super(verb)
10
+ end
11
+ end
12
+
13
+ def perform(&block)
14
+ Celerbrake::Rack.capture_timing(:http) do
15
+ super(&block)
16
+ end
17
+ end
18
+ end
19
+
20
+ # Allows measuring request timing.
21
+ module CurlMulti
22
+ def http(urls_with_config, multi_options = {}, &block)
23
+ Celerbrake::Rack.capture_timing(:http) do
24
+ super(urls_with_config, multi_options, &block)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ Curl::Easy.prepend(Celerbrake::Rails::CurlEasy)
32
+ Curl::Multi.singleton_class.prepend(Celerbrake::Rails::CurlMulti)
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Event is a wrapper around ActiveSupport::Notifications::Event.
6
+ #
7
+ # @since v9.0.3
8
+ # @api private
9
+ class Event
10
+ # @see https://github.com/rails/rails/issues/8987
11
+ HTML_RESPONSE_WILDCARD = "*/*"
12
+
13
+ # @return [Integer]
14
+ MILLISECOND = 1000
15
+
16
+ include Celerbrake::Loggable
17
+
18
+ def initialize(*args)
19
+ @event = ActiveSupport::Notifications::Event.new(*args)
20
+ @rails_7_or_greater = ::Rails::VERSION::MAJOR >= 7
21
+ end
22
+
23
+ def method
24
+ @event.payload[:method]
25
+ end
26
+
27
+ def response_type
28
+ response_type = @event.payload[:format]
29
+ response_type == HTML_RESPONSE_WILDCARD ? :html : response_type
30
+ end
31
+
32
+ def params
33
+ @event.payload[:params]
34
+ end
35
+
36
+ def sql
37
+ @event.payload[:sql]
38
+ end
39
+
40
+ def db_runtime
41
+ @db_runtime ||= @event.payload[:db_runtime] || 0
42
+ end
43
+
44
+ def view_runtime
45
+ @view_runtime ||= @event.payload[:view_runtime] || 0
46
+ end
47
+
48
+ def time
49
+ # On Rails 7+ `ActiveSupport::Notifications::Event#time` returns an
50
+ # instance of Float. It represents monotonic time in milliseconds.
51
+ # Celerbrake Ruby expects that the provided time is in seconds. Hence,
52
+ # we need to convert it from milliseconds to seconds. In the
53
+ # versions below Rails 7, time is an instance of Time.
54
+ #
55
+ # Relevant commit:
56
+ # https://github.com/rails/rails/commit/81d0dc90becfe0b8e7f7f26beb66c25d84b8ec7f
57
+ @rails_7_or_greater ? @event.time / MILLISECOND : @event.time
58
+ end
59
+
60
+ def groups
61
+ groups = {}
62
+ groups[:db] = db_runtime if db_runtime > 0
63
+ groups[:view] = view_runtime if view_runtime > 0
64
+ groups
65
+ end
66
+
67
+ def status_code
68
+ return @event.payload[:status] if @event.payload[:status]
69
+
70
+ if @event.payload[:exception]
71
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(
72
+ @event.payload[:exception].first,
73
+ )
74
+ status = 500 if status == 0
75
+
76
+ return status
77
+ end
78
+
79
+ # The ActiveSupport event doesn't have status only in two cases:
80
+ # - an exception was thrown
81
+ # - unauthorized access
82
+ # We have already handled the exception so what's left is unauthorized
83
+ # access. There's no way to know for sure it's unauthorized access, so
84
+ # we are rather optimistic here.
85
+ 401
86
+ end
87
+
88
+ def duration
89
+ @event.duration
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'celerbrake/rails/event'
4
+
5
+ module Celerbrake
6
+ module Rails
7
+ # @api private
8
+ # @since v9.2.0
9
+ class Excon
10
+ def call(*args)
11
+ return unless Celerbrake::Config.instance.performance_stats
12
+
13
+ routes = Celerbrake::Rack::RequestStore[:routes]
14
+ return if !routes || routes.none?
15
+
16
+ event = Celerbrake::Rails::Event.new(*args)
17
+
18
+ routes.each do |_route_path, params|
19
+ params[:groups][:http] ||= 0
20
+ params[:groups][:http] += event.duration
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Monkey-patch to measure request timing.
6
+ # @api private
7
+ # @since v11.0.2
8
+ module HTTP
9
+ def perform(request, options)
10
+ Celerbrake::Rack.capture_timing(:http) do
11
+ super(request, options)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ HTTP::Client.prepend(Celerbrake::Rails::HTTP)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Allows measuring request timing.
6
+ module HTTPClient
7
+ def do_get_block(request, proxy, connection, &block)
8
+ Celerbrake::Rack.capture_timing(:http) do
9
+ super(request, proxy, connection, &block)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ HTTPClient.prepend(Celerbrake::Rails::HTTPClient)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Monkey-patch Net::HTTP to benchmark it.
6
+ # @api private
7
+ # @since v10.0.2
8
+ module NetHttp
9
+ def request(request, *args, &block)
10
+ Celerbrake::Rack.capture_timing(:http) do
11
+ super(request, *args, &block)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ Net::HTTP.prepend(Celerbrake::Rails::NetHttp)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # This railtie works for any Rails application that supports railties (Rails
6
+ # 3.2+ apps). It makes Celerbrake Ruby work with Rails and report errors
7
+ # occurring in the application automatically.
8
+ class Railtie < ::Rails::Railtie
9
+ initializer('celerbrake.middleware') do |app|
10
+ require 'celerbrake/rails/railties/middleware_tie'
11
+ Railties::MiddlewareTie.new(app).call
12
+ end
13
+
14
+ rake_tasks do
15
+ # Report exceptions occurring in Rake tasks.
16
+ require 'celerbrake/rake'
17
+
18
+ # Defines tasks such as `celerbrake:test` & `celerbrake:deploy`.
19
+ require 'celerbrake/rake/tasks'
20
+ end
21
+
22
+ initializer('celerbrake.action_controller') do
23
+ require 'celerbrake/rails/railties/action_controller_tie'
24
+ Railties::ActionControllerTie.new.call
25
+ end
26
+
27
+ initializer('celerbrake.active_record') do
28
+ require 'celerbrake/rails/railties/active_record_tie'
29
+ Railties::ActiveRecordTie.new.call
30
+ end
31
+
32
+ initializer('celerbrake.active_job') do
33
+ ActiveSupport.on_load(:active_job, run_once: true) do
34
+ # Reports exceptions occurring in ActiveJob jobs.
35
+ require 'celerbrake/rails/active_job'
36
+ include Celerbrake::Rails::ActiveJob
37
+ end
38
+ end
39
+
40
+ initializer('celerbrake.action_cable') do
41
+ ActiveSupport.on_load(:action_cable, run_once: true) do
42
+ # Reports exceptions occurring in ActionCable connections.
43
+ require 'celerbrake/rails/action_cable'
44
+ end
45
+ end
46
+
47
+ runner do
48
+ at_exit do
49
+ Celerbrake.notify_sync($ERROR_INFO) if $ERROR_INFO
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'celerbrake/rails/action_controller'
4
+ require 'celerbrake/rails/action_controller_route_subscriber'
5
+ require 'celerbrake/rails/action_controller_notify_subscriber'
6
+ require 'celerbrake/rails/action_controller_performance_breakdown_subscriber'
7
+
8
+ module Celerbrake
9
+ module Rails
10
+ module Railties
11
+ # Ties Celerbrake APM (routes) and HTTP clients with Rails.
12
+ #
13
+ # @api private
14
+ # @since v13.0.1
15
+ class ActionControllerTie
16
+ def initialize
17
+ @route_subscriber = Celerbrake::Rails::ActionControllerRouteSubscriber.new
18
+ @notify_subscriber = Celerbrake::Rails::ActionControllerNotifySubscriber.new
19
+ @performance_breakdown_subscriber =
20
+ Celerbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
21
+ end
22
+
23
+ def call
24
+ ActiveSupport.on_load(:action_controller, run_once: true, yield: self) do
25
+ # Patches ActionController with methods that allow us to retrieve
26
+ # interesting request data. Appends that information to notices.
27
+ ::ActionController::Base.include(Celerbrake::Rails::ActionController)
28
+
29
+ tie_routes_apm
30
+ tie_http_integrations
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def tie_routes_apm
37
+ [
38
+ # Cache route information for the duration of the request.
39
+ ['start_processing.action_controller', @route_subscriber],
40
+
41
+ # Send route stats.
42
+ ['process_action.action_controller', @notify_subscriber],
43
+
44
+ # Send performance breakdown: where a request spends its time.
45
+ ['process_action.action_controller', @performance_breakdown_subscriber],
46
+ ].each do |(event, callback)|
47
+ ActiveSupport::Notifications.subscribe(event, callback)
48
+ end
49
+ end
50
+
51
+ def tie_http_integrations
52
+ tie_net_http
53
+ tie_curl
54
+ tie_http
55
+ tie_http_client
56
+ tie_typhoeus
57
+ tie_excon
58
+ end
59
+
60
+ def tie_net_http
61
+ require 'celerbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
62
+ end
63
+
64
+ def tie_curl
65
+ require 'celerbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
66
+ end
67
+
68
+ def tie_http
69
+ require 'celerbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
70
+ end
71
+
72
+ def tie_http_client
73
+ require 'celerbrake/rails/http_client' if defined?(HTTPClient)
74
+ end
75
+
76
+ def tie_typhoeus
77
+ require 'celerbrake/rails/typhoeus' if defined?(Typhoeus)
78
+ end
79
+
80
+ def tie_excon
81
+ return unless defined?(Excon)
82
+
83
+ require 'celerbrake/rails/excon_subscriber'
84
+ ActiveSupport::Notifications.subscribe(/excon/, Celerbrake::Rails::Excon.new)
85
+ ::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'celerbrake/rails/active_record'
4
+ require 'celerbrake/rails/active_record_subscriber'
5
+
6
+ module Celerbrake
7
+ module Rails
8
+ module Railties
9
+ # Ties Celerbrake APM (queries) with Rails.
10
+ #
11
+ # @api private
12
+ # @since v13.0.1
13
+ class ActiveRecordTie
14
+ def initialize
15
+ @active_record_subscriber = Celerbrake::Rails::ActiveRecordSubscriber.new
16
+ end
17
+
18
+ def call
19
+ ActiveSupport.on_load(:active_record, run_once: true, yield: self) do
20
+ tie_activerecord_callback_fix
21
+ tie_activerecord_apm
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def tie_activerecord_callback_fix
28
+ # Reports exceptions occurring in some bugged ActiveRecord callbacks.
29
+ # Applicable only to the versions of Rails lower than 4.2.
30
+ return unless defined?(::Rails)
31
+ return if Gem::Version.new(::Rails.version) > Gem::Version.new('4.2')
32
+
33
+ ActiveRecord::Base.include(Celerbrake::Rails::ActiveRecord)
34
+ end
35
+
36
+ def tie_activerecord_apm
37
+ # Some Rails apps don't use ActiveRecord.
38
+ return unless defined?(::ActiveRecord)
39
+
40
+ # However, some dependencies might still require it, so we need an
41
+ # extra check. Apps that don't need ActiveRecord will likely have no
42
+ # AR configurations defined. We will skip APM integration in that
43
+ # case. See: https://github.com/celerbrake/celerbrake/issues/1222
44
+ configurations = ::ActiveRecord::Base.configurations
45
+ return unless configurations.any?
46
+
47
+ # Send SQL queries.
48
+ ActiveSupport::Notifications.subscribe(
49
+ 'sql.active_record',
50
+ @active_record_subscriber,
51
+ )
52
+
53
+ # Filter out parameters from SQL body.
54
+ sql_filter = Celerbrake::Filters::SqlFilter.new(
55
+ detect_activerecord_adapter(configurations),
56
+ )
57
+ Celerbrake.add_performance_filter(sql_filter)
58
+ end
59
+
60
+ # Rails 6+ introduces the `configs_for` API instead of the deprecated
61
+ # `#[]`, so we need an updated call.
62
+ def detect_activerecord_adapter(configurations)
63
+ unless configurations.respond_to?(:configs_for)
64
+ return configurations[::Rails.env]['adapter']
65
+ end
66
+
67
+ cfg = configurations.configs_for(env_name: ::Rails.env).first
68
+ # Rails 7+ API : Rails 6 API.
69
+ cfg.respond_to?(:adapter) ? cfg.adapter : cfg.config['adapter']
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ module Railties
6
+ # Ties Celerbrake Rails Middleware with Rails (error sending).
7
+ #
8
+ # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
9
+ # responsible for logging exceptions and showing a debugging page in case
10
+ # the request is local. We want to insert our middleware after
11
+ # DebugExceptions, so we don't notify Celerbrake about local requests.
12
+ #
13
+ # @api private
14
+ # @since v13.0.1
15
+ class MiddlewareTie
16
+ def initialize(app)
17
+ @app = app
18
+ @middleware = app.config.middleware
19
+ end
20
+
21
+ def call
22
+ return tie_rails_5_or_above if ::Rails.version.to_i >= 5
23
+
24
+ if defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
25
+ return tie_rails_4_or_below_with_active_record
26
+ end
27
+
28
+ tie_rails_4_or_below_with_active_record
29
+ end
30
+
31
+ private
32
+
33
+ # Avoid the warning about deprecated strings.
34
+ # Insert after DebugExceptions, since ConnectionManagement doesn't
35
+ # exist in Rails 5 anymore.
36
+ def tie_rails_5_or_above
37
+ @middleware.insert_after(
38
+ ActionDispatch::DebugExceptions,
39
+ Celerbrake::Rack::Middleware,
40
+ )
41
+ end
42
+
43
+ # Insert after ConnectionManagement to avoid DB connection leakage:
44
+ # https://github.com/celerbrake/celerbrake/pull/568
45
+ def tie_rails_4_or_below_with_active_record
46
+ @middleware.insert_after(
47
+ ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
48
+ 'Celerbrake::Rack::Middleware',
49
+ )
50
+ end
51
+
52
+ # Insert after DebugExceptions for apps without ActiveRecord.
53
+ def tie_rails_4_or_below_without_active_record
54
+ @middleware.insert_after(
55
+ ActionDispatch::DebugExceptions,
56
+ 'Celerbrake::Rack::Middleware',
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Celerbrake
4
+ module Rails
5
+ # Allow measuring request timing.
6
+ module TyphoeusRequest
7
+ def run
8
+ Celerbrake::Rack.capture_timing(:http) do
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Typhoeus::Request.prepend(Celerbrake::Rails::TyphoeusRequest)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'celerbrake/rails/railtie'
4
+
5
+ module Celerbrake
6
+ # Rails namespace holds all Rails-related functionality.
7
+ module Rails
8
+ def self.logger
9
+ # Rails.logger is not set in some Rake tasks such as
10
+ # 'celerbrake:deploy'. In this case we use a sensible fallback.
11
+ level = (::Rails.logger ? ::Rails.logger.level : Logger::ERROR)
12
+
13
+ if ENV['RAILS_LOG_TO_STDOUT'].present?
14
+ Logger.new($stdout, level: level)
15
+ else
16
+ Logger.new(::Rails.root.join('log', 'celerbrake.log'), level: level)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ if defined?(ActionController::Metal)
23
+ require 'celerbrake/rails/action_controller'
24
+ module ActionController
25
+ # Adds support for Rails API/Metal for Rails < 5. Rails 5+ uses standard
26
+ # hooks.
27
+ # @see https://github.com/celerbrake/celerbrake/issues/821
28
+ class Metal
29
+ include Celerbrake::Rails::ActionController
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'celerbrake-ruby'
4
+
5
+ namespace :celerbrake do
6
+ desc 'Verify your gem installation by sending a test exception'
7
+ task test: (:environment if defined?(Rails)) do
8
+ raise Celerbrake::Error, 'celerbrake-ruby is not configured' unless Celerbrake.configured?
9
+
10
+ require 'pp'
11
+
12
+ response = Celerbrake.notify_sync('Exception from the test Rake task')
13
+ if response['error']
14
+ puts "Error: #{response['error']}"
15
+ elsif response.nil? || response['code']
16
+ puts <<-ERROR.gsub(/^\s+\|/, '')
17
+ |#{response['type']}: #{response['message']} (#{response['code']})
18
+ |
19
+ |Possible problems:
20
+ | 1. Project id/key is incorrect
21
+ | 2. Custom filters ignore the exception we try to send
22
+ | 3. Environment this task runs in is ignored (see `ignored_environments`)
23
+ |
24
+ |If nothing works, please file an issue at: https://github.com/celerbrake/celerbrake/issues
25
+ ERROR
26
+ elsif response['url']
27
+ puts <<-SUCCESS.gsub(/^\s+\|/, '')
28
+ |A test exception was sent to Celerbrake.
29
+ |Find it here: #{response['url']}
30
+ SUCCESS
31
+ else
32
+ puts <<-ERROR.gsub(/^\s+\|/, '')
33
+ |Unexpected error occurred. Response from Celerbrake:
34
+ |#{response}
35
+ ERROR
36
+ end
37
+ end
38
+
39
+ desc 'Notify Celerbrake of a new deploy'
40
+ task :deploy do
41
+ if defined?(Rails)
42
+ initializer = Rails.root.join('config', 'initializers', 'celerbrake.rb')
43
+
44
+ # Avoid loading the environment to speed up the deploy task and try guess
45
+ # the initializer file location.
46
+ if initializer.exist? && !Celerbrake.configured?
47
+ load(initializer)
48
+ else
49
+ Rake::Task[:environment].invoke
50
+ end
51
+ end
52
+
53
+ raise Celerbrake::Error, 'celerbrake-ruby is not configured' unless Celerbrake.configured?
54
+
55
+ deploy_params = {
56
+ environment: ENV.fetch('ENVIRONMENT', nil),
57
+ username: ENV.fetch('USERNAME', nil),
58
+ revision: ENV.fetch('REVISION', nil),
59
+ repository: ENV.fetch('REPOSITORY', nil),
60
+ version: ENV.fetch('VERSION', nil),
61
+ }
62
+ promise = Celerbrake.notify_deploy(deploy_params)
63
+ promise.then do
64
+ puts "The #{deploy_params[:environment]} environment was deployed."
65
+ end
66
+ promise.rescue { |error| abort(error) }
67
+ end
68
+
69
+ desc 'Install a Heroku deploy hook to notify Celerbrake of deploys'
70
+ task :install_heroku_deploy_hook do
71
+ app = ENV.fetch('HEROKU_APP', nil)
72
+
73
+ config = Bundler.with_clean_env do
74
+ `heroku config --shell#{" --app #{app}" if app}`
75
+ end
76
+
77
+ heroku_env = config.each_line.with_object({}) do |line, h|
78
+ h.merge!(Hash[*line.rstrip.split("\n").flat_map { |v| v.split('=', 2) }])
79
+ end
80
+
81
+ id = heroku_env['CELERBRAKE_PROJECT_ID']
82
+ key = heroku_env['CELERBRAKE_API_KEY']
83
+
84
+ exit!(1) if [id, key].any?(&:nil?)
85
+
86
+ unless (env = heroku_env['RAILS_ENV'])
87
+ env = 'production'
88
+ puts "Celerbrake couldn't identify your app's environment, so the '#{env}'" \
89
+ " environment will be used."
90
+ end
91
+
92
+ unless (repo = ENV.fetch('REPOSITORY_URL', nil))
93
+ repo = `git remote get-url origin 2>/dev/null`.chomp
94
+ if repo.empty?
95
+ puts "Celerbrake couldn't identify your app's repository."
96
+ else
97
+ puts "Celerbrake couldn't identify your app's repository, so the " \
98
+ "'origin' remote url '#{repo}' will be used."
99
+ end
100
+ end
101
+
102
+ url = ["https://celerbrake.com/api/v3/projects/#{id}/heroku-deploys?key=#{key}"]
103
+ url << "&environment=#{env}"
104
+ url << "&repository=#{repo}" unless repo.empty?
105
+
106
+ command = [%(heroku addons:create deployhooks:http --url="#{url.join}")]
107
+ command << " --app #{app}" if app
108
+
109
+ puts "$ #{command.join}"
110
+ Bundler.with_clean_env { puts `#{command}` }
111
+ end
112
+ end