lib/honeybadger/cli/main.rb in honeybadger-2.7.2 vs lib/honeybadger/cli/main.rb in honeybadger-3.0.0.beta1
- old
+ new
@@ -1,196 +1,181 @@
-require 'stringio'
+require 'honeybadger/cli/deploy'
+require 'honeybadger/cli/exec'
+require 'honeybadger/cli/heroku'
+require 'honeybadger/cli/install'
+require 'honeybadger/cli/notify'
+require 'honeybadger/cli/test'
+require 'honeybadger/config'
+require 'honeybadger/config/defaults'
+require 'honeybadger/util/http'
+require 'honeybadger/version'
+require 'logger'
-require 'honeybadger/cli/helpers'
-
module Honeybadger
module CLI
+ BLANK = /\A\s*\z/
+
+ NOTIFIER = {
+ name: 'honeybadger-ruby (cli)'.freeze,
+ url: 'https://github.com/honeybadger-io/honeybadger-ruby'.freeze,
+ version: VERSION,
+ language: nil
+ }.freeze
+
class Main < Thor
- include Helpers
+ def self.project_options
+ option :api_key, required: false, aliases: :'-k', type: :string, desc: 'Api key of your Honeybadger application'
+ option :environment, required: false, aliases: [:'-e', :'-env'], type: :string, desc: 'Environment this command is being executed in (i.e. "production", "staging")'
+ end
- class HoneybadgerTestingException < RuntimeError; end
+ desc 'install API_KEY', 'Install Honeybadger into a new project'
+ def install(api_key)
+ Install.new(options, api_key).run
+ rescue => e
+ log_error(e)
+ exit(1)
+ end
- NOT_BLANK = Regexp.new('\S').freeze
+ desc 'test', 'Send a test notification from Honeybadger'
+ option :dry_run, type: :boolean, aliases: :'-d', default: false, desc: 'Skip sending data to Honeybadger'
+ option :file, type: :string, aliases: :'-f', default: nil, desc: 'Write the output to FILE'
+ def test
+ Test.new(options).run
+ rescue => e
+ log_error(e)
+ exit(1)
+ end
desc 'deploy', 'Notify Honeybadger of deployment'
- option :environment, aliases: :'-e', type: :string, desc: 'Environment of the deploy (i.e. "production", "staging")'
- option :revision, aliases: :'-s', type: :string, desc: 'The revision/sha that is being deployed'
- option :repository, aliases: :'-r', type: :string, desc: 'The address of your repository'
- option :user, aliases: :'-u', type: :string, default: ENV['USER'] || ENV['USERNAME'], desc: 'The local user who is deploying'
- option :api_key, aliases: :'-k', type: :string, desc: 'Api key of your Honeybadger application'
+ project_options
+ option :repository, required: true, type: :string, aliases: :'-r', desc: 'The address of your repository'
+ option :revision, required: true, type: :string, aliases: :'-s', desc: 'The revision/sha that is being deployed'
+ option :user, required: true, type: :string, aliases: :'-u', default: ENV['USER'] || ENV['USERNAME'], desc: 'The local user who is deploying'
def deploy
- load_rails(verbose: true)
+ config = build_config(options)
- payload = Hash[[:environment, :revision, :repository].map {|k| [k, options[k]] }]
- payload[:local_username] = options[:user]
+ if config.get(:api_key).to_s =~ BLANK
+ say("No value provided for required options '--api-key'")
+ return
+ end
- ENV['HONEYBADGER_LOGGING_LEVEL'] = '2'
- ENV['HONEYBADGER_LOGGING_TTY_LEVEL'] = '0'
- ENV['HONEYBADGER_LOGGING_PATH'] = 'STDOUT'
- ENV['HONEYBADGER_REPORT_DATA'] = 'true'
+ Deploy.new(options, [], config).run
+ rescue => e
+ log_error(e)
+ exit(1)
+ end
- say('Loading configuration')
- config = Config.new(rails_framework_opts)
- config.update(api_key: options[:api_key]) if options[:api_key] =~ NOT_BLANK
+ desc 'notify', 'Notify Honeybadger of an error'
+ project_options
+ option :class, required: true, type: :string, aliases: :'-c', default: 'CLI Notification', desc: 'The class name of the error. (Default: CLI Notification)'
+ option :message, required: true, type: :string, aliases: :'-m', desc: 'The error message.'
+ option :action, required: false, type: :string, aliases: :'-a', desc: 'The action.'
+ option :component, required: false, type: :string, aliases: :'-C', desc: 'The component.'
+ option :fingerprint, required: false, type: :string, aliases: :'-f', desc: 'The component.'
+ option :tags, required: false, type: :string, aliases: :'-t', desc: 'The tags.'
+ option :url, required: false, type: :string, aliases: :'-u', desc: 'The URL.'
+ def notify
+ config = build_config(options)
- unless (payload[:environment] ||= config[:env]) =~ NOT_BLANK
- say('Unable to determine environment. (see: `honeybadger help deploy`)', :red)
- exit(1)
+ if config.get(:api_key).to_s =~ BLANK
+ say("No value provided for required options '--api-key'")
+ return
end
- unless config.valid?
- say("Invalid configuration: #{config.inspect}", :red)
- exit(1)
- end
-
- response = config.backend.notify(:deploys, payload)
- if response.success?
- say("Deploy notification for #{payload[:environment]} complete.", :green)
- else
- say("Deploy notification failed: #{response.code}", :red)
- exit(1)
- end
+ Notify.new(options, [], config).run
rescue => e
- say("An error occurred during deploy notification: #{e}\n\t#{e.backtrace.join("\n\t")}", :red)
+ log_error(e)
exit(1)
end
- desc 'config', 'List configuration options'
- option :default, aliases: :'-d', type: :boolean, default: true, desc: 'Output default options'
- def config
- load_rails
- config = Config.new(rails_framework_opts)
- output_config(config.to_hash(options[:default]))
- end
+ desc 'exec', 'Execute a command. If the exit status is not 0, report the result to Honeybadger'
+ project_options
+ option :quiet, required: false, type: :boolean, aliases: :'-q', default: false, desc: 'Suppress all output unless Honeybdager notification fails.'
+ def exec(*args)
+ config = build_config(options)
- desc 'test', 'Output test/debug information'
- option :dry_run, aliases: :'-d', type: :boolean, default: false, desc: 'Skip sending data to Honeybadger'
- option :file, aliases: :'-f', type: :string, default: nil, desc: 'Write the output to FILE'
- def test
- if options[:file]
- out = StringIO.new
- $stdout = out
+ if config.get(:api_key).to_s =~ BLANK
+ say("No value provided for required options '--api-key'")
+ return
+ end
- flush = Proc.new do
- $stdout = STDOUT
- File.open(options[:file], 'w+') do |f|
- out.rewind
- out.each_line {|l| f.write(l) }
- end
+ Exec.new(options, args, config).run
+ rescue => e
+ log_error(e)
+ exit(1)
+ end
- say("Output written to #{options[:file]}", :green)
- end
+ desc 'heroku SUBCOMMAND ...ARGS', 'Manage Honeybadger on Heroku'
+ subcommand 'heroku', Heroku
- Agent.at_exit(&flush)
+ private
- at_exit do
- # If the agent couldn't be started, the callback should happen here
- # instead.
- flush.() unless Agent.running?
- end
- end
+ def fetch_value(options, key)
+ options[key] == key ? nil : options[key]
+ end
- say("Detecting framework\n\n", :bold)
- load_rails(verbose: true)
+ def build_config(options)
+ config = Config.new(logger: Logger.new('/dev/null'))
+ config.set(:api_key, fetch_value(options, 'api_key')) if options.has_key?('api_key')
+ config.set(:env, fetch_value(options, 'environment')) if options.has_key?('environment')
+ config.init!({
+ framework: :cli
+ })
+ config
+ end
- ENV['HONEYBADGER_LOGGING_LEVEL'] = '0'
- ENV['HONEYBADGER_LOGGING_TTY_LEVEL'] = '0'
- ENV['HONEYBADGER_LOGGING_PATH'] = 'STDOUT'
- ENV['HONEYBADGER_DEBUG'] = 'true'
- ENV['HONEYBADGER_REPORT_DATA'] = options[:dry_run] ? 'false' : 'true'
+ def log_error(e)
+ case e
+ when *Util::HTTP::ERRORS
+ say(<<-MSG, :red)
+!! --- Failed to notify Honeybadger ------------------------------------------- !!
- config = Config.new(rails_framework_opts)
- say("\nConfiguration\n\n", :bold)
- output_config(config.to_hash)
+# What happened?
- say("\nStarting Honeybadger\n\n", :bold)
- Honeybadger.start(config) unless load_rails_env(verbose: true)
+ We encountered an HTTP error while contacting our service. Issues like this are
+ usually temporary.
- say("\nSending test notice\n\n", :bold)
- send_test
+# Error details
- say("\nRunning at exit hooks\n\n", :bold)
- end
+ #{e.class}: #{e.message}\n at #{e.backtrace && e.backtrace.first}
- desc 'install API_KEY', 'Install Honeybadger into the current directory using API_KEY'
- option :test, aliases: :'-t', type: :boolean, default: nil, desc: 'Send a test error'
- def install(api_key)
- say("Installing Honeybadger #{VERSION}")
+# What can I do?
- load_rails(verbose: true)
+ - Retry the command.
+ - Make sure you can connect to api.honeybadger.io (`ping api.honeybadger.io`).
+ - If you continue to see this message, email us at support@honeybadger.io
+ (don't forget to attach this output!)
- ENV['HONEYBADGER_LOGGING_LEVEL'] = '2'
- ENV['HONEYBADGER_LOGGING_TTY_LEVEL'] = '0'
- ENV['HONEYBADGER_LOGGING_PATH'] = 'STDOUT'
- ENV['HONEYBADGER_REPORT_DATA'] = 'true'
-
- config = Config.new(rails_framework_opts)
- config[:api_key] = api_key.to_s
-
- if (path = config.config_path).exist?
- say("You're already on Honeybadger, so you're all set.", :yellow)
+!! --- End -------------------------------------------------------------------- !!
+MSG
else
- say("Writing configuration to: #{path}", :yellow)
+ say(<<-MSG, :red)
+!! --- Honeybadger command failed --------------------------------------------- !!
- begin
- config.write
- rescue Config::ConfigError => e
- error("Error: Unable to write configuration file:\n\t#{e}")
- return
- rescue StandardError => e
- error("Error: Unable to write configuration file:\n\t#{e.class} -- #{e.message}\n\t#{e.backtrace.join("\n\t")}")
- return
- end
- end
+# What did you try to do?
- if (capfile = Pathname.new(config[:root]).join('Capfile')).exist?
- if capfile.read.match(/honeybadger/)
- say("Detected Honeybadger in Capfile; skipping Capistrano installation.", :yellow)
- else
- say("Appending Capistrano tasks to: #{capfile}", :yellow)
- File.open(capfile, 'a') do |f|
- f.puts("\nrequire 'capistrano/honeybadger'")
- end
- end
- end
+ You tried to execute the following command:
+ `honeybadger #{ARGV.join(' ')}`
- if options[:test].nil? || options[:test]
- Honeybadger.start(config) unless load_rails_env(verbose: true)
- say('Sending test notice', :yellow)
- unless Agent.instance && send_test(false)
- say('Honeybadger is installed, but failed to send a test notice. Try `honeybadger test`.', :red)
- exit(1)
- end
- end
+# What actually happend?
- say("Installation complete. Happy 'badgering!", :green)
- end
+ We encountered a Ruby exception and were forced to cancel your request.
- desc 'heroku SUBCOMMAND ...ARGS', 'Manage Honeybadger on Heroku'
- subcommand 'heroku', Heroku
+# Error details
- private
+ #{e.class}: #{e.message}
+ #{e.backtrace && e.backtrace.join("\n ")}
- def output_config(nested_hash, hierarchy = [])
- nested_hash.each_pair do |key, value|
- if value.kind_of?(Hash)
- say(tab_indent(hierarchy.size) << "#{key}:")
- output_config(value, hierarchy + [key])
- else
- dotted_key = (hierarchy + [key]).join('.').to_sym
- say(tab_indent(hierarchy.size) << "#{key}:")
- indent = tab_indent(hierarchy.size+1)
- say(indent + "Description: #{Config::OPTIONS[dotted_key][:description]}")
- say(indent + "Type: #{Config::OPTIONS[dotted_key].fetch(:type, String).name.split('::').last}")
- say(indent + "Default: #{Config::OPTIONS[dotted_key][:default].inspect}")
- say(indent + "Current: #{value.inspect}")
- end
- end
- end
+# What can I do?
- def tab_indent(number)
- ''.tap do |s|
- number.times { s << "\s\s" }
+ - If you're calling the `install` or `test` command in a Rails app, make sure
+ you can boot the Rails console: `bundle exec rails console`.
+ - Retry the command.
+ - If you continue to see this message, email us at support@honeybadger.io
+ (don't forget to attach this output!)
+
+!! --- End -------------------------------------------------------------------- !!
+MSG
end
end
end
end
end