honeybadger 1.16.7 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -0
  3. data/README.md +37 -16
  4. data/bin/honeybadger +5 -0
  5. data/lib/honeybadger.rb +167 -191
  6. data/lib/honeybadger/agent.rb +136 -0
  7. data/lib/honeybadger/backend.rb +26 -0
  8. data/lib/honeybadger/backend/base.rb +66 -0
  9. data/lib/honeybadger/backend/debug.rb +12 -0
  10. data/lib/honeybadger/backend/null.rb +16 -0
  11. data/lib/honeybadger/backend/server.rb +51 -0
  12. data/lib/honeybadger/backend/test.rb +24 -0
  13. data/lib/honeybadger/backtrace.rb +29 -24
  14. data/lib/honeybadger/cli.rb +367 -0
  15. data/lib/honeybadger/config.rb +333 -0
  16. data/lib/honeybadger/config/callbacks.rb +70 -0
  17. data/lib/honeybadger/config/defaults.rb +175 -0
  18. data/lib/honeybadger/config/env.rb +40 -0
  19. data/lib/honeybadger/config/yaml.rb +43 -0
  20. data/lib/honeybadger/const.rb +28 -0
  21. data/lib/honeybadger/init/rails.rb +84 -0
  22. data/lib/honeybadger/init/sinatra.rb +27 -0
  23. data/lib/honeybadger/logging.rb +133 -0
  24. data/lib/honeybadger/notice.rb +243 -280
  25. data/lib/honeybadger/plugin.rb +110 -0
  26. data/lib/honeybadger/plugins/delayed_job.rb +22 -0
  27. data/lib/honeybadger/{integrations → plugins}/delayed_job/plugin.rb +6 -7
  28. data/lib/honeybadger/{integrations → plugins}/local_variables.rb +7 -8
  29. data/lib/honeybadger/{integrations → plugins}/net_http.rb +10 -8
  30. data/lib/honeybadger/plugins/passenger.rb +24 -0
  31. data/lib/honeybadger/plugins/rails.rb +61 -0
  32. data/lib/honeybadger/plugins/sidekiq.rb +35 -0
  33. data/lib/honeybadger/{integrations → plugins}/thor.rb +9 -8
  34. data/lib/honeybadger/{integrations → plugins}/unicorn.rb +10 -9
  35. data/lib/honeybadger/rack/error_notifier.rb +44 -27
  36. data/lib/honeybadger/rack/metrics_reporter.rb +41 -0
  37. data/lib/honeybadger/rack/request_hash.rb +50 -0
  38. data/lib/honeybadger/rack/user_feedback.rb +15 -10
  39. data/lib/honeybadger/rack/user_informer.rb +14 -3
  40. data/lib/honeybadger/trace.rb +185 -0
  41. data/lib/honeybadger/util/http.rb +79 -0
  42. data/lib/honeybadger/util/request_sanitizer.rb +35 -0
  43. data/lib/honeybadger/util/sanitizer.rb +71 -0
  44. data/lib/honeybadger/util/stats.rb +31 -0
  45. data/lib/honeybadger/version.rb +4 -0
  46. data/lib/honeybadger/worker.rb +224 -0
  47. data/lib/honeybadger/worker/batch.rb +50 -0
  48. data/lib/honeybadger/worker/metered_queue.rb +80 -0
  49. data/lib/honeybadger/worker/metrics_collection.rb +61 -0
  50. data/lib/honeybadger/worker/metrics_collector.rb +96 -0
  51. data/{lib/honeybadger/capistrano.rb → vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb} +1 -3
  52. data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +76 -0
  53. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano.rb +2 -0
  54. data/{lib → vendor/capistrano-honeybadger/lib}/honeybadger/capistrano/legacy.rb +16 -15
  55. data/vendor/thor/lib/thor.rb +484 -0
  56. data/vendor/thor/lib/thor/actions.rb +319 -0
  57. data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  58. data/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  59. data/vendor/thor/lib/thor/actions/directory.rb +118 -0
  60. data/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  61. data/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  62. data/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  63. data/vendor/thor/lib/thor/base.rb +656 -0
  64. data/vendor/thor/lib/thor/command.rb +133 -0
  65. data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  66. data/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  67. data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  68. data/vendor/thor/lib/thor/error.rb +32 -0
  69. data/vendor/thor/lib/thor/group.rb +281 -0
  70. data/vendor/thor/lib/thor/invocation.rb +178 -0
  71. data/vendor/thor/lib/thor/line_editor.rb +17 -0
  72. data/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  73. data/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  74. data/vendor/thor/lib/thor/parser.rb +4 -0
  75. data/vendor/thor/lib/thor/parser/argument.rb +73 -0
  76. data/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  77. data/vendor/thor/lib/thor/parser/option.rb +125 -0
  78. data/vendor/thor/lib/thor/parser/options.rb +218 -0
  79. data/vendor/thor/lib/thor/rake_compat.rb +71 -0
  80. data/vendor/thor/lib/thor/runner.rb +322 -0
  81. data/vendor/thor/lib/thor/shell.rb +81 -0
  82. data/vendor/thor/lib/thor/shell/basic.rb +421 -0
  83. data/vendor/thor/lib/thor/shell/color.rb +149 -0
  84. data/vendor/thor/lib/thor/shell/html.rb +126 -0
  85. data/vendor/thor/lib/thor/util.rb +267 -0
  86. data/vendor/thor/lib/thor/version.rb +3 -0
  87. metadata +97 -305
  88. data/Appraisals +0 -95
  89. data/CHANGELOG.md +0 -422
  90. data/Gemfile +0 -8
  91. data/Gemfile.lock +0 -136
  92. data/Guardfile +0 -5
  93. data/MIT-LICENSE +0 -32
  94. data/Rakefile +0 -159
  95. data/features/metal.feature +0 -20
  96. data/features/rack.feature +0 -55
  97. data/features/rails.feature +0 -343
  98. data/features/rails3.x.feature +0 -26
  99. data/features/rake.feature +0 -25
  100. data/features/sinatra.feature +0 -27
  101. data/features/standalone.feature +0 -73
  102. data/features/step_definitions/metal_steps.rb +0 -24
  103. data/features/step_definitions/rack_steps.rb +0 -18
  104. data/features/step_definitions/rails_steps.rb +0 -270
  105. data/features/step_definitions/rake_steps.rb +0 -17
  106. data/features/step_definitions/standalone_steps.rb +0 -12
  107. data/features/step_definitions/thor_steps.rb +0 -4
  108. data/features/support/env.rb +0 -22
  109. data/features/support/honeybadger_failure_shim.rb.template +0 -5
  110. data/features/support/honeybadger_shim.rb.template +0 -6
  111. data/features/support/rails.rb +0 -202
  112. data/features/support/rake/Rakefile +0 -68
  113. data/features/support/test.thor +0 -22
  114. data/features/thor.feature +0 -5
  115. data/gemfiles/binding_of_caller.gemfile +0 -13
  116. data/gemfiles/delayed_job.gemfile +0 -13
  117. data/gemfiles/rack.gemfile +0 -13
  118. data/gemfiles/rails.gemfile +0 -16
  119. data/gemfiles/rails2.3.gemfile +0 -15
  120. data/gemfiles/rails3.0.gemfile +0 -16
  121. data/gemfiles/rails3.1.gemfile +0 -16
  122. data/gemfiles/rails3.2.gemfile +0 -16
  123. data/gemfiles/rails4.0.gemfile +0 -16
  124. data/gemfiles/rails4.1.gemfile +0 -16
  125. data/gemfiles/rake.gemfile +0 -13
  126. data/gemfiles/sinatra.gemfile +0 -13
  127. data/gemfiles/standalone.gemfile +0 -12
  128. data/gemfiles/thor.gemfile +0 -13
  129. data/generators/honeybadger/honeybadger_generator.rb +0 -95
  130. data/generators/honeybadger/lib/insert_commands.rb +0 -34
  131. data/generators/honeybadger/lib/rake_commands.rb +0 -24
  132. data/generators/honeybadger/templates/capistrano_hook.rb +0 -6
  133. data/generators/honeybadger/templates/honeybadger_tasks.rake +0 -25
  134. data/generators/honeybadger/templates/initializer.rb +0 -6
  135. data/honeybadger.gemspec +0 -174
  136. data/lib/honeybadger/array.rb +0 -53
  137. data/lib/honeybadger/capistrano/tasks.rake +0 -73
  138. data/lib/honeybadger/configuration.rb +0 -397
  139. data/lib/honeybadger/dependency.rb +0 -65
  140. data/lib/honeybadger/integrations.rb +0 -9
  141. data/lib/honeybadger/integrations/delayed_job.rb +0 -20
  142. data/lib/honeybadger/integrations/passenger.rb +0 -18
  143. data/lib/honeybadger/integrations/sidekiq.rb +0 -37
  144. data/lib/honeybadger/monitor.rb +0 -17
  145. data/lib/honeybadger/monitor/railtie.rb +0 -53
  146. data/lib/honeybadger/monitor/sender.rb +0 -44
  147. data/lib/honeybadger/monitor/trace.rb +0 -187
  148. data/lib/honeybadger/monitor/worker.rb +0 -169
  149. data/lib/honeybadger/payload.rb +0 -101
  150. data/lib/honeybadger/rack.rb +0 -12
  151. data/lib/honeybadger/rails.rb +0 -45
  152. data/lib/honeybadger/rails/action_controller_catcher.rb +0 -30
  153. data/lib/honeybadger/rails/controller_methods.rb +0 -78
  154. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +0 -29
  155. data/lib/honeybadger/rails3_tasks.rb +0 -94
  156. data/lib/honeybadger/railtie.rb +0 -52
  157. data/lib/honeybadger/rake_handler.rb +0 -66
  158. data/lib/honeybadger/sender.rb +0 -185
  159. data/lib/honeybadger/shared_tasks.rb +0 -56
  160. data/lib/honeybadger/stats.rb +0 -29
  161. data/lib/honeybadger/tasks.rb +0 -95
  162. data/lib/honeybadger/user_feedback.rb +0 -8
  163. data/lib/honeybadger/user_informer.rb +0 -8
  164. data/lib/honeybadger_tasks.rb +0 -69
  165. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +0 -99
  166. data/rails/init.rb +0 -1
  167. data/resources/README.md +0 -34
  168. data/script/integration_test.rb +0 -38
  169. data/spec/allocation_stats.rb +0 -32
  170. data/spec/honeybadger/backtrace_spec.rb +0 -242
  171. data/spec/honeybadger/capistrano_spec.rb +0 -36
  172. data/spec/honeybadger/configuration_spec.rb +0 -328
  173. data/spec/honeybadger/dependency_spec.rb +0 -134
  174. data/spec/honeybadger/integrations/delayed_job_spec.rb +0 -82
  175. data/spec/honeybadger/integrations/local_variables_spec.rb +0 -60
  176. data/spec/honeybadger/integrations/net_http_spec.rb +0 -29
  177. data/spec/honeybadger/integrations/passenger_spec.rb +0 -29
  178. data/spec/honeybadger/integrations/sidekiq_spec.rb +0 -60
  179. data/spec/honeybadger/integrations/thor_spec.rb +0 -32
  180. data/spec/honeybadger/integrations/unicorn_spec.rb +0 -40
  181. data/spec/honeybadger/logger_spec.rb +0 -79
  182. data/spec/honeybadger/monitor/trace_spec.rb +0 -65
  183. data/spec/honeybadger/monitor/worker_spec.rb +0 -274
  184. data/spec/honeybadger/notice_spec.rb +0 -669
  185. data/spec/honeybadger/notifier_spec.rb +0 -328
  186. data/spec/honeybadger/payload_spec.rb +0 -162
  187. data/spec/honeybadger/rack_spec.rb +0 -85
  188. data/spec/honeybadger/rails/action_controller_spec.rb +0 -328
  189. data/spec/honeybadger/rails_spec.rb +0 -37
  190. data/spec/honeybadger/sender_spec.rb +0 -317
  191. data/spec/honeybadger/stats_spec.rb +0 -57
  192. data/spec/honeybadger/user_feedback_spec.rb +0 -80
  193. data/spec/honeybadger/user_informer_spec.rb +0 -30
  194. data/spec/honeybadger_tasks_spec.rb +0 -171
  195. data/spec/spec_helper.rb +0 -24
  196. data/spec/support/array_including.rb +0 -31
  197. data/spec/support/backtraced_exception.rb +0 -9
  198. data/spec/support/collected_sender.rb +0 -12
  199. data/spec/support/defines_constants.rb +0 -18
  200. data/spec/support/helpers.rb +0 -101
