honeybadger 5.0.2 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +713 -701
  3. data/LICENSE +19 -19
  4. data/README.md +57 -57
  5. data/TROUBLESHOOTING.md +3 -3
  6. data/bin/honeybadger +5 -5
  7. data/lib/honeybadger/agent.rb +488 -488
  8. data/lib/honeybadger/backend/base.rb +116 -116
  9. data/lib/honeybadger/backend/debug.rb +22 -22
  10. data/lib/honeybadger/backend/null.rb +29 -29
  11. data/lib/honeybadger/backend/server.rb +62 -62
  12. data/lib/honeybadger/backend/test.rb +46 -46
  13. data/lib/honeybadger/backend.rb +27 -27
  14. data/lib/honeybadger/backtrace.rb +181 -181
  15. data/lib/honeybadger/breadcrumbs/active_support.rb +119 -119
  16. data/lib/honeybadger/breadcrumbs/breadcrumb.rb +53 -53
  17. data/lib/honeybadger/breadcrumbs/collector.rb +82 -82
  18. data/lib/honeybadger/breadcrumbs/logging.rb +51 -51
  19. data/lib/honeybadger/breadcrumbs/ring_buffer.rb +44 -44
  20. data/lib/honeybadger/breadcrumbs.rb +8 -8
  21. data/lib/honeybadger/cli/deploy.rb +43 -43
  22. data/lib/honeybadger/cli/exec.rb +143 -143
  23. data/lib/honeybadger/cli/helpers.rb +28 -28
  24. data/lib/honeybadger/cli/heroku.rb +129 -129
  25. data/lib/honeybadger/cli/install.rb +101 -101
  26. data/lib/honeybadger/cli/main.rb +237 -237
  27. data/lib/honeybadger/cli/notify.rb +67 -67
  28. data/lib/honeybadger/cli/test.rb +267 -267
  29. data/lib/honeybadger/cli.rb +14 -14
  30. data/lib/honeybadger/config/defaults.rb +336 -333
  31. data/lib/honeybadger/config/env.rb +42 -42
  32. data/lib/honeybadger/config/ruby.rb +146 -146
  33. data/lib/honeybadger/config/yaml.rb +76 -76
  34. data/lib/honeybadger/config.rb +413 -413
  35. data/lib/honeybadger/const.rb +20 -20
  36. data/lib/honeybadger/context_manager.rb +55 -55
  37. data/lib/honeybadger/conversions.rb +16 -16
  38. data/lib/honeybadger/init/rails.rb +38 -38
  39. data/lib/honeybadger/init/rake.rb +66 -66
  40. data/lib/honeybadger/init/ruby.rb +11 -11
  41. data/lib/honeybadger/init/sinatra.rb +51 -51
  42. data/lib/honeybadger/logging.rb +177 -177
  43. data/lib/honeybadger/notice.rb +579 -568
  44. data/lib/honeybadger/plugin.rb +210 -210
  45. data/lib/honeybadger/plugins/breadcrumbs.rb +111 -111
  46. data/lib/honeybadger/plugins/delayed_job/plugin.rb +56 -56
  47. data/lib/honeybadger/plugins/delayed_job.rb +22 -22
  48. data/lib/honeybadger/plugins/faktory.rb +52 -52
  49. data/lib/honeybadger/plugins/lambda.rb +71 -71
  50. data/lib/honeybadger/plugins/local_variables.rb +44 -44
  51. data/lib/honeybadger/plugins/passenger.rb +23 -23
  52. data/lib/honeybadger/plugins/rails.rb +72 -63
  53. data/lib/honeybadger/plugins/resque.rb +72 -72
  54. data/lib/honeybadger/plugins/shoryuken.rb +52 -52
  55. data/lib/honeybadger/plugins/sidekiq.rb +71 -62
  56. data/lib/honeybadger/plugins/sucker_punch.rb +18 -18
  57. data/lib/honeybadger/plugins/thor.rb +32 -32
  58. data/lib/honeybadger/plugins/warden.rb +19 -19
  59. data/lib/honeybadger/rack/error_notifier.rb +92 -92
  60. data/lib/honeybadger/rack/user_feedback.rb +88 -88
  61. data/lib/honeybadger/rack/user_informer.rb +45 -45
  62. data/lib/honeybadger/ruby.rb +2 -2
  63. data/lib/honeybadger/singleton.rb +103 -103
  64. data/lib/honeybadger/tasks.rb +22 -22
  65. data/lib/honeybadger/templates/feedback_form.erb +84 -84
  66. data/lib/honeybadger/util/http.rb +92 -92
  67. data/lib/honeybadger/util/lambda.rb +32 -32
  68. data/lib/honeybadger/util/request_hash.rb +73 -73
  69. data/lib/honeybadger/util/request_payload.rb +41 -41
  70. data/lib/honeybadger/util/revision.rb +39 -39
  71. data/lib/honeybadger/util/sanitizer.rb +214 -214
  72. data/lib/honeybadger/util/sql.rb +34 -34
  73. data/lib/honeybadger/util/stats.rb +50 -50
  74. data/lib/honeybadger/version.rb +4 -4
  75. data/lib/honeybadger/worker.rb +253 -253
  76. data/lib/honeybadger.rb +11 -11
  77. data/resources/ca-bundle.crt +3376 -3376
  78. data/vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb +5 -5
  79. data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +89 -89
  80. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano/legacy.rb +47 -47
  81. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano.rb +2 -2
  82. data/vendor/cli/inifile.rb +628 -628
  83. data/vendor/cli/thor/actions/create_file.rb +103 -103
  84. data/vendor/cli/thor/actions/create_link.rb +59 -59
  85. data/vendor/cli/thor/actions/directory.rb +118 -118
  86. data/vendor/cli/thor/actions/empty_directory.rb +135 -135
  87. data/vendor/cli/thor/actions/file_manipulation.rb +316 -316
  88. data/vendor/cli/thor/actions/inject_into_file.rb +107 -107
  89. data/vendor/cli/thor/actions.rb +319 -319
  90. data/vendor/cli/thor/base.rb +656 -656
  91. data/vendor/cli/thor/command.rb +133 -133
  92. data/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +77 -77
  93. data/vendor/cli/thor/core_ext/io_binary_read.rb +10 -10
  94. data/vendor/cli/thor/core_ext/ordered_hash.rb +98 -98
  95. data/vendor/cli/thor/error.rb +32 -32
  96. data/vendor/cli/thor/group.rb +281 -281
  97. data/vendor/cli/thor/invocation.rb +178 -178
  98. data/vendor/cli/thor/line_editor/basic.rb +35 -35
  99. data/vendor/cli/thor/line_editor/readline.rb +88 -88
  100. data/vendor/cli/thor/line_editor.rb +17 -17
  101. data/vendor/cli/thor/parser/argument.rb +73 -73
  102. data/vendor/cli/thor/parser/arguments.rb +175 -175
  103. data/vendor/cli/thor/parser/option.rb +125 -125
  104. data/vendor/cli/thor/parser/options.rb +218 -218
  105. data/vendor/cli/thor/parser.rb +4 -4
  106. data/vendor/cli/thor/rake_compat.rb +71 -71
  107. data/vendor/cli/thor/runner.rb +322 -322
  108. data/vendor/cli/thor/shell/basic.rb +421 -421
  109. data/vendor/cli/thor/shell/color.rb +149 -149
  110. data/vendor/cli/thor/shell/html.rb +126 -126
  111. data/vendor/cli/thor/shell.rb +81 -81
  112. data/vendor/cli/thor/util.rb +267 -267
  113. data/vendor/cli/thor/version.rb +3 -3
  114. data/vendor/cli/thor.rb +484 -484
  115. metadata +10 -5
