honeybadger 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Gemfile +13 -0
  2. data/Gemfile.lock +114 -0
  3. data/Guardfile +5 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +271 -0
  6. data/Rakefile +261 -0
  7. data/SUPPORTED_RAILS_VERSIONS +26 -0
  8. data/TESTING.md +33 -0
  9. data/features/metal.feature +18 -0
  10. data/features/rack.feature +56 -0
  11. data/features/rails.feature +211 -0
  12. data/features/rake.feature +27 -0
  13. data/features/sinatra.feature +29 -0
  14. data/features/step_definitions/file_steps.rb +10 -0
  15. data/features/step_definitions/metal_steps.rb +23 -0
  16. data/features/step_definitions/rack_steps.rb +23 -0
  17. data/features/step_definitions/rails_application_steps.rb +394 -0
  18. data/features/step_definitions/rake_steps.rb +17 -0
  19. data/features/support/env.rb +17 -0
  20. data/features/support/honeybadger_shim.rb.template +8 -0
  21. data/features/support/rails.rb +201 -0
  22. data/features/support/rake/Rakefile +68 -0
  23. data/features/support/terminal.rb +107 -0
  24. data/generators/honeybadger/honeybadger_generator.rb +94 -0
  25. data/generators/honeybadger/lib/insert_commands.rb +34 -0
  26. data/generators/honeybadger/lib/rake_commands.rb +24 -0
  27. data/generators/honeybadger/templates/capistrano_hook.rb +6 -0
  28. data/generators/honeybadger/templates/honeybadger_tasks.rake +25 -0
  29. data/generators/honeybadger/templates/initializer.rb +6 -0
  30. data/honeybadger.gemspec +109 -0
  31. data/lib/honeybadger.rb +162 -0
  32. data/lib/honeybadger/backtrace.rb +123 -0
  33. data/lib/honeybadger/capistrano.rb +43 -0
  34. data/lib/honeybadger/configuration.rb +273 -0
  35. data/lib/honeybadger/notice.rb +314 -0
  36. data/lib/honeybadger/rack.rb +55 -0
  37. data/lib/honeybadger/rails.rb +34 -0
  38. data/lib/honeybadger/rails/action_controller_catcher.rb +30 -0
  39. data/lib/honeybadger/rails/controller_methods.rb +69 -0
  40. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +29 -0
  41. data/lib/honeybadger/rails3_tasks.rb +84 -0
  42. data/lib/honeybadger/railtie.rb +45 -0
  43. data/lib/honeybadger/rake_handler.rb +65 -0
  44. data/lib/honeybadger/sender.rb +120 -0
  45. data/lib/honeybadger/shared_tasks.rb +36 -0
  46. data/lib/honeybadger/tasks.rb +82 -0
  47. data/lib/honeybadger_tasks.rb +65 -0
  48. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +99 -0
  49. data/rails/init.rb +1 -0
  50. data/resources/README.md +34 -0
  51. data/resources/ca-bundle.crt +3376 -0
  52. data/script/integration_test.rb +38 -0
  53. data/test/test_helper.rb +143 -0
  54. data/test/unit/backtrace_test.rb +180 -0
  55. data/test/unit/capistrano_test.rb +34 -0
  56. data/test/unit/configuration_test.rb +201 -0
  57. data/test/unit/honeybadger_tasks_test.rb +163 -0
  58. data/test/unit/logger_test.rb +72 -0
  59. data/test/unit/notice_test.rb +406 -0
  60. data/test/unit/notifier_test.rb +245 -0
  61. data/test/unit/rack_test.rb +56 -0
  62. data/test/unit/rails/action_controller_catcher_test.rb +300 -0
  63. data/test/unit/rails_test.rb +35 -0
  64. data/test/unit/sender_test.rb +257 -0
  65. metadata +315 -0