@@ -0,0 +1,61 @@
1
+ module Honeybadger
2
+ class Worker
3
+ # This code comes from batsd
4
+ class MetricsCollection < Array
5
+ PERCENTILE_METHOD_SIGNATURE = /\Apercentile_(.+)\z/.freeze
6
+
7
+ # Calculates the sum of values in the array
8
+ def sum
9
+ inject( nil ) { |sum,x| sum ? sum+x : x };
10
+ end
11
+
12
+ # Calculates the arithmetic mean of values in the array
13
+ def mean
14
+ self.sum.to_f / self.length
15
+ end
16
+
17
+ # Calculates the median of values in the array
18
+ def median
19
+ self.sort[self.length/2]
20
+ end
21
+
22
+ # Calculates the value of the upper percentile of values
23
+ # in the array. If only a single value is provided in the array, that is
24
+ # returned
25
+ def percentile(threshold)
26
+ if (count > 1)
27
+ self.sort!
28
+ # strip off the top 100-threshold
29
+ threshold_index = (((100 - threshold).to_f / 100) * count).round
30
+ self[0..-threshold_index].last
31
+ else
32
+ self.first
33
+ end
34
+ end
35
+
36
+ # Calculates the mean squared error of values in the array
37
+ def mean_squared
38
+ m = mean
39
+ self.class.new(map{|v| (v-m)**2}).sum
40
+ end
41
+
42
+ # Calculates the standard deviatiation of values in the array
43
+ def standard_dev
44
+ (mean_squared/(count-1))**0.5
45
+ end
46
+
47
+ # Allow [1,2,3].percentile_90, [1,2,3].percentile(75), etc.
48
+ def method_missing(method, *args, &block)
49
+ if method.to_s =~ PERCENTILE_METHOD_SIGNATURE
50
+ percentile($1.to_i)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def respond_to_missing?(method, include_private = false)
57
+ method.to_s =~ PERCENTILE_METHOD_SIGNATURE or super
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,96 @@
1
+ require 'securerandom'
2
+ require 'forwardable'
3
+
4
+ module Honeybadger
5
+ class Worker
6
+ autoload :MetricsCollection, 'honeybadger/worker/metrics_collection'
7
+
8
+ class MetricsCollector
9
+ extend Forwardable
10
+
11
+ class Chunk
12
+ extend Forwardable
13
+
14
+ attr_reader :id
15
+
16
+ def initialize(id, metrics)
17
+ @id = SecureRandom.uuid
18
+ @metrics = metrics
19
+ end
20
+
21
+ def_delegators :@metrics, :to_json, :size
22
+ end
23
+
24
+ def initialize(config, interval = 60, now = now)
25
+ @id = SecureRandom.uuid
26
+ @config = config
27
+ @interval = interval
28
+ @future = now + interval
29
+ @mutex = Mutex.new
30
+ @metrics = { :timing => {}, :counter => {} }
31
+ end
32
+
33
+ attr_reader :id
34
+
35
+ def_delegators :@metrics, :[]
36
+
37
+ def timing(name, value)
38
+ add_metric(name, value, :timing)
39
+ end
40
+
41
+ def increment(name, value)
42
+ add_metric(name, value, :counter)
43
+ end
44
+
45
+ def flush?
46
+ now >= future
47
+ end
48
+
49
+ def to_a
50
+ [].tap do |m|
51
+ metrics[:counter].each do |metric, values|
52
+ m << "#{metric} #{values.sum}"
53
+ end
54
+ metrics[:timing].each do |metric, values|
55
+ m << "#{metric}:mean #{values.mean}"
56
+ m << "#{metric}:median #{values.median}"
57
+ m << "#{metric}:percentile_90 #{values.percentile(90)}"
58
+ m << "#{metric}:min #{values.min}"
59
+ m << "#{metric}:max #{values.max}"
60
+ m << "#{metric}:stddev #{values.standard_dev}" if values.count > 1
61
+ m << "#{metric} #{values.count}"
62
+ end
63
+ m.compact!
64
+ end
65
+ end
66
+
67
+ def chunk(size)
68
+ to_a.each_slice(size) do |metrics|
69
+ yield(Chunk.new(id, as_json(metrics)))
70
+ end
71
+ end
72
+
73
+ def as_json(metrics = to_a)
74
+ {metrics: metrics, :environment => config[:env], :hostname => config[:hostname]}
75
+ end
76
+
77
+ def to_json(*args)
78
+ as_json.to_json(*args)
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :config, :future, :metrics, :mutex
84
+
85
+ def now
86
+ Time.now.to_i
87
+ end
88
+
89
+ def add_metric(name, value, kind)
90
+ mutex.synchronize do
91
+ (metrics[kind][name] ||= MetricsCollection.new) << value
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,7 +1,5 @@
1
- require 'capistrano'
2
-
3
1
  if defined?(Capistrano::Configuration.instance)
