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