@@ -0,0 +1,8 @@
1
+ require 'sham_rack'
2
+
3
+ ShamRack.at("api.honeybadger.io") do |env|
4
+ response = <<-end_json
5
+ {"id":"123456789"}
6
+ end_json
7
+ ["200 OK", { "Content-type" => "application/json" }, [response]]
8
+ end
@@ -0,0 +1,201 @@
1
+ module RailsHelpers
2
+ def rails_root_exists?
3
+ File.exists?(environment_path)
4
+ end
5
+
6
+ def application_controller_filename
7
+ controller_filename = File.join(rails_root, 'app', 'controllers', "application_controller.rb")
8
+ end
9
+
10
+ def rails3?
11
+ rails_version =~ /^3/
12
+ end
13
+
14
+ def rails_root
15
+ LOCAL_RAILS_ROOT
16
+ end
17
+
18
+ def rails_uses_rack?
19
+ rails3? || rails_version =~ /^2\.3/
20
+ end
21
+
22
+ def rails_version
23
+ @rails_version ||= begin
24
+ if ENV["RAILS_VERSION"]
25
+ ENV["RAILS_VERSION"]
26
+ elsif bundler_manages_gems?
27
+ rails_version = open(gemfile_path).read.match(/gem.*rails["'].*["'](.+)["']/)[1]
28
+ else
29
+ environment_file = File.join(rails_root, 'config', 'environment.rb')
30
+ rails_version = `grep RAILS_GEM_VERSION #{environment_file}`.match(/[\d.]+/)[0]
31
+ end
32
+ end
33
+ end
34
+
35
+ def bundler_manages_gems?
36
+ File.exists?(gemfile_path)
37
+ end
38
+
39
+ def gemfile_path
40
+ gemfile = File.join(rails_root, 'Gemfile')
41
+ end
42
+
43
+ def rails_manages_gems?
44
+ rails_version =~ /^2\.[123]/
45
+ end
46
+
47
+ def rails_supports_initializers?
48
+ rails3? || rails_version =~ /^2\./
49
+ end
50
+
51
+ def rails_finds_generators_in_gems?
52
+ rails3? || rails_version =~ /^2\./
53
+ end
54
+
55
+ def version_string
56
+ ENV['RAILS_VERSION'] || `tail -n 1 SUPPORTED_RAILS_VERSIONS` # use latest version if ENV["RAILS_VERSION"] is undefined
57
+ end
58
+
59
+ def environment_path
60
+ File.join(rails_root, 'config', 'environment.rb')
61
+ end
62
+
63
+ def rakefile_path
64
+ File.join(rails_root, 'Rakefile')
65
+ end
66
+
67
+ def bundle_gem(gem_name, version = nil)
68
+ File.open(gemfile_path, 'a') do |file|
69
+ gem = "gem '#{gem_name}'"
70
+ gem += ", '#{version}'" if version
71
+ file.puts(gem)
72
+ end
73
+ end
74
+
75
+ def config_gem(gem_name, version = nil)
76
+ run = "Rails::Initializer.run do |config|"
77
+ insert = " config.gem '#{gem_name}'"
78
+ insert += ", :version => '#{version}'" if version
79
+ content = File.read(environment_path)
80
+ content = "require 'thread'\n#{content}"
81
+ if content.sub!(run, "#{run}\n#{insert}")
82
+ File.open(environment_path, 'wb') { |file| file.write(content) }
83
+ else
84
+ raise "Couldn't find #{run.inspect} in #{environment_path}"
85
+ end
86
+ end
87
+
88
+ def config_gem_dependencies
89
+ insert = <<-END
90
+ if Gem::VERSION >= "1.3.6"
91
+ module Rails
92
+ class GemDependency
93
+ def requirement
94
+ r = super
95
+ (r == Gem::Requirement.default) ? nil : r
96
+ end
97
+ end
98
+ end
99
+ end
100
+ END
101
+ run = "Rails::Initializer.run do |config|"
102
+ content = File.read(environment_path)
103
+ if content.sub!(run, "#{insert}\n#{run}")
104
+ File.open(environment_path, 'wb') { |file| file.write(content) }
105
+ else
106
+ raise "Couldn't find #{run.inspect} in #{environment_path}"
107
+ end
108
+ end
109
+
110
+ def require_thread
111
+ content = File.read(rakefile_path)
112
+ content = "require 'thread'\n#{content}"
113
+ File.open(rakefile_path, 'wb') { |file| file.write(content) }
114
+ end
115
+
116
+ def perform_request(uri, environment = 'production')
117
+ if rails3?
118
+ request_script = <<-SCRIPT
119
+ require File.expand_path('../config/environment', __FILE__)
120
+
121
+
122
+ env = Rack::MockRequest.env_for(#{uri.inspect})
123
+ response = RailsRoot::Application.call(env)
124
+
125
+
126
+ response = response.last if response.last.is_a?(ActionDispatch::Response)
127
+
128
+ if response.is_a?(Array)
129
+ puts response.join
130
+ else
131
+ puts response.body
132
+ end
133
+ SCRIPT
134
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
135
+ @terminal.cd(rails_root)
136
+ @terminal.run("ruby -rthread ./script/rails runner -e #{environment} request.rb")
137
+ elsif rails_uses_rack?
138
+ request_script = <<-SCRIPT
139
+ require File.expand_path('../config/environment', __FILE__)
140
+
141
+ env = Rack::MockRequest.env_for(#{uri.inspect})
142
+ app = Rack::Lint.new(ActionController::Dispatcher.new)
143
+
144
+ status, headers, body = app.call(env)
145
+
146
+ response = ""
147
+ if body.respond_to?(:to_str)
148
+ response << body
149
+ else
150
+ body.each { |part| response << part }
151
+ end
152
+
153
+ puts response
154
+ SCRIPT
155
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
156
+ @terminal.cd(rails_root)
157
+ @terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
158
+ else
159
+ uri = URI.parse(uri)
160
+ request_script = <<-SCRIPT
161
+ require 'cgi'
162
+ class CGIWrapper < CGI
163
+ def initialize(*args)
164
+ @env_table = {}
165
+ @stdinput = $stdin
166
+ super(*args)
167
+ end
168
+ attr_reader :env_table
169
+ end
170
+ $stdin = StringIO.new("")
171
+ cgi = CGIWrapper.new
172
+ cgi.env_table.update({
173
+ 'HTTPS' => 'off',
174
+ 'REQUEST_METHOD' => "GET",
175
+ 'HTTP_HOST' => #{[uri.host, uri.port].join(':').inspect},
176
+ 'SERVER_PORT' => #{uri.port.inspect},
177
+ 'REQUEST_URI' => #{uri.request_uri.inspect},
178
+ 'PATH_INFO' => #{uri.path.inspect},
179
+ 'QUERY_STRING' => #{uri.query.inspect}
180
+ })
181
+ require 'dispatcher' unless defined?(ActionController::Dispatcher)
182
+ Dispatcher.dispatch(cgi)
183
+ SCRIPT
184
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
185
+ @terminal.cd(rails_root)
186
+ @terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
187
+ end
188
+ end
189
+
190
+ def monkeypatch_old_version
191
+ monkeypatchin= <<-MONKEYPATCHIN
192
+
193
+ MissingSourceFile::REGEXPS << [/^cannot load such file -- (.+)$/i, 1]
194
+
195
+ MONKEYPATCHIN
196
+
197
+ File.open(File.join(rails_root,"config","initializers", 'monkeypatchin.rb'), 'w') { |file| file.write(monkeypatchin) }
198
+ end
199
+ end
200
+
201
+ World(RailsHelpers)
@@ -0,0 +1,68 @@
1
+ # A test harness for RakeHandler
2
+ #
3
+ require 'rake'
4
+ require 'rubygems'
5
+ require 'honeybadger'
6
+ require 'honeybadger/rake_handler'
7
+
8
+ Honeybadger.configure do |c|
9
+ end
10
+
11
+ # Should catch exception
12
+ task :honeybadger do
13
+ Honeybadger.configuration.rescue_rake_exceptions = true
14
+ stub_tty_output(true)
15
+ raise_exception
16
+ end
17
+
18
+ # Should not catch exception
19
+ task :honeybadger_disabled do
20
+ Honeybadger.configuration.rescue_rake_exceptions = false
21
+ stub_tty_output(true)
22
+ raise_exception
23
+ end
24
+
25
+ # Should not catch exception as tty_output is true
26
+ task :honeybadger_autodetect_from_terminal do
27
+ Honeybadger.configuration.rescue_rake_exceptions = nil
28
+ stub_tty_output(true)
29
+ raise_exception
30
+ end
31
+
32
+ # Should catch exception as tty_output is false
33
+ task :honeybadger_autodetect_not_from_terminal do
34
+ Honeybadger.configuration.rescue_rake_exceptions = nil
35
+ stub_tty_output(false)
36
+ raise_exception
37
+ end
38
+
39
+ task :honeybadger_not_yet_configured do
40
+ Honeybadger.configuration.rescue_rake_exceptions = true
41
+ stub_tty_output(true)
42
+ stub_empty_sender
43
+ raise_exception
44
+ end
45
+
46
+ module Honeybadger
47
+ def self.notify_or_ignore(*args)
48
+ # TODO if you need to check more params, you'll have to use json.dump or something
49
+ $stderr.puts "honeybadger #{args[1][:component]}"
50
+ end
51
+ end
52
+
53
+ def stub_empty_sender
54
+ Honeybadger.sender = nil
55
+ end
56
+
57
+ def stub_tty_output(value)
58
+ Rake.application.instance_eval do
59
+ @tty_output_stub = value
60
+ def tty_output?
61
+ @tty_output_stub
62
+ end
63
+ end
64
+ end
65
+
66
+ def raise_exception
67
+ raise 'TEST'
68
+ end
@@ -0,0 +1,107 @@
1
+ require 'fileutils'
2
+
3
+ Before do
4
+ @terminal = Terminal.new
5
+ end
6
+
7
+ After do |story|
8
+ if story.failed?
9
+ # puts @terminal.output
10
+ end
11
+ end
12
+
13
+ class Terminal
14
+ attr_reader :output, :status
15
+ attr_accessor :environment_variables, :invoke_heroku_rake_tasks_locally
16
+
17
+ def initialize
18
+ @cwd = FileUtils.pwd
19
+ @output = ""
20
+ @status = 0
21
+ @logger = Logger.new(File.join(TEMP_DIR, 'terminal.log'))
22
+
23
+ @invoke_heroku_rake_tasks_locally = false
24
+
25
+ @environment_variables = {
26
+ "GEM_HOME" => LOCAL_GEM_ROOT,
27
+ "GEM_PATH" => "#{LOCAL_GEM_ROOT}:#{BUILT_GEM_ROOT}",
28
+ "PATH" => "#{gem_bin_path}:#{ENV['PATH']}"
29
+ }
30
+ end
31
+
32
+ def cd(directory)
33
+ @cwd = directory
34
+ end
35
+
36
+ def run(command)
37
+ command = optionally_invoke_heroku_rake_tasks_locally(command)
38
+
39
+ output << "#{command}\n"
40
+ FileUtils.cd(@cwd) do
41
+ # The ; forces ruby to shell out so the env settings work right
42
+ cmdline = "#{environment_settings} #{command} 2>&1 ; "
43
+ logger.debug(cmdline)
44
+ result = `#{cmdline}`
45
+ logger.debug(result)
46
+ output << result
47
+ end
48
+ @status = $?
49
+ end
50
+
51
+ def optionally_invoke_heroku_rake_tasks_locally(command)
52
+ if invoke_heroku_rake_tasks_locally
53
+ command.sub(/^heroku /, '')
54
+ else
55
+ command
56
+ end
57
+ end
58
+
59
+ def echo(string)
60
+ logger.debug(string)
61
+ end
62
+
63
+ def flush!
64
+ @output = ""
65
+ end
66
+
67
+ def build_and_install_gem(gemspec)
68
+ pkg_dir = File.join(TEMP_DIR, 'pkg')
69
+ FileUtils.mkdir_p(pkg_dir)
70
+ output = `gem build #{gemspec} 2>&1`
71
+ gem_file = Dir.glob("*.gem").first
72
+ unless gem_file
73
+ raise "Gem didn't build:\n#{output}"
74
+ end
75
+ target = File.join(pkg_dir, gem_file)
76
+ FileUtils.mv(gem_file, target)
77
+ install_gem_to(LOCAL_GEM_ROOT, target)
78
+ end
79
+
80
+ def install_gem(gem)
81
+ install_gem_to(LOCAL_GEM_ROOT, gem)
82
+ end
83
+
84
+ def uninstall_gem(gem)
85
+ `gem uninstall -i #{LOCAL_GEM_ROOT} #{gem}`
86
+ end
87
+
88
+ def prepend_path(path)
89
+ @environment_variables['PATH'] = path + ":" + @environment_variables['PATH']
90
+ end
91
+
92
+ private
93
+
94
+ def install_gem_to(root, gem)
95
+ `gem install -i #{root} --no-ri --no-rdoc #{gem}`
96
+ end
97
+
98
+ def environment_settings
99
+ @environment_variables.map { |key, value| "#{key}=#{value}" }.join(' ')
100
+ end
101
+
102
+ def gem_bin_path
103
+ File.join(LOCAL_GEM_ROOT, "bin")
104
+ end
105
+
106
+ attr_reader :logger
107
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb")
2
+ require File.expand_path(File.dirname(__FILE__) + "/lib/rake_commands.rb")
3
+
4
+ class HoneybadgerGenerator < Rails::Generator::Base
5
+ def add_options!(opt)
6
+ opt.on('-k', '--api-key=key', String, "Your Honeybadger API key") { |v| options[:api_key] = v}
7
+ opt.on('-h', '--heroku', "Use the Heroku addon to provide your Honeybadger API key") { |v| options[:heroku] = v}
8
+ opt.on('-a', '--app=myapp', String, "Your Heroku app name (only required if deploying to >1 Heroku app)") { |v| options[:app] = v}
9
+ end
10
+
11
+ def manifest
12
+ if !api_key_configured? && !options[:api_key] && !options[:heroku]
13
+ puts "Must pass --api-key or --heroku or create config/initializers/honeybadger.rb"
14
+ exit
15
+ end
16
+ if plugin_is_present?
17
+ puts "You must first remove the honeybadger plugin. Please run: script/plugin remove honeybadger"
18
+ exit
19
+ end
20
+ record do |m|
21
+ m.directory 'lib/tasks'
22
+ m.file 'honeybadger_tasks.rake', 'lib/tasks/honeybadger_tasks.rake'
23
+ if ['config/deploy.rb', 'Capfile'].all? { |file| File.exists?(file) }
24
+ m.append_to 'config/deploy.rb', capistrano_hook
25
+ end
26
+ if api_key_expression
27
+ if use_initializer?
28
+ m.template 'initializer.rb', 'config/initializers/honeybadger.rb',
29
+ :assigns => {:api_key => api_key_expression}
30
+ else
31
+ m.template 'initializer.rb', 'config/honeybadger.rb',
32
+ :assigns => {:api_key => api_key_expression}
33
+ m.append_to 'config/environment.rb', "require 'config/honeybadger'"
34
+ end
35
+ end
36
+ determine_api_key if heroku?
37
+ m.rake "honeybadger:test --trace", :generate_only => true
38
+ end
39
+ end
40
+
41
+ def api_key_expression
42
+ s = if options[:api_key]
43
+ "'#{options[:api_key]}'"
44
+ elsif options[:heroku]
45
+ "ENV['HONEYBADGER_API_KEY']"
46
+ end
47
+ end
48
+
49
+ def determine_api_key
50
+ puts "Attempting to determine your API Key from Heroku..."
51
+ ENV['HONEYBADGER_API_KEY'] = heroku_api_key
52
+ if ENV['HONEYBADGER_API_KEY'].blank?
53
+ puts "... Failed."
54
+ puts "WARNING: We were unable to detect the Honeybadger API Key from your Heroku environment."
55
+ puts "Your Heroku application environment may not be configured correctly."
56
+ exit 1
57
+ else
58
+ puts "... Done."
59
+ puts "Heroku's Honeybadger API Key is '#{ENV['HONEYBADGER_API_KEY']}'"
60
+ end
61
+ end
62
+
63
+ def heroku_var(var,app_name = nil)
64
+ app = app_name ? "--app #{app_name}" : ''
65
+ `heroku config #{app} | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
66
+ end
67
+
68
+ def heroku_api_key
69
+ heroku_var("(hoptoad|honeybadger)_api_key",options[:app]).split.find {|x| x unless x.blank?}
70
+ end
71
+
72
+ def heroku?
73
+ options[:heroku] ||
74
+ system("grep HONEYBADGER_API_KEY config/initializers/honeybadger.rb") ||
75
+ system("grep HONEYBADGER_API_KEY config/environment.rb")
76
+ end
77
+
78
+ def use_initializer?
79
+ Rails::VERSION::MAJOR > 1
80
+ end
81
+
82
+ def api_key_configured?
83
+ File.exists?('config/initializers/honeybadger.rb') ||
84
+ system("grep Honeybadger config/environment.rb")
85
+ end
86
+
87
+ def capistrano_hook
88
+ IO.read(source_path('capistrano_hook.rb'))
89
+ end
90
+
91
+ def plugin_is_present?
92
+ File.exists?('vendor/plugins/honeybadger')
93
+ end
94
+ end