4
2
  require 'honeybadger/capistrano/legacy'
5
3
  else
6
- load File.expand_path('../capistrano/tasks.rake', __FILE__)
4
+ load File.expand_path('../tasks/deploy.cap', __FILE__)
7
5
  end
@@ -0,0 +1,76 @@
1
+ namespace :honeybadger do
2
+ def sshkit_outdated?
3
+ !::SSHKit.config.command_map.respond_to?(:prefix)
4
+ end
5
+
6
+ desc 'Notify Honeybadger of the deployment.'
7
+ task :deploy => :env do
8
+ next if sshkit_outdated?
9
+ if server = fetch(:honeybadger_server)
10
+ on server do |host|
11
+ info 'Notifying Honeybadger of deploy.'
12
+
13
+ executable = fetch(:honeybadger, :honeybadger)
14
+
15
+ revision = fetch(:current_revision) do
16
+ within(repo_path) do
17
+ capture("cd #{repo_path} && git rev-parse --short HEAD")
18
+ end
19
+ end
20
+
21
+ options = [
22
+ 'deploy',
23
+ '--environment', fetch(:honeybadger_env, fetch(:rails_env, 'production')),
24
+ '--revision', revision,
25
+ '--repository', fetch(:repo_url),
26
+ '--user', fetch(:honeybadger_user, ENV['USER'] || ENV['USERNAME'])
27
+ ]
28
+
29
+ api_key = fetch(:honeybadger_api_key, ENV['HONEYBADGER_API_KEY'])
30
+ options += ['--api-key', api_key] if api_key
31
+
32
+ if fetch(:honeybadger_async_notify, false)
33
+ ::SSHKit.config.command_map.prefix[:honeybadger].push(:nohup)
34
+ options << '>> /dev/null 2>&1 &'
35
+ end
36
+
37
+ within release_path do
38
+ execute executable, options
39
+ end
40
+
41
+ info 'Honeybadger notification complete.'
42
+ end
43
+ end
44
+ end
45
+
46
+ desc 'Setup ENV for Honeybadger deploy rake task.'
47
+ task :env do
48
+ if sshkit_outdated?
49
+ run_locally do
50
+ warn 'Unable to notify Honeybadger: you are using an outdated version of SSHKIT. Please upgrade to >= 1.2.0.'
51
+ end
52
+ next
53
+ end
54
+
55
+ server = fetch(:honeybadger_server) do
56
+ s = primary(:app)
57
+ set(:honeybadger_server, s.select?({exclude: :no_release}) ? s : nil)
58
+ end
59
+
60
+ if server
61
+ on server do |host|
62
+ rails_env = fetch(:rails_env, 'production')
63
+ env = ["RAILS_ENV=#{rails_env}"]
64
+ ::SSHKit.config.command_map.prefix[:honeybadger].unshift(*env)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ namespace :load do
71
+ task :defaults do
72
+ set :bundle_bins, fetch(:bundle_bins, []).push('honeybadger')
73
+ end
74
+ end
75
+
76
+ after 'deploy:finishing', 'honeybadger:deploy'
@@ -0,0 +1,2 @@
1
+ warn 'DEPRECATED: requiring honeybadger/capistrano has been deprecated. Require capistrano/honeybadger instead.'
2
+ require File.expand_path('../../capistrano/honeybadger', __FILE__)
@@ -1,9 +1,11 @@
1
+ require 'capistrano'
2
+
1
3
  module Honeybadger