@@ -1,143 +1,143 @@
1
- require 'erb'
2
- require 'forwardable'
3
- require 'honeybadger/cli/main'
4
- require 'honeybadger/cli/helpers'
5
- require 'honeybadger/util/http'
6
- require 'honeybadger/util/stats'
7
- require 'open3'
8
- require 'ostruct'
9
- require 'thor/shell'
10
-
11
- module Honeybadger
12
- module CLI
13
- class Exec
14
- extend Forwardable
15
- include Helpers::BackendCmd
16
-
17
- FAILED_TEMPLATE = <<-MSG
18
- Honeybadger detected failure or error output for the command:
19
- `<%= args.join(' ') %>`
20
-
21
- PROCESS ID: <%= pid %>
22
-
23
- RESULT CODE: <%= code %>
24
-
25
- ERROR OUTPUT:
26
- <%= stderr %>
27
-
28
- STANDARD OUTPUT:
29
- <%= stdout %>
30
- MSG
31
-
32
- NO_EXEC_TEMPLATE = <<-MSG
33
- Honeybadger failed to execute the following command:
34
- `<%= args.join(' ') %>`
35
-
36
- The command was not executable. Try adjusting permissions on the file.
37
- MSG
38
-
39
- NOT_FOUND_TEMPLATE = <<-MSG
40
- Honeybadger failed to execute the following command:
41
- `<%= args.join(' ') %>`
42
-
43
- The command was not found. Make sure it exists in your PATH.
44
- MSG
45
-
46
- def initialize(options, args, config)
47
- @options = options
48
- @args = args
49
- @config = config
50
- @shell = ::Thor::Base.shell.new
51
- end
52
-
53
- def run
54
- result = exec_cmd
55
- return if result.success
56
-
57
- executable = args.first.to_s[/\S+/]
58
- payload = {
59
- api_key: config.get(:api_key),
60
- notifier: NOTIFIER,
61
- error: {
62
- class: 'honeybdager exec error',
63
- message: result.msg
64
- },
65
- request: {
66
- component: executable,
67
- context: {
68
- command: args.join(' '),
69
- code: result.code,
70
- pid: result.pid,
71
- pwd: Dir.pwd,
72
- path: ENV['PATH']
73
- }
74
- },
75
- server: {
76
- project_root: Dir.pwd,
77
- environment_name: config.get(:env),
78
- time: Time.now,
79
- stats: Util::Stats.all
80
- }
81
- }
82
-
83
- begin
84
- response = config.backend.notify(:notices, payload)
85
- rescue
86
- say(result.msg)
87
- raise
88
- end
89
-
90
- if !response.success?
91
- say(result.msg)
92
- say(error_message(response), :red)
93
- exit(1)
94
- end
95
-
96
- unless quiet?
97
- say(result.msg)
98
- say("\nSuccessfully notified Honeybadger")
99
- end
100
-
101
- exit(0)
102
- end
103
-
104
- private
105
-
106
- attr_reader :options, :args, :config
107
-
108
- def_delegator :@shell, :say
109
-
110
- def quiet?
111
- !!options[:quiet]
112
- end
113
-
114
- def exec_cmd
115
- stdout, stderr, status = Open3.capture3(args.join(' '))
116
-
117
- success = status.success? && stderr =~ BLANK
118
- pid = status.pid
119
- code = status.to_i
120
- msg = ERB.new(FAILED_TEMPLATE).result(binding) unless success
121
-
122
- OpenStruct.new(
123
- msg: msg,
124
- pid: pid,
125
- code: code,
126
- stdout: stdout,
127
- stderr: stderr,
128
- success: success
129
- )
130
- rescue Errno::EACCES, Errno::ENOEXEC
131
- OpenStruct.new(
132
- msg: ERB.new(NO_EXEC_TEMPLATE).result(binding),
133
- code: 126
134
- )
135
- rescue Errno::ENOENT
136
- OpenStruct.new(
137
- msg: ERB.new(NOT_FOUND_TEMPLATE).result(binding),
138
- code: 127
139
- )
140
- end
141
- end
142
- end
143
- end
1
+ require 'erb'
2
+ require 'forwardable'
3
+ require 'honeybadger/cli/main'
4
+ require 'honeybadger/cli/helpers'
5
+ require 'honeybadger/util/http'
6
+ require 'honeybadger/util/stats'
7
+ require 'open3'
8
+ require 'ostruct'
9
+ require 'thor/shell'
10
+
11
+ module Honeybadger
12
+ module CLI
13
+ class Exec
14
+ extend Forwardable
15
+ include Helpers::BackendCmd
16
+
17
+ FAILED_TEMPLATE = <<-MSG
18
+ Honeybadger detected failure or error output for the command:
19
+ `<%= args.join(' ') %>`
20
+
21
+ PROCESS ID: <%= pid %>
22
+
23
+ RESULT CODE: <%= code %>
24
+
25
+ ERROR OUTPUT:
26
+ <%= stderr %>
27
+
28
+ STANDARD OUTPUT:
29
+ <%= stdout %>
30
+ MSG
31
+
32
+ NO_EXEC_TEMPLATE = <<-MSG
33
+ Honeybadger failed to execute the following command:
34
+ `<%= args.join(' ') %>`
35
+
36
+ The command was not executable. Try adjusting permissions on the file.
37
+ MSG
38
+
39
+ NOT_FOUND_TEMPLATE = <<-MSG
40
+ Honeybadger failed to execute the following command:
41
+ `<%= args.join(' ') %>`
42
+
43
+ The command was not found. Make sure it exists in your PATH.
44
+ MSG
45
+
46
+ def initialize(options, args, config)
47
+ @options = options
48
+ @args = args
49
+ @config = config
50
+ @shell = ::Thor::Base.shell.new
51
+ end
52
+
53
+ def run
54
+ result = exec_cmd
55
+ return if result.success
56
+
57
+ executable = args.first.to_s[/\S+/]
58
+ payload = {
59
+ api_key: config.get(:api_key),
60
+ notifier: NOTIFIER,
61
+ error: {
62
+ class: 'honeybdager exec error',
63
+ message: result.msg
64
+ },
65
+ request: {
66
+ component: executable,
67
+ context: {
68
+ command: args.join(' '),
69
+ code: result.code,
70
+ pid: result.pid,
71
+ pwd: Dir.pwd,
72
+ path: ENV['PATH']
73
+ }
74
+ },
75
+ server: {
76
+ project_root: Dir.pwd,
77
+ environment_name: config.get(:env),
78
+ time: Time.now,
79
+ stats: Util::Stats.all
80
+ }
81
+ }
82
+
83
+ begin
84
+ response = config.backend.notify(:notices, payload)
85
+ rescue
86
+ say(result.msg)
87
+ raise
88
+ end
89
+
90
+ if !response.success?
91
+ say(result.msg)
92
+ say(error_message(response), :red)
93
+ exit(1)
94
+ end
95
+
96
+ unless quiet?
97
+ say(result.msg)
98
+ say("\nSuccessfully notified Honeybadger")
99
+ end
100
+
101
+ exit(0)
102
+ end
103
+
104
+ private
105
+
106
+ attr_reader :options, :args, :config
107
+
108
+ def_delegator :@shell, :say
109
+
110
+ def quiet?
111
+ !!options[:quiet]
112
+ end
113
+
114
+ def exec_cmd
115
+ stdout, stderr, status = Open3.capture3(args.join(' '))
116
+
117
+ success = status.success? && stderr =~ BLANK
118
+ pid = status.pid
119
+ code = status.to_i
120
+ msg = ERB.new(FAILED_TEMPLATE).result(binding) unless success
121
+
122
+ OpenStruct.new(
123
+ msg: msg,
124
+ pid: pid,
125
+ code: code,
126
+ stdout: stdout,
127
+ stderr: stderr,
128
+ success: success
129
+ )
130
+ rescue Errno::EACCES, Errno::ENOEXEC
131
+ OpenStruct.new(
132
+ msg: ERB.new(NO_EXEC_TEMPLATE).result(binding),
133
+ code: 126
134
+ )
135
+ rescue Errno::ENOENT
136
+ OpenStruct.new(
137
+ msg: ERB.new(NOT_FOUND_TEMPLATE).result(binding),
138
+ code: 127
139
+ )
140
+ end
141
+ end
142
+ end
143
+ end
@@ -1,28 +1,28 @@
1
- module Honeybadger
2
- module CLI
3
- module Helpers
4
- module BackendCmd
5
- def error_message(response)
6
- host = config.get(:'connection.host')
7
- <<-MSG
8
- !! --- Honeybadger request failed --------------------------------------------- !!
9
-
10
- We encountered an error when contacting the server:
11
-
12
- #{response.error_message}
13
-
14
- To fix this issue, please try the following:
15
-
16
- - Make sure the gem is configured properly.
17
- - Retry executing this command a few times.
18
- - Make sure you can connect to #{host} (`curl https://#{host}/v1/notices`).
19
- - Email support@honeybadger.io for help. Include as much debug info as you
20
- can for a faster resolution!
21
-
22
- !! --- End -------------------------------------------------------------------- !!
23
- MSG
24
- end
25
- end
26
- end
27
- end
28
- end
1
+ module Honeybadger
2
+ module CLI
3
+ module Helpers
4
+ module BackendCmd
5
+ def error_message(response)
6
+ host = config.get(:'connection.host')
7
+ <<-MSG
8
+ !! --- Honeybadger request failed --------------------------------------------- !!
9
+
10
+ We encountered an error when contacting the server:
11
+
12
+ #{response.error_message}
13
+
14
+ To fix this issue, please try the following:
15
+
16
+ - Make sure the gem is configured properly.
17
+ - Retry executing this command a few times.
18
+ - Make sure you can connect to #{host} (`curl https://#{host}/v1/notices`).
19
+ - Email support@honeybadger.io for help. Include as much debug info as you
20
+ can for a faster resolution!
21
+
22
+ !! --- End -------------------------------------------------------------------- !!
23
+ MSG
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,129 +1,129 @@
1
- module Honeybadger
2
- module CLI
3
- class Heroku < Thor
4
- class_option :app, aliases: :'-a', type: :string, default: nil, desc: 'Specify optional Heroku APP'
5
-
6
- desc 'install_deploy_notification', 'Install Heroku deploy notifications addon'
7
- option :api_key, aliases: :'-k', type: :string, desc: 'Api key of your Honeybadger application'
8
- option :environment, aliases: :'-e', type: :string, desc: 'Environment of your Heroku application (i.e. "production", "staging")'
9
- def install_deploy_notification
10
- app = options.has_key?('app') ? options['app'] : detect_heroku_app(false)
11
- rails_env = options['environment'] || heroku_var('RAILS_ENV', app)
12
- api_key = options['api_key'] || heroku_var('HONEYBADGER_API_KEY', app)
13
-
14
- unless api_key =~ /\S/
15
- say("Unable to detect your API key from Heroku.", :red)
16
- say('Have you configured multiple Heroku apps? Try using --app APP', :red) unless app
17
- exit(1)
18
- end
19
-
20
- unless rails_env =~ /\S/
21
- say("Unable to detect your environment from Heroku. Use --environment ENVIRONMENT.", :red)
22
- say('Have you configured multiple Heroku apps? Try using --app APP', :red) unless app
23
- exit(1)
24
- end
25
-
26
- cmd = %Q(heroku webhooks:add -i api:release -l notify -u "https://api.honeybadger.io/v1/deploys/heroku?environment=#{rails_env}&api_key=#{api_key}"#{app ? " --app #{app}" : ''})
27
-
28
- say("Running: `#{cmd}`")
29
- say(run(cmd))
30
- end
31
-
32
- desc 'install API_KEY', 'Install Honeybadger on Heroku using API_KEY'
33
- def install(api_key)
34
- say("Installing Honeybadger #{VERSION} for Heroku")
35
-
36
- app = options[:app] || detect_heroku_app(false)
37
- say("Adding config HONEYBADGER_API_KEY=#{api_key} to Heroku.", :magenta)
38
- unless write_heroku_env({'HONEYBADGER_API_KEY' => api_key}, app)
39
- say('Unable to update heroku config. You may need to specify an app name with --app APP', :red)
40
- exit(1)
41
- end
42
-
43
- if env = heroku_var('RAILS_ENV', app, heroku_var('RACK_ENV', app))
44
- say('Installing deploy notification addon', :magenta)
45
- invoke :install_deploy_notification, [], { app: app, api_key: api_key, environment: env }
46
- else
47
- say('Skipping deploy notification installation: we were unable to determine the environment name from your Heroku app.', :yellow)
48
- say("To install manually, try `honeybadger heroku install_deploy_notification#{app ? " -a #{app}" : ""} -k #{api_key} --environment ENVIRONMENT`", :yellow)
49
- end
50
-
51
- say("Installation complete. Happy 'badgering!", :green)
52
- end
53
-
54
- private
55
-
56
- # Detects the Heroku app name from GIT.
57
- #
58
- # @param [Boolean] prompt_on_default If a single remote is discoverd,
59
- # should we prompt the user before returning it?
60
- #
61
- # Returns the String app name if detected, otherwise nil.
62
- def detect_heroku_app(prompt_on_default = true)
63
- apps, git_config = {}, File.join(Dir.pwd, '.git', 'config')
64
- if File.exist?(git_config)
65
- require 'inifile'
66
- ini = IniFile.load(git_config)
67
- ini.each_section do |section|
68
- if match = section.match(/remote \"(?<remote>.+)\"/)
69
- url = ini[section]['url']
70
- if url_match = url.match(/heroku\.com:(?<app>.+)\.git$/)
71
- apps[match[:remote]] = url_match[:app]
72
- end
73
- end
74
- end
75
-
76
- if apps.size == 1
77
- if !prompt_on_default
78
- apps.values.first
79
- else
80
- say "We detected a Heroku app named #{apps.values.first}. Do you want to load the config? (y/yes or n/no)"
81
- if STDIN.gets.chomp =~ /(y|yes)/i
82
- apps.values.first
83
- end
84
- end
85
- elsif apps.size > 1
86
- say "We detected the following Heroku apps:"
87
- apps.each_with_index {|a,i| say "\s\s#{i+1}. #{a[1]}" }
88
- say "\s\s#{apps.size+1}. Use default"
89
- say "Please select an option (1-#{apps.size+1}):"
90
- apps.values[STDIN.gets.chomp.to_i-1]
91
- end
92
- end
93
- end
94
-
95
- def run(cmd)
96
- Bundler.with_unbundled_env { `#{cmd}` }
97
- end
98
-
99
- def heroku_var(var, app_name, default = nil)
100
- app = app_name ? "--app #{app_name}" : ''
101
- result = run("heroku config:get #{var} #{app} 2> /dev/null").strip
102
- result.split.find(lambda { default }) {|x| x =~ /\S/ }
103
- end
104
-
105
- def read_heroku_env(app = nil)
106
- cmd = ['heroku config']
107
- cmd << "--app #{app}" if app
108
- output = run(cmd.join("\s"))
109
- return false unless $?.to_i == 0
110
- Hash[output.scan(/(HONEYBADGER_[^:]+):\s*(\S.*)\s*$/)]
111
- end
112
-
113
- def set_env_from_heroku(app = nil)
114
- return false unless env = read_heroku_env(app)
115
- env.each_pair do |k,v|
116
- ENV[k] ||= v
117
- end
118
- end
119
-
120
- def write_heroku_env(env, app = nil)
121
- cmd = ["heroku config:set"]
122
- Hash(env).each_pair {|k,v| cmd << "#{k}=#{v}" }
123
- cmd << "--app #{app}" if app
124
- run(cmd.join("\s"))
125
- $?.to_i == 0
126
- end
127
- end
128
- end
129
- end
1
+ module Honeybadger
2
+ module CLI
3
+ class Heroku < Thor
4
+ class_option :app, aliases: :'-a', type: :string, default: nil, desc: 'Specify optional Heroku APP'
5
+
6
+ desc 'install_deploy_notification', 'Install Heroku deploy notifications addon'
7
+ option :api_key, aliases: :'-k', type: :string, desc: 'Api key of your Honeybadger application'
8
+ option :environment, aliases: :'-e', type: :string, desc: 'Environment of your Heroku application (i.e. "production", "staging")'
9
+ def install_deploy_notification
10
+ app = options.has_key?('app') ? options['app'] : detect_heroku_app(false)
11
+ rails_env = options['environment'] || heroku_var('RAILS_ENV', app)
12
+ api_key = options['api_key'] || heroku_var('HONEYBADGER_API_KEY', app)
13
+
14
+ unless api_key =~ /\S/
15
+ say("Unable to detect your API key from Heroku.", :red)
16
+ say('Have you configured multiple Heroku apps? Try using --app APP', :red) unless app
17
+ exit(1)
18
+ end
19
+
20
+ unless rails_env =~ /\S/
21
+ say("Unable to detect your environment from Heroku. Use --environment ENVIRONMENT.", :red)
22
+ say('Have you configured multiple Heroku apps? Try using --app APP', :red) unless app
23
+ exit(1)
24
+ end
25
+
26
+ cmd = %Q(heroku webhooks:add -i api:release -l notify -u "https://api.honeybadger.io/v1/deploys/heroku?environment=#{rails_env}&api_key=#{api_key}"#{app ? " --app #{app}" : ''})
27
+
28
+ say("Running: `#{cmd}`")
29
+ say(run(cmd))
30
+ end
31
+
32
+ desc 'install API_KEY', 'Install Honeybadger on Heroku using API_KEY'
33
+ def install(api_key)
34
+ say("Installing Honeybadger #{VERSION} for Heroku")
35
+
36
+ app = options[:app] || detect_heroku_app(false)
37
+ say("Adding config HONEYBADGER_API_KEY=#{api_key} to Heroku.", :magenta)
38
+ unless write_heroku_env({'HONEYBADGER_API_KEY' => api_key}, app)
39
+ say('Unable to update heroku config. You may need to specify an app name with --app APP', :red)
40
+ exit(1)
41
+ end
42
+
43
+ if env = heroku_var('RAILS_ENV', app, heroku_var('RACK_ENV', app))
44
+ say('Installing deploy notification addon', :magenta)
45
+ invoke :install_deploy_notification, [], { app: app, api_key: api_key, environment: env }
46
+ else
47
+ say('Skipping deploy notification installation: we were unable to determine the environment name from your Heroku app.', :yellow)
48
+ say("To install manually, try `honeybadger heroku install_deploy_notification#{app ? " -a #{app}" : ""} -k #{api_key} --environment ENVIRONMENT`", :yellow)
49
+ end
50
+
51
+ say("Installation complete. Happy 'badgering!", :green)
52
+ end
53
+
54
+ private
55
+
56
+ # Detects the Heroku app name from GIT.
57
+ #
58
+ # @param [Boolean] prompt_on_default If a single remote is discoverd,
59
+ # should we prompt the user before returning it?
60
+ #
61
+ # Returns the String app name if detected, otherwise nil.
62
+ def detect_heroku_app(prompt_on_default = true)
63
+ apps, git_config = {}, File.join(Dir.pwd, '.git', 'config')
64
+ if File.exist?(git_config)
65
+ require 'inifile'
66
+ ini = IniFile.load(git_config)
67
+ ini.each_section do |section|
68
+ if match = section.match(/remote \"(?<remote>.+)\"/)
69
+ url = ini[section]['url']
70
+ if url_match = url.match(/heroku\.com:(?<app>.+)\.git$/)
71
+ apps[match[:remote]] = url_match[:app]
72
+ end
73
+ end
74
+ end
75
+
76
+ if apps.size == 1
77
+ if !prompt_on_default
78
+ apps.values.first
79
+ else
80
+ say "We detected a Heroku app named #{apps.values.first}. Do you want to load the config? (y/yes or n/no)"
81
+ if STDIN.gets.chomp =~ /(y|yes)/i
82
+ apps.values.first
83
+ end
84
+ end
85
+ elsif apps.size > 1
86
+ say "We detected the following Heroku apps:"
87
+ apps.each_with_index {|a,i| say "\s\s#{i+1}. #{a[1]}" }
88
+ say "\s\s#{apps.size+1}. Use default"
89
+ say "Please select an option (1-#{apps.size+1}):"
90
+ apps.values[STDIN.gets.chomp.to_i-1]
91
+ end
92
+ end
93
+ end
94
+
95
+ def run(cmd)
96
+ Bundler.with_unbundled_env { `#{cmd}` }
97
+ end
98
+
99
+ def heroku_var(var, app_name, default = nil)
100
+ app = app_name ? "--app #{app_name}" : ''
101
+ result = run("heroku config:get #{var} #{app} 2> /dev/null").strip
102
+ result.split.find(lambda { default }) {|x| x =~ /\S/ }
103
+ end
104
+
105
+ def read_heroku_env(app = nil)
106
+ cmd = ['heroku config']
107
+ cmd << "--app #{app}" if app
108
+ output = run(cmd.join("\s"))
109
+ return false unless $?.to_i == 0
110
+ Hash[output.scan(/(HONEYBADGER_[^:]+):\s*(\S.*)\s*$/)]
111
+ end
112
+
113
+ def set_env_from_heroku(app = nil)
114
+ return false unless env = read_heroku_env(app)
115
+ env.each_pair do |k,v|
116
+ ENV[k] ||= v
117
+ end
118
+ end
119
+
120
+ def write_heroku_env(env, app = nil)
121
+ cmd = ["heroku config:set"]
122
+ Hash(env).each_pair {|k,v| cmd << "#{k}=#{v}" }
123
+ cmd << "--app #{app}" if app
124
+ run(cmd.join("\s"))
125
+ $?.to_i == 0
126
+ end
127
+ end
128
+ end
129
+ end