Improve CLI error reporting

This commit is contained in:
Thibault Jouan
2014-10-11 17:34:19 +00:00
parent f6237bfc0c
commit 25d03d4322
8 changed files with 142 additions and 7 deletions

View File

@@ -32,10 +32,12 @@ require 'producer/core/tests/has_file'
require 'producer/core/tests/shell_command_status'
require 'producer/core/tests/yaml_eq'
require 'producer/core/errors'
require 'producer/core/cli'
require 'producer/core/condition'
require 'producer/core/env'
require 'producer/core/errors'
require 'producer/core/error_formatter'
require 'producer/core/logger_formatter'
require 'producer/core/prompter'
require 'producer/core/recipe'

View File

@@ -17,8 +17,9 @@ module Producer
rescue ArgumentError => e
stderr.puts e.message
exit EX_USAGE
rescue RuntimeError => e
stderr.puts "#{e.class.name.split('::').last}: #{e.message}"
rescue Exception => e
ef = ErrorFormatter.new(force_cause: [RecipeEvaluationError])
stderr.puts ef.format e
exit EX_SOFTWARE
end
end

View File

@@ -0,0 +1,53 @@
module Producer
module Core
class ErrorFormatter
def initialize(debug: false, force_cause: [])
@debug = debug
@force_cause = force_cause
end
def debug?
!!@debug
end
def format(exception)
lines = format_exception exception
if debug? && exception.cause
lines << ''
lines << 'cause:'
lines << format_exception(exception.cause, filter: false)
end
lines.join("\n")
end
private
def format_exception(exception, filter: true)
[
format_message(exception),
*format_backtrace(exception.backtrace, filter: filter)
]
end
def format_message(exception)
exception = exception.cause if @force_cause.include? exception.class
"#{exception.class.name.split('::').last}: #{exception.message}"
end
def format_backtrace(backtrace, filter: true)
backtrace = filter_backtrace backtrace if filter
indent_backtrace backtrace
end
def filter_backtrace(backtrace)
backtrace.reject { |l| l =~ /\/producer-\w+\/(?:bin|lib)\// }
end
def indent_backtrace(backtrace)
backtrace.map { |e| ' %s' % e }
end
end
end
end

View File

@@ -2,8 +2,10 @@ module Producer
module Core
Error = Class.new(StandardError)
RuntimeError = Class.new(RuntimeError)
ArgumentError = Class.new(Error)
ConditionNotMetError = Class.new(Error)
RecipeEvaluationError = Class.new(RuntimeError)
RemoteCommandExecutionError = Class.new(RuntimeError)
RegistryKeyError = Class.new(RuntimeError)
end

View File

@@ -5,7 +5,14 @@ module Producer
class << self
def evaluate(file_path, env)
content = File.read(file_path)
Recipe.new(env).tap { |o| o.instance_eval content, file_path }
begin
Recipe.new(env).tap { |o| o.instance_eval content, file_path }
rescue Exception => e
fail RecipeEvaluationError, e.message, [
'%s (recipe)' % file_path,
*e.backtrace
]
end
end
end
end