2
4
  module Capistrano
3
5
  def self.load_into(configuration)
4
6
  configuration.load do
5
- after "deploy", "honeybadger:deploy"
6
- after "deploy:migrations", "honeybadger:deploy"
7
+ after 'deploy', 'honeybadger:deploy'
8
+ after 'deploy:migrations', 'honeybadger:deploy'
7
9
 
8
10
  namespace :honeybadger do
9
11
  desc <<-DESC
@@ -11,28 +13,27 @@ module Honeybadger
11
13
  - Run remotely so we use remote API keys, environment, etc.
12
14
  DESC
13
15
  task :deploy, :except => { :no_release => true } do
14
- rails_env = fetch(:rails_env, "production")
15
- honeybadger_env = fetch(:honeybadger_env, fetch(:rails_env, "production"))
16
- rake_task = fetch(:honeybadger_deploy_task, 'honeybadger:deploy')
16
+ rails_env = fetch(:rails_env, 'production')
17
+ honeybadger_env = fetch(:honeybadger_env, fetch(:rails_env, 'production'))
17
18
  local_user = ENV['USER'] || ENV['USERNAME']
18
- executable = RUBY_PLATFORM.downcase.include?('mswin') ? fetch(:rake, 'rake.bat') : fetch(:rake, 'rake')
19
+ executable = fetch(:honeybadger, "#{fetch(:bundle_cmd, 'bundle')} exec honeybadger")
19
20
  async_notify = fetch(:honeybadger_async_notify, false)
