airbrakeV4rails5 4.3.8
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.
- checksums.yaml +7 -0
- data/CHANGELOG +1716 -0
- data/Gemfile +3 -0
- data/Guardfile +6 -0
- data/INSTALL +20 -0
- data/LICENSE +61 -0
- data/README.md +148 -0
- data/README_FOR_HEROKU_ADDON.md +102 -0
- data/Rakefile +179 -0
- data/TESTED_AGAINST +7 -0
- data/airbrake.gemspec +41 -0
- data/bin/airbrake +12 -0
- data/features/metal.feature +34 -0
- data/features/rack.feature +60 -0
- data/features/rails.feature +324 -0
- data/features/rake.feature +33 -0
- data/features/sinatra.feature +126 -0
- data/features/step_definitions/file_steps.rb +14 -0
- data/features/step_definitions/rack_steps.rb +27 -0
- data/features/step_definitions/rails_application_steps.rb +267 -0
- data/features/step_definitions/rake_steps.rb +22 -0
- data/features/support/airbrake_shim.rb.template +11 -0
- data/features/support/aruba.rb +5 -0
- data/features/support/env.rb +39 -0
- data/features/support/matchers.rb +35 -0
- data/features/support/rails.rb +156 -0
- data/features/support/rake/Rakefile +77 -0
- data/features/user_informer.feature +57 -0
- data/generators/airbrake/airbrake_generator.rb +94 -0
- data/generators/airbrake/lib/insert_commands.rb +34 -0
- data/generators/airbrake/lib/rake_commands.rb +24 -0
- data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
- data/generators/airbrake/templates/capistrano_hook.rb +6 -0
- data/generators/airbrake/templates/initializer.rb +4 -0
- data/install.rb +1 -0
- data/lib/airbrake.rb +191 -0
- data/lib/airbrake/backtrace.rb +103 -0
- data/lib/airbrake/capistrano.rb +103 -0
- data/lib/airbrake/capistrano3.rb +3 -0
- data/lib/airbrake/cli/client.rb +76 -0
- data/lib/airbrake/cli/options.rb +45 -0
- data/lib/airbrake/cli/printer.rb +33 -0
- data/lib/airbrake/cli/project.rb +17 -0
- data/lib/airbrake/cli/project_factory.rb +33 -0
- data/lib/airbrake/cli/runner.rb +49 -0
- data/lib/airbrake/cli/validator.rb +8 -0
- data/lib/airbrake/configuration.rb +366 -0
- data/lib/airbrake/jobs/send_job.rb +7 -0
- data/lib/airbrake/notice.rb +411 -0
- data/lib/airbrake/rack.rb +64 -0
- data/lib/airbrake/rails.rb +45 -0
- data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
- data/lib/airbrake/rails/controller_methods.rb +146 -0
- data/lib/airbrake/rails/error_lookup.rb +35 -0
- data/lib/airbrake/rails/middleware.rb +63 -0
- data/lib/airbrake/rails3_tasks.rb +126 -0
- data/lib/airbrake/railtie.rb +44 -0
- data/lib/airbrake/rake_handler.rb +75 -0
- data/lib/airbrake/response.rb +29 -0
- data/lib/airbrake/sender.rb +213 -0
- data/lib/airbrake/shared_tasks.rb +59 -0
- data/lib/airbrake/sidekiq.rb +8 -0
- data/lib/airbrake/sinatra.rb +40 -0
- data/lib/airbrake/tasks.rb +81 -0
- data/lib/airbrake/tasks/airbrake.cap +28 -0
- data/lib/airbrake/user_informer.rb +36 -0
- data/lib/airbrake/utils/params_cleaner.rb +141 -0
- data/lib/airbrake/utils/rack_filters.rb +45 -0
- data/lib/airbrake/version.rb +3 -0
- data/lib/airbrake_tasks.rb +62 -0
- data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
- data/lib/templates/rescue.erb +91 -0
- data/rails/init.rb +1 -0
- data/resources/README.md +34 -0
- data/resources/airbrake_2_4.xsd +89 -0
- data/resources/airbrake_3_0.json +52 -0
- data/resources/ca-bundle.crt +3376 -0
- data/script/integration_test.rb +35 -0
- data/test/airbrake_tasks_test.rb +161 -0
- data/test/backtrace_test.rb +215 -0
- data/test/capistrano_test.rb +44 -0
- data/test/configuration_test.rb +303 -0
- data/test/controller_methods_test.rb +230 -0
- data/test/helper.rb +233 -0
- data/test/integration.rb +13 -0
- data/test/integration/catcher_test.rb +371 -0
- data/test/logger_test.rb +79 -0
- data/test/notice_test.rb +494 -0
- data/test/notifier_test.rb +288 -0
- data/test/params_cleaner_test.rb +204 -0
- data/test/rack_test.rb +62 -0
- data/test/rails_initializer_test.rb +36 -0
- data/test/recursion_test.rb +10 -0
- data/test/response_test.rb +18 -0
- data/test/sender_test.rb +335 -0
- data/test/support/response_shim.xml +4 -0
- data/test/user_informer_test.rb +29 -0
- metadata +469 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'airbrake'
|
2
|
+
require File.join(File.dirname(__FILE__), 'shared_tasks')
|
3
|
+
|
4
|
+
namespace :airbrake do
|
5
|
+
desc "Verify your gem installation by sending a test exception to the airbrake service"
|
6
|
+
task :test => ['airbrake:log_stdout', :environment] do
|
7
|
+
RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
|
8
|
+
|
9
|
+
Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
|
10
|
+
|
11
|
+
class AirbrakeTestingException < RuntimeError; end
|
12
|
+
|
13
|
+
unless Airbrake.configuration.api_key
|
14
|
+
puts "Airbrake needs an API key configured! Check the README to see how to add it."
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
Airbrake.configuration.development_environments = []
|
19
|
+
|
20
|
+
catcher = Airbrake::Rails::ActionControllerCatcher
|
21
|
+
in_controller = ApplicationController.included_modules.include?(catcher)
|
22
|
+
in_base = ActionController::Base.included_modules.include?(catcher)
|
23
|
+
if !in_controller || !in_base
|
24
|
+
puts "Rails initialization did not occur"
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "Configuration:"
|
29
|
+
Airbrake.configuration.to_hash.each do |key, value|
|
30
|
+
puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
|
31
|
+
end
|
32
|
+
|
33
|
+
unless defined?(ApplicationController)
|
34
|
+
puts "No ApplicationController found"
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
puts 'Setting up the Controller.'
|
39
|
+
class ApplicationController
|
40
|
+
# This is to bypass any filters that may prevent access to the action.
|
41
|
+
prepend_before_filter :test_airbrake
|
42
|
+
def test_airbrake
|
43
|
+
puts "Raising '#{exception_class.name}' to simulate application failure."
|
44
|
+
raise exception_class.new, 'Testing airbrake via "rake airbrake:test". If you can see this, it works.'
|
45
|
+
end
|
46
|
+
|
47
|
+
def rescue_action(exception)
|
48
|
+
rescue_action_in_public exception
|
49
|
+
end
|
50
|
+
|
51
|
+
# Ensure we actually have an action to go to.
|
52
|
+
def verify; end
|
53
|
+
|
54
|
+
def consider_all_requests_local
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def local_request?
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def exception_class
|
63
|
+
exception_name = ENV['EXCEPTION'] || "AirbrakeTestingException"
|
64
|
+
exception_name.split("::").inject(Object){|klass, name| klass.const_get(name)}
|
65
|
+
rescue
|
66
|
+
Object.const_set(exception_name.gsub(/:+/, "_"), Class.new(Exception))
|
67
|
+
end
|
68
|
+
|
69
|
+
def logger
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
class AirbrakeVerificationController < ApplicationController; end
|
74
|
+
|
75
|
+
puts 'Processing request.'
|
76
|
+
request = ActionController::TestRequest.new("REQUEST_URI" => "/airbrake_verification_controller")
|
77
|
+
response = ActionController::TestResponse.new
|
78
|
+
AirbrakeVerificationController.new.process(request, response)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :airbrake do
|
2
|
+
desc <<-DESC
|
3
|
+
Notify Airbrake of the deployment by running the notification on the REMOTE machine.
|
4
|
+
- Run remotely so we use remote API keys, environment, etc.
|
5
|
+
DESC
|
6
|
+
task :deploy do
|
7
|
+
# update scm state to get the repository information
|
8
|
+
invoke "#{scm}:update"
|
9
|
+
on roles(:all) do
|
10
|
+
within release_path do
|
11
|
+
# XXX: Invoking deploy:set_rails_env would set :rails_env to proper
|
12
|
+
# value, but that would make us depend on capistrano-rails
|
13
|
+
with rails_env: fetch(:rails_env, fetch(:stage)) do
|
14
|
+
# Compose the command notify_command
|
15
|
+
airbrake_env = fetch(:airbrake_env, fetch(:rails_env, fetch(:stage)))
|
16
|
+
notify_command = "airbrake:deploy"
|
17
|
+
notify_command << " TO=#{airbrake_env}"
|
18
|
+
notify_command << " REVISION=#{fetch(:current_revision)} REPO=#{fetch(:repo_url)}"
|
19
|
+
notify_command << " USER=#{local_user.strip.shellescape}"
|
20
|
+
|
21
|
+
info "Notifying Airbrake of Deploy (#{notify_command})"
|
22
|
+
execute :rake, notify_command
|
23
|
+
info "Airbrake Notification Complete."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Airbrake
|
2
|
+
class UserInformer
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def replacement(with)
|
8
|
+
Airbrake.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
dup._call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
def _call(env)
|
16
|
+
status, headers, body = @app.call(env)
|
17
|
+
|
18
|
+
if env['airbrake.error_id'] && Airbrake.configuration.user_information
|
19
|
+
new_body = []
|
20
|
+
replace = replacement(env['airbrake.error_id'])
|
21
|
+
body.each do |chunk|
|
22
|
+
new_body << chunk.gsub("<!-- AIRBRAKE ERROR -->", replace)
|
23
|
+
end
|
24
|
+
body.close if body.respond_to?(:close)
|
25
|
+
headers['Content-Length'] = new_body.inject(0){|sum, x| sum + x.bytesize}.to_s
|
26
|
+
body = new_body
|
27
|
+
end
|
28
|
+
|
29
|
+
[status, headers, body]
|
30
|
+
|
31
|
+
ensure
|
32
|
+
body.close if body && body.respond_to?(:close) && $!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Utils
|
3
|
+
class ParamsCleaner
|
4
|
+
attr_writer :blacklist_filters, :whitelist_filters, :to_clean
|
5
|
+
attr_reader :parameters, :cgi_data, :session_data
|
6
|
+
|
7
|
+
# Public: Initialize a new Airbrake::Utils::ParamsCleaner
|
8
|
+
#
|
9
|
+
# opts - The Hash options that contain filters and params (default: {}):
|
10
|
+
# :blacklist_filters - The Array of param keys that should be filtered
|
11
|
+
# :whitelist_filters - The Array of param keys that shouldn't be filtered
|
12
|
+
# :to_clean - The Hash of unfiltered params
|
13
|
+
# :blacklist_filters take precedence over the :whitelist_filters
|
14
|
+
def initialize(opts = {})
|
15
|
+
@blacklist_filters = (opts[:blacklist_filters] || []).flatten
|
16
|
+
@blacklist_filters.map!{|f| f.is_a?(Symbol) ? f.to_s : f }
|
17
|
+
@whitelist_filters = (opts[:whitelist_filters] || []).flatten
|
18
|
+
@whitelist_filters.map!{|f| f.is_a?(Symbol) ? f.to_s : f }
|
19
|
+
@to_clean = opts[:to_clean]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Takes the params to_clean passed in an initializer
|
23
|
+
# and filters them out by filters passed.
|
24
|
+
#
|
25
|
+
# Also normalizes unserializable data.
|
26
|
+
#
|
27
|
+
# Returns self, so that :parameters, :cgi_data, :session_data
|
28
|
+
# could be extracted
|
29
|
+
def clean
|
30
|
+
clean_parameters
|
31
|
+
clean_session_data
|
32
|
+
clean_cgi_data
|
33
|
+
clean_rack_request_data
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def clean_parameters
|
41
|
+
return unless @to_clean[:parameters]
|
42
|
+
|
43
|
+
@parameters = if any_filters?
|
44
|
+
filter(clean_unserializable_data(@to_clean[:parameters]))
|
45
|
+
else
|
46
|
+
clean_unserializable_data(@to_clean[:parameters])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def clean_cgi_data
|
51
|
+
return unless @to_clean[:cgi_data]
|
52
|
+
|
53
|
+
@cgi_data = if any_filters?
|
54
|
+
filter(clean_unserializable_data(@to_clean[:cgi_data]))
|
55
|
+
else
|
56
|
+
clean_unserializable_data(@to_clean[:cgi_data])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def clean_session_data
|
61
|
+
return unless @to_clean[:session_data]
|
62
|
+
|
63
|
+
@session_data = if any_filters?
|
64
|
+
filter(clean_unserializable_data(@to_clean[:session_data]))
|
65
|
+
else
|
66
|
+
clean_unserializable_data(@to_clean[:session_data])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def clean_rack_request_data
|
71
|
+
if @cgi_data
|
72
|
+
@cgi_data.reject! do |key, val|
|
73
|
+
Airbrake::FILTERED_RACK_VARS.include?(key) || Airbrake::SENSITIVE_ENV_VARS.any?{|re| re.match(key)}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def any_filters?
|
79
|
+
@blacklist_filters.any? || @whitelist_filters.any?
|
80
|
+
end
|
81
|
+
|
82
|
+
def filter_key?(key)
|
83
|
+
blacklist_key?(key) || !whitelist_key?(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def blacklist_key?(key)
|
87
|
+
@blacklist_filters.any? do |filter|
|
88
|
+
key == filter || filter.is_a?(Regexp) && filter.match(key)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def whitelist_key?(key)
|
93
|
+
return true if @whitelist_filters.empty?
|
94
|
+
@whitelist_filters.any? do |filter|
|
95
|
+
key == filter || filter.is_a?(Regexp) && filter.match(key)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def filter(hash)
|
100
|
+
return hash unless hash.is_a?(Hash)
|
101
|
+
|
102
|
+
hash.each do |key, value|
|
103
|
+
if filter_key?(key)
|
104
|
+
hash[key] = "[FILTERED]"
|
105
|
+
elsif value.respond_to?(:to_hash)
|
106
|
+
filter(value)
|
107
|
+
elsif value.is_a?(Array)
|
108
|
+
hash[key] = value.map { |item| filter(item) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Removes non-serializable data. Allowed data types are strings, arrays,
|
114
|
+
# and hashes. All other types are converted to strings.
|
115
|
+
def clean_unserializable_data(data, stack = [])
|
116
|
+
return "[possible infinite recursion halted]" if stack.any?{|item| item == data.object_id }
|
117
|
+
if data.is_a?(String)
|
118
|
+
data
|
119
|
+
elsif data.is_a?(Hash)
|
120
|
+
data.inject({}) do |result, (key, value)|
|
121
|
+
result.merge!(key => clean_unserializable_data(value, stack + [data.object_id]))
|
122
|
+
end
|
123
|
+
elsif data.respond_to?(:to_hash)
|
124
|
+
data.to_hash.inject({}) do |result, (key, value)|
|
125
|
+
result.merge!(key => clean_unserializable_data(value, stack + [data.object_id]))
|
126
|
+
end
|
127
|
+
elsif data.respond_to?(:collect) && !data.respond_to?(:readlines)
|
128
|
+
data = data.collect do |value|
|
129
|
+
clean_unserializable_data(value, stack + [data.object_id])
|
130
|
+
end
|
131
|
+
elsif data.respond_to?(:to_ary)
|
132
|
+
data = data.to_ary.collect do |value|
|
133
|
+
clean_unserializable_data(value, stack + [data.object_id])
|
134
|
+
end
|
135
|
+
elsif data.respond_to?(:to_s)
|
136
|
+
data.nil? ? nil : data.to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Airbrake
|
2
|
+
SENSITIVE_RACK_VARS = %w(
|
3
|
+
HTTP_X_CSRF_TOKEN
|
4
|
+
HTTP_COOKIE
|
5
|
+
HTTP_AUTHORIZATION
|
6
|
+
|
7
|
+
action_dispatch.request.unsigned_session_cookie
|
8
|
+
action_dispatch.cookies
|
9
|
+
action_dispatch.unsigned_session_cookie
|
10
|
+
action_dispatch.secret_key_base
|
11
|
+
action_dispatch.signed_cookie_salt
|
12
|
+
action_dispatch.encrypted_cookie_salt
|
13
|
+
action_dispatch.encrypted_signed_cookie_salt
|
14
|
+
action_dispatch.http_auth_salt
|
15
|
+
action_dispatch.secret_token
|
16
|
+
|
17
|
+
rack.request.cookie_hash
|
18
|
+
rack.request.cookie_string
|
19
|
+
rack.request.form_vars
|
20
|
+
|
21
|
+
rack.session
|
22
|
+
rack.session.options
|
23
|
+
)
|
24
|
+
|
25
|
+
RACK_VARS_CONTAINING_INSTANCES = %w(
|
26
|
+
action_controller.instance
|
27
|
+
|
28
|
+
action_dispatch.backtrace_cleaner
|
29
|
+
action_dispatch.routes
|
30
|
+
action_dispatch.logger
|
31
|
+
action_dispatch.key_generator
|
32
|
+
|
33
|
+
rack-cache.storage
|
34
|
+
|
35
|
+
rack.errors
|
36
|
+
rack.input
|
37
|
+
)
|
38
|
+
|
39
|
+
SENSITIVE_ENV_VARS = [
|
40
|
+
/secret/i,
|
41
|
+
/password/i
|
42
|
+
]
|
43
|
+
|
44
|
+
FILTERED_RACK_VARS = SENSITIVE_RACK_VARS + SENSITIVE_ENV_VARS + RACK_VARS_CONTAINING_INSTANCES
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
# Capistrano tasks for notifying Airbrake of deploys
|
5
|
+
module AirbrakeTasks
|
6
|
+
|
7
|
+
# Alerts Airbrake of a deploy.
|
8
|
+
#
|
9
|
+
# @param [Hash] opts Data about the deploy that is set to Airbrake
|
10
|
+
#
|
11
|
+
# @option opts [String] :rails_env Environment of the deploy (production, staging)
|
12
|
+
# @option opts [String] :scm_revision The given revision/sha that is being deployed
|
13
|
+
# @option opts [String] :scm_repository Address of your repository to help with code lookups
|
14
|
+
# @option opts [String] :local_username Who is deploying
|
15
|
+
def self.deploy(opts = {})
|
16
|
+
unless Airbrake.configuration.api_key =~ /\S/
|
17
|
+
puts "I don't seem to be configured with an API key. Please check your configuration."
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
21
|
+
unless opts[:rails_env] =~ /\S/
|
22
|
+
puts "I don't know to which Rails environment you are deploying (use the TO=production option)."
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
dry_run = opts.delete(:dry_run)
|
27
|
+
params = {'api_key' => Airbrake.configuration.api_key}
|
28
|
+
opts.each {|k,v| params["deploy[#{k}]"] = v }
|
29
|
+
|
30
|
+
host = Airbrake.configuration.host || 'api.airbrake.io'
|
31
|
+
port = Airbrake.configuration.port
|
32
|
+
|
33
|
+
proxy = Net::HTTP.Proxy(Airbrake.configuration.proxy_host,
|
34
|
+
Airbrake.configuration.proxy_port,
|
35
|
+
Airbrake.configuration.proxy_user,
|
36
|
+
Airbrake.configuration.proxy_pass)
|
37
|
+
http = proxy.new(host, port)
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
# Handle Security
|
42
|
+
if Airbrake.configuration.secure?
|
43
|
+
http.use_ssl = true
|
44
|
+
http.ca_file = Airbrake.configuration.ca_bundle_path
|
45
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
46
|
+
end
|
47
|
+
|
48
|
+
post = Net::HTTP::Post.new("/deploys.txt")
|
49
|
+
post.set_form_data(params)
|
50
|
+
|
51
|
+
if dry_run
|
52
|
+
puts http.inspect, params.inspect
|
53
|
+
return true
|
54
|
+
else
|
55
|
+
response = http.request(post)
|
56
|
+
|
57
|
+
puts response.body
|
58
|
+
return Net::HTTPSuccess === response
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class AirbrakeGenerator < Rails::Generators::Base
|
4
|
+
desc "Creates the Airbrake initializer file at config/initializers/airbrake.rb"
|
5
|
+
|
6
|
+
class_option :api_key, :aliases => "-k", :type => :string,
|
7
|
+
:desc => "Your Airbrake API key"
|
8
|
+
|
9
|
+
class_option :heroku, :type => :boolean,
|
10
|
+
:desc => "Use the Heroku addon to provide your Airbrake API key"
|
11
|
+
|
12
|
+
class_option :app, :aliases => "-a", :type => :string,
|
13
|
+
:desc => "Your Heroku app name (only required if deploying to >1 Heroku app)"
|
14
|
+
|
15
|
+
class_option :secure, :type => :boolean,
|
16
|
+
:desc => "Use SSL connection"
|
17
|
+
|
18
|
+
class_option :test_mode, :aliases => "-t", :type => :boolean,
|
19
|
+
:desc => "Use Airbrake in test mode"
|
20
|
+
|
21
|
+
def self.source_root
|
22
|
+
@_airbrake_source_root ||= File.expand_path("../../../../../generators/airbrake/templates", __FILE__)
|
23
|
+
end
|
24
|
+
|
25
|
+
def install
|
26
|
+
ensure_api_key_was_configured
|
27
|
+
ensure_plugin_is_not_present
|
28
|
+
append_capistrano_hook if capistrano_present?
|
29
|
+
generate_initializer unless api_key_configured?
|
30
|
+
determine_api_key if heroku?
|
31
|
+
test_airbrake
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def ensure_api_key_was_configured
|
37
|
+
if !options[:api_key] && !options[:heroku] && !api_key_configured?
|
38
|
+
puts "Must pass --api-key or --heroku or create config/initializers/airbrake.rb"
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def ensure_plugin_is_not_present
|
44
|
+
if plugin_is_present?
|
45
|
+
puts "You must first remove the airbrake plugin. Please run: script/plugin remove airbrake"
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def capistrano_present?
|
51
|
+
!Gem.loaded_specs['capistrano'].nil? &&
|
52
|
+
File.exists?('config/deploy.rb') &&
|
53
|
+
File.exists?('Capfile')
|
54
|
+
end
|
55
|
+
|
56
|
+
def append_capistrano_hook
|
57
|
+
if capistrano_version < Gem::Version.new('3')
|
58
|
+
append_file('config/deploy.rb', capistrano2_hook)
|
59
|
+
else
|
60
|
+
append_file('config/deploy.rb', capistrano3_hook)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def capistrano_version
|
65
|
+
Gem.loaded_specs['capistrano'].version
|
66
|
+
end
|
67
|
+
|
68
|
+
def capistrano2_hook
|
69
|
+
<<-HOOK
|
70
|
+
|
71
|
+
require './config/boot'
|
72
|
+
require 'airbrake/capistrano'
|
73
|
+
HOOK
|
74
|
+
end
|
75
|
+
|
76
|
+
def capistrano3_hook
|
77
|
+
<<-HOOK
|
78
|
+
|
79
|
+
require 'airbrake/capistrano3'
|
80
|
+
after "deploy:finished", "airbrake:deploy"
|
81
|
+
HOOK
|
82
|
+
end
|
83
|
+
|
84
|
+
def api_key_expression
|
85
|
+
if options[:api_key]
|
86
|
+
"'#{options[:api_key]}'"
|
87
|
+
elsif options[:heroku]
|
88
|
+
"ENV['AIRBRAKE_API_KEY']"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def generate_initializer
|
93
|
+
template 'initializer.rb', 'config/initializers/airbrake.rb'
|
94
|
+
end
|
95
|
+
|
96
|
+
def determine_api_key
|
97
|
+
puts "Attempting to determine your API Key from Heroku..."
|
98
|
+
ENV['AIRBRAKE_API_KEY'] = heroku_api_key
|
99
|
+
if ENV['AIRBRAKE_API_KEY'] =~ /\S/
|
100
|
+
puts "... Done."
|
101
|
+
puts "Heroku's Airbrake API Key is '#{ENV['AIRBRAKE_API_KEY']}'"
|
102
|
+
else
|
103
|
+
puts "... Failed."
|
104
|
+
puts "WARNING: We were unable to detect the Airbrake API Key from your Heroku environment."
|
105
|
+
puts "Your Heroku application environment may not be configured correctly."
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def heroku_var(var,app_name = nil)
|
111
|
+
app = app_name ? "-a #{app_name}" : ''
|
112
|
+
`heroku config:get #{var} #{app}`
|
113
|
+
end
|
114
|
+
|
115
|
+
def heroku_api_key
|
116
|
+
heroku_var("AIRBRAKE_API_KEY",options[:app]).split.find {|x| x if x =~ /\S/}
|
117
|
+
end
|
118
|
+
|
119
|
+
def secure?
|
120
|
+
options[:secure]
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_mode?
|
124
|
+
options[:test_mode]
|
125
|
+
end
|
126
|
+
|
127
|
+
def heroku?
|
128
|
+
options[:heroku] ||
|
129
|
+
system("grep AIRBRAKE_API_KEY config/initializers/airbrake.rb") ||
|
130
|
+
system("grep AIRBRAKE_API_KEY config/environment.rb")
|
131
|
+
end
|
132
|
+
|
133
|
+
def api_key_configured?
|
134
|
+
File.exists?('config/initializers/airbrake.rb')
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_airbrake
|
138
|
+
puts run("rake airbrake:test")
|
139
|
+
end
|
140
|
+
|
141
|
+
def plugin_is_present?
|
142
|
+
File.exists?('vendor/plugins/airbrake')
|
143
|
+
end
|
144
|
+
|
145
|
+
def configuration_output
|
146
|
+
output = <<-eos
|
147
|
+
Airbrake.configure do |config|
|
148
|
+
config.api_key = #{api_key_expression}
|
149
|
+
eos
|
150
|
+
|
151
|
+
output << " config.secure = true\n" if secure?
|
152
|
+
output << " config.test_mode = true\n" if test_mode?
|
153
|
+
output << "end"
|
154
|
+
end
|
155
|
+
end
|