20
21
  directory = fetch(:honeybadger_deploy_dir, configuration.current_release)
21
22
  notify_options = "cd #{directory};"
22
- notify_options << " nohup" if async_notify
23
- notify_options << " #{executable} RAILS_ENV=#{rails_env} #{rake_task} TO=#{honeybadger_env} REVISION=#{current_revision} REPO=#{repository} USER=#{local_user}"
24
- notify_options << " DRY_RUN=true" if dry_run
25
- notify_options << " API_KEY=#{ENV['API_KEY']}" if ENV['API_KEY']
26
- notify_options << " >> /dev/null 2>&1 &" if async_notify
23
+ notify_options << " RAILS_ENV=#{rails_env}"
24
+ notify_options << ' nohup' if async_notify
25
+ notify_options << " #{executable} deploy --environment=#{honeybadger_env} --revision=#{current_revision} --repository=#{repository} --user=#{local_user}"
26
+ notify_options << ' --dry-run' if dry_run
27
+ notify_options << " --api-key=#{ENV['API_KEY']}" if ENV['API_KEY']
28
+ notify_options << ' >> /dev/null 2>&1 &' if async_notify
27
29
  logger.info "Notifying Honeybadger of Deploy (#{notify_options})"
28
30
  if configuration.dry_run
29
- logger.info "DRY RUN: Notification not actually run."
31
+ logger.info 'DRY RUN: Notification not actually run.'
30
32
  else
31
- result = ""
33
+ result = ''
32
34
  run(notify_options, :once => true, :pty => false) { |ch, stream, data| result << data }
33
- # TODO: Check if SSL is active on account via result content.
34
35
  end
35
- logger.info "Honeybadger Notification Complete."
36
+ logger.info 'Honeybadger Notification Complete.'
36
37
  end
37
38
  end
38
39
  end
@@ -0,0 +1,484 @@
1
+ require "set"
2
+ require "thor/base"
3
+
4
+ class Thor # rubocop:disable ClassLength
5
+ class << self
6
+ # Allows for custom "Command" package naming.
7
+ #
8
+ # === Parameters
9
+ # name<String>
10
+ # options<Hash>
11
+ #
12
+ def package_name(name, options = {})
13
+ @package_name = name.nil? || name == "" ? nil : name
14
+ end
15
+
16
+ # Sets the default command when thor is executed without an explicit command to be called.
17
+ #
18
+ # ==== Parameters
19
+ # meth<Symbol>:: name of the default command
20
+ #
21
+ def default_command(meth = nil)
22
+ if meth
23
+ @default_command = meth == :none ? "help" : meth.to_s
24
+ else
25
+ @default_command ||= from_superclass(:default_command, "help")
26
+ end
27
+ end
28
+ alias_method :default_task, :default_command
29
+
30
+ # Registers another Thor subclass as a command.
31
+ #
32
+ # ==== Parameters
33
+ # klass<Class>:: Thor subclass to register
34
+ # command<String>:: Subcommand name to use
35
+ # usage<String>:: Short usage for the subcommand
36
+ # description<String>:: Description for the subcommand
37
+ def register(klass, subcommand_name, usage, description, options = {})
38
+ if klass <= Thor::Group
39
+ desc usage, description, options
40
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
41
+ else
42
+ desc usage, description, options
43
+ subcommand subcommand_name, klass
44
+ end
45
+ end
46
+
47
+ # Defines the usage and the description of the next command.
48
+ #
49
+ # ==== Parameters
50
+ # usage<String>
51
+ # description<String>
52
+ # options<String>
53
+ #
54
+ def desc(usage, description, options = {})
55
+ if options[:for]
56
+ command = find_and_refresh_command(options[:for])
57
+ command.usage = usage if usage
58
+ command.description = description if description
59
+ else
60
+ @usage, @desc, @hide = usage, description, options[:hide] || false
61
+ end
62
+ end
63
+
64
+ # Defines the long description of the next command.
65
+ #
66
+ # ==== Parameters
67
+ # long description<String>
68
+ #
69
+ def long_desc(long_description, options = {})
70
+ if options[:for]
71
+ command = find_and_refresh_command(options[:for])
72
+ command.long_description = long_description if long_description
73
+ else
74
+ @long_desc = long_description
75
+ end
76
+ end
77
+
78
+ # Maps an input to a command. If you define:
79
+ #
80
+ # map "-T" => "list"
81
+ #
82
+ # Running:
83
+ #
84
+ # thor -T
85
+ #
86
+ # Will invoke the list command.
87
+ #
88
+ # ==== Parameters
89
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
90
+ #
91
+ def map(mappings = nil)
92
+ @map ||= from_superclass(:map, {})
93
+
94
+ if mappings
95
+ mappings.each do |key, value|
96
+ if key.respond_to?(:each)
97
+ key.each { |subkey| @map[subkey] = value }
98
+ else
99
+ @map[key] = value
100
+ end
101
+ end
102
+ end
103
+
104
+ @map
105
+ end
106
+
107
+ # Declares the options for the next command to be declared.
108
+ #
109
+ # ==== Parameters
110
+ # Hash[Symbol => Object]:: The hash key is the name of the option and the value
111
+ # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
112
+ # or :required (string). If you give a value, the type of the value is used.
113
+ #
114
+ def method_options(options = nil)
115
+ @method_options ||= {}
116
+ build_options(options, @method_options) if options
117
+ @method_options
118
+ end
119
+
120
+ alias_method :options, :method_options
121
+
122
+ # Adds an option to the set of method options. If :for is given as option,
123
+ # it allows you to change the options from a previous defined command.
124
+ #
125
+ # def previous_command
126
+ # # magic
127
+ # end
128
+ #
129
+ # method_option :foo => :bar, :for => :previous_command
130
+ #
131
+ # def next_command
132
+ # # magic
133
+ # end
134
+ #
135
+ # ==== Parameters
136
+ # name<Symbol>:: The name of the argument.
137
+ # options<Hash>:: Described below.
138
+ #
139
+ # ==== Options
140
+ # :desc - Description for the argument.
141
+ # :required - If the argument is required or not.
142
+ # :default - Default value for this argument. It cannot be required and have default values.
143
+ # :aliases - Aliases for this option.
144
+ # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
145
+ # :banner - String to show on usage notes.
146
+ # :hide - If you want to hide this option from the help.
147
+ #
148
+ def method_option(name, options = {})
149
+ scope = if options[:for]
150
+ find_and_refresh_command(options[:for]).options
151
+ else
152
+ method_options
153
+ end
154
+
155
+ build_option(name, options, scope)
156
+ end
157
+ alias_method :option, :method_option
158
+
159
+ # Prints help information for the given command.
160
+ #
161
+ # ==== Parameters
162
+ # shell<Thor::Shell>
163
+ # command_name<String>
164
+ #
165
+ def command_help(shell, command_name)
166
+ meth = normalize_command_name(command_name)
167
+ command = all_commands[meth]
168
+ handle_no_command_error(meth) unless command
169
+
170
+ shell.say "Usage:"
171
+ shell.say " #{banner(command)}"
172
+ shell.say
173
+ class_options_help(shell, nil => command.options.map { |_, o| o })
174
+ if command.long_description
175
+ shell.say "Description:"
176
+ shell.print_wrapped(command.long_description, :indent => 2)
177
+ else
178
+ shell.say command.description
179
+ end
180
+ end
181
+ alias_method :task_help, :command_help
182
+
183
+ # Prints help information for this class.
184
+ #
185
+ # ==== Parameters
186
+ # shell<Thor::Shell>
187
+ #
188
+ def help(shell, subcommand = false)
189
+ list = printable_commands(true, subcommand)
190
+ Thor::Util.thor_classes_in(self).each do |klass|
191
+ list += klass.printable_commands(false)
192
+ end
193
+ list.sort! { |a, b| a[0] <=> b[0] }
194
+
195
+ if defined?(@package_name) && @package_name
196
+ shell.say "#{@package_name} commands:"
197
+ else
198
+ shell.say "Commands:"
199
+ end
200
+
201
+ shell.print_table(list, :indent => 2, :truncate => true)
202
+ shell.say
203
+ class_options_help(shell)
204
+ end
205
+
206
+ # Returns commands ready to be printed.
207
+ def printable_commands(all = true, subcommand = false)
208
+ (all ? all_commands : commands).map do |_, command|
209
+ next if command.hidden?
210
+ item = []
211
+ item << banner(command, false, subcommand)
212
+ item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
213
+ item
214
+ end.compact
215
+ end
216
+ alias_method :printable_tasks, :printable_commands
217
+
218
+ def subcommands
219
+ @subcommands ||= from_superclass(:subcommands, [])
220
+ end
221
+ alias_method :subtasks, :subcommands
222
+
223
+ def subcommand_classes
224
+ @subcommand_classes ||= {}
225
+ end
226
+
227
+ def subcommand(subcommand, subcommand_class)
228
+ subcommands << subcommand.to_s
229
+ subcommand_class.subcommand_help subcommand
230
+ subcommand_classes[subcommand.to_s] = subcommand_class
231
+
232
+ define_method(subcommand) do |*args|
233
+ args, opts = Thor::Arguments.split(args)
234
+ args.unshift("help") if opts.include? "--help" or opts.include? "-h"
235
+ invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
236
+ end
237
+ end
238
+ alias_method :subtask, :subcommand
239
+
240
+ # Extend check unknown options to accept a hash of conditions.
241
+ #
242
+ # === Parameters
243
+ # options<Hash>: A hash containing :only and/or :except keys
244
+ def check_unknown_options!(options = {})
245
+ @check_unknown_options ||= {}
246
+ options.each do |key, value|
247
+ if value
248
+ @check_unknown_options[key] = Array(value)
249
+ else
250
+ @check_unknown_options.delete(key)
251
+ end
252
+ end
253
+ @check_unknown_options
254
+ end
255
+
256
+ # Overwrite check_unknown_options? to take subcommands and options into account.
257
+ def check_unknown_options?(config) #:nodoc:
258
+ options = check_unknown_options
259
+ return false unless options
260
+
261
+ command = config[:current_command]
262
+ return true unless command
263
+
264
+ name = command.name
265
+
266
+ if subcommands.include?(name)
267
+ false
268
+ elsif options[:except]
269
+ !options[:except].include?(name.to_sym)
270
+ elsif options[:only]
271
+ options[:only].include?(name.to_sym)
272
+ else
273
+ true
274
+ end
275
+ end
276
+
277
+ # Stop parsing of options as soon as an unknown option or a regular
278
+ # argument is encountered. All remaining arguments are passed to the command.
279
+ # This is useful if you have a command that can receive arbitrary additional
280
+ # options, and where those additional options should not be handled by
281
+ # Thor.
282
+ #
283
+ # ==== Example
284
+ #
285
+ # To better understand how this is useful, let's consider a command that calls
286
+ # an external command. A user may want to pass arbitrary options and
287
+ # arguments to that command. The command itself also accepts some options,
288
+ # which should be handled by Thor.
289
+ #
290
+ # class_option "verbose", :type => :boolean
291
+ # stop_on_unknown_option! :exec
292
+ # check_unknown_options! :except => :exec
293
+ #
294
+ # desc "exec", "Run a shell command"
295
+ # def exec(*args)
296
+ # puts "diagnostic output" if options[:verbose]
297
+ # Kernel.exec(*args)
298
+ # end
299
+ #
300
+ # Here +exec+ can be called with +--verbose+ to get diagnostic output,
301
+ # e.g.:
302
+ #
303
+ # $ thor exec --verbose echo foo
304
+ # diagnostic output
305
+ # foo
306
+ #
307
+ # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
308
+ #
309
+ # $ thor exec echo --verbose foo
310
+ # --verbose foo
311
+ #
312
+ # ==== Parameters
313
+ # Symbol ...:: A list of commands that should be affected.
314
+ def stop_on_unknown_option!(*command_names)
315
+ stop_on_unknown_option.merge(command_names)
316
+ end
317
+
318
+ def stop_on_unknown_option?(command) #:nodoc:
319
+ command && stop_on_unknown_option.include?(command.name.to_sym)
320
+ end
321
+
322
+ protected
323
+ def stop_on_unknown_option #:nodoc:
324
+ @stop_on_unknown_option ||= Set.new
325
+ end
326
+
327
+ # The method responsible for dispatching given the args.
328
+ def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
329
+ meth ||= retrieve_command_name(given_args)
330
+ command = all_commands[normalize_command_name(meth)]
331
+
332
+ if !command && config[:invoked_via_subcommand]
333
+ # We're a subcommand and our first argument didn't match any of our
334
+ # commands. So we put it back and call our default command.
335
+ given_args.unshift(meth)
336
+ command = all_commands[normalize_command_name(default_command)]
337
+ end
338
+
339
+ if command
340
+ args, opts = Thor::Options.split(given_args)
341
+ if stop_on_unknown_option?(command) && !args.empty?
342
+ # given_args starts with a non-option, so we treat everything as
343
+ # ordinary arguments
344
+ args.concat opts
345
+ opts.clear
346
+ end
347
+ else
348
+ args, opts = given_args, nil
349
+ command = dynamic_command_class.new(meth)
350
+ end
351
+
352
+ opts = given_opts || opts || []
353
+ config.merge!(:current_command => command, :command_options => command.options)
354
+
355
+ instance = new(args, opts, config)
356
+ yield instance if block_given?
357
+ args = instance.args
358
+ trailing = args[Range.new(arguments.size, -1)]
359
+ instance.invoke_command(command, trailing || [])
360
+ end
361
+
362
+ # The banner for this class. You can customize it if you are invoking the
363
+ # thor class by another ways which is not the Thor::Runner. It receives
364
+ # the command that is going to be invoked and a boolean which indicates if
365
+ # the namespace should be displayed as arguments.
366
+ #
367
+ def banner(command, namespace = nil, subcommand = false)
368
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
369
+ end
370
+
371
+ def baseclass #:nodoc:
372
+ Thor
373
+ end
374
+
375
+ def dynamic_command_class #:nodoc:
376
+ Thor::DynamicCommand
377
+ end
378
+
379
+ def create_command(meth) #:nodoc:
380
+ @usage ||= nil
381
+ @desc ||= nil
382
+ @long_desc ||= nil
383
+
384
+ if @usage && @desc
385
+ base_class = @hide ? Thor::HiddenCommand : Thor::Command
386
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
387
+ @usage, @desc, @long_desc, @method_options, @hide = nil
388
+ true
389
+ elsif all_commands[meth] || meth == "method_missing"
390
+ true
391
+ else
392
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
393
+ "Call desc if you want this method to be available as command or declare it inside a " <<
394
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
395
+ false
396
+ end
397
+ end
398
+ alias_method :create_task, :create_command
399
+
400
+ def initialize_added #:nodoc:
401
+ class_options.merge!(method_options)
402
+ @method_options = nil
403
+ end
404
+
405
+ # Retrieve the command name from given args.
406
+ def retrieve_command_name(args) #:nodoc:
407
+ meth = args.first.to_s unless args.empty?
408
+ if meth && (map[meth] || meth !~ /^\-/)
409
+ args.shift
410
+ else
411
+ nil
412
+ end
413
+ end
414
+ alias_method :retrieve_task_name, :retrieve_command_name
415
+
416
+ # receives a (possibly nil) command name and returns a name that is in
417
+ # the commands hash. In addition to normalizing aliases, this logic
418
+ # will determine if a shortened command is an unambiguous substring of
419
+ # a command or alias.
420
+ #
421
+ # +normalize_command_name+ also converts names like +animal-prison+
422
+ # into +animal_prison+.
423
+ def normalize_command_name(meth) #:nodoc:
424
+ return default_command.to_s.gsub("-", "_") unless meth
425
+
426
+ possibilities = find_command_possibilities(meth)
427
+ if possibilities.size > 1
428
+ fail AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
429
+ elsif possibilities.size < 1
430
+ meth = meth || default_command
431
+ elsif map[meth]
432
+ meth = map[meth]
433
+ else
434
+ meth = possibilities.first
435
+ end
436
+
437
+ meth.to_s.gsub("-", "_") # treat foo-bar as foo_bar
438
+ end
439
+ alias_method :normalize_task_name, :normalize_command_name
440
+
441
+ # this is the logic that takes the command name passed in by the user
442
+ # and determines whether it is an unambiguous substrings of a command or
443
+ # alias name.
444
+ def find_command_possibilities(meth)
445
+ len = meth.to_s.length
446
+ possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
447
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
448
+
449
+ if possibilities.include?(meth)
450
+ [meth]
451
+ elsif unique_possibilities.size == 1
452
+ unique_possibilities
453
+ else
454
+ possibilities
455
+ end
456
+ end
457
+ alias_method :find_task_possibilities, :find_command_possibilities
458
+
459
+ def subcommand_help(cmd)
460
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
461
+ class_eval "
462
+ def help(command = nil, subcommand = true); super; end
463
+ "
464
+ end
465
+ alias_method :subtask_help, :subcommand_help
466
+ end
467
+
468
+ include Thor::Base
469
+
470
+ map HELP_MAPPINGS => :help
471
+
472
+ desc "help [COMMAND]", "Describe available commands or one specific command"
473
+ def help(command = nil, subcommand = false)
474
+ if command
475
+ if self.class.subcommands.include? command
476
+ self.class.subcommand_classes[command].help(shell, true)
477
+ else
478
+ self.class.command_help(shell, command)
479
+ end
480
+ else
481
+ self.class.help(shell, subcommand)
482
+ end
483
+ end
484
+ end