Apply new UI code to CLI and plugin-manager-cli (#4000)

Apply new UI code to CLI and plugin-manager-cli
This commit is contained in:
Clinton Wolfe 2019-07-30 14:37:09 -04:00 committed by GitHub
commit 80d5788883
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 96 deletions

View file

@ -1,5 +1,8 @@
version: "2"
checks:
method-lines:
config:
threshold: 40
file-lines:
enabled: false
identical-code:

View file

@ -123,36 +123,32 @@ class Inspec::InspecCLI < Inspec::BaseCLI
puts JSON.generate(result)
else
%w{location profile controls timestamp valid}.each do |item|
puts format("%-12s %s", item.to_s.capitalize + ":",
mark_text(result[:summary][item.to_sym]))
prepared_string = format("%-12s %s",
"#{item.to_s.capitalize} :",
result[:summary][item.to_sym])
ui.plain_line(prepared_string)
end
puts
if result[:errors].empty? && result[:warnings].empty?
puts "No errors or warnings"
ui.plain_line("No errors or warnings")
else
red = "\033[31m"
yellow = "\033[33m"
rst = "\033[0m"
item_msg = lambda { |item|
pos = [item[:file], item[:line], item[:column]].compact.join(":")
pos.empty? ? item[:msg] : pos + ": " + item[:msg]
}
result[:errors].each do |item|
puts "#{red}#{item_msg.call(item)}#{rst}"
end
result[:warnings].each do |item|
puts "#{yellow} ! #{item_msg.call(item)}#{rst}"
end
result[:errors].each { |item| ui.red " #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n" }
result[:warnings].each { |item| ui.yellow " ! #{item_msg.call(item)}\n" }
puts
puts format("Summary: %s%d errors%s, %s%d warnings%s",
red, result[:errors].length, rst,
yellow, result[:warnings].length, rst)
errors = ui.red("#{result[:errors].length} errors", print: false)
warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
ui.plain_line("Summary: #{errors}, #{warnings}")
end
end
exit 1 unless result[:summary][:valid]
ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
rescue StandardError => e
pretty_handle_exception(e)
end
@ -203,11 +199,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI
if result && !o[:ignore_errors] == false
o[:logger].info "Profile check failed. Please fix the profile before generating an archive."
return exit 1
return ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
# generate archive
exit 1 unless profile.archive(o)
ui.exit Inspec::UI::EXIT_USAGE_ERROR unless profile.archive(o)
rescue StandardError => e
pretty_handle_exception(e)
end
@ -294,10 +290,10 @@ class Inspec::InspecCLI < Inspec::BaseCLI
runner = Inspec::Runner.new(o)
targets.each { |target| runner.add_target(target) }
exit runner.run
ui.exit runner.run
rescue ArgumentError, RuntimeError, Train::UserError => e
$stderr.puts e.message
exit 1
ui.exit Inspec::UI::EXIT_USAGE_ERROR
rescue StandardError => e
pretty_handle_exception(e)
end
@ -312,12 +308,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
if o["format"] == "json"
puts res.to_json
else
headline("Platform Details")
puts Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36)
ui.headline("Platform Details")
ui.plain Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36)
end
rescue ArgumentError, RuntimeError, Train::UserError => e
$stderr.puts e.message
exit 1
ui.exit Inspec::UI::EXIT_USAGE_ERROR
rescue StandardError => e
pretty_handle_exception(e)
end
@ -348,12 +344,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
end
run_type, res = run_command(o)
exit res unless run_type == :ruby_eval
ui.exit res unless run_type == :ruby_eval
# No InSpec tests - just print evaluation output.
res = (res.respond_to?(:to_json) ? res.to_json : JSON.dump(res)) if o["reporter"]&.keys&.include?("json")
puts res
exit 0
ui.exit Inspec::UI::EXIT_NORMAL
rescue RuntimeError, Train::UserError => e
$stderr.puts e.message
rescue StandardError => e
@ -463,5 +459,5 @@ rescue Inspec::Plugin::V2::Exception => v2ex
else
Inspec::Log.error "Run again with --debug for a stacktrace."
end
exit 2
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
end

View file

@ -1,4 +1,3 @@
require "term/ansicolor"
require "pathname"
require "inspec/plugin/v2"
require "inspec/plugin/v2/installer"
@ -7,7 +6,6 @@ require "inspec/dist"
module InspecPlugins
module PluginManager
class CliCommand < Inspec.plugin(2, :cli_command)
include Term::ANSIColor
include Inspec::Dist
subcommand_desc "plugin SUBCOMMAND", "Manage #{PRODUCT_NAME} and Train plugins"
@ -22,15 +20,17 @@ module InspecPlugins
plugin_statuses = Inspec::Plugin::V2::Registry.instance.plugin_statuses
plugin_statuses.reject! { |s| %i{core bundle}.include?(s.installation_type) } unless options[:all]
# TODO: ui object support
puts
puts(bold { format(" %-30s%-10s%-8s%-6s", "Plugin Name", "Version", "Via", "ApiVer") })
puts "-" * 55
ui.bold(format(" %-30s%-10s%-8s%-6s", "Plugin Name", "Version", "Via", "ApiVer"))
ui.line
plugin_statuses.sort_by(&:name).each do |status|
puts(format(" %-30s%-10s%-8s%-6s", status.name, make_pretty_version(status), status.installation_type, status.api_generation.to_s))
ui.plain(format(" %-30s%-10s%-8s%-6s", status.name,
make_pretty_version(status),
status.installation_type,
status.api_generation.to_s))
end
puts "-" * 55
puts(" #{plugin_statuses.count} plugin(s) total")
ui.line
ui.plain(" #{plugin_statuses.count} plugin(s) total")
puts
end
@ -59,23 +59,22 @@ module InspecPlugins
search_results.delete("train-test-fixture")
end
# TODO: ui object support
puts
puts(bold { format(" %-30s%-50s", "Plugin Name", "Versions Available") })
puts "-" * 55
ui.bold(format(" %-30s%-50s", "Plugin Name", "Versions Available"))
ui.line
search_results.keys.sort.each do |plugin_name|
versions = options[:all] ? search_results[plugin_name] : [search_results[plugin_name].first]
versions = "(" + versions.join(", ") + ")"
puts(format(" %-30s%-50s", plugin_name, versions))
ui.plain(format(" %-30s%-50s", plugin_name, versions))
end
puts "-" * 55
puts(" #{search_results.count} plugin(s) found")
ui.line
ui.plain(" #{search_results.count} plugin(s) found")
puts
exit 2 if search_results.empty?
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR if search_results.empty?
rescue Inspec::Plugin::V2::SearchError => ex
Inspec::Log.error ex.message
exit 1
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
#==================================================================#
@ -119,13 +118,14 @@ module InspecPlugins
begin
installer.update(plugin_name)
rescue Inspec::Plugin::V2::UpdateError => ex
puts(red { "Update error: " } + ex.message + " - update failed")
exit 1
ui.plain("#{ui.red('Update error:')} #{ex.message} - update failed")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
post_update_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
new_version = (post_update_versions - pre_update_versions).first
puts(bold { plugin_name } + " plugin, version #{old_version} -> #{new_version}, updated from rubygems.org")
ui.bold(plugin_name + " plugin, version #{old_version} -> " \
"#{new_version}, updated from rubygems.org")
end
#--------------------------
@ -144,9 +144,9 @@ module InspecPlugins
def uninstall(plugin_name)
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
unless status
puts(red { "No such plugin installed: " } + "#{plugin_name} is not installed - uninstall failed")
exit 1
ui.plain("#{ui.red('No such plugin installed:')} #{plugin_name} is not " \
"installed - uninstall failed")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
installer = Inspec::Plugin::V2::Installer.instance
@ -156,11 +156,13 @@ module InspecPlugins
installer.uninstall(plugin_name)
if status.installation_type == :path
puts(bold { plugin_name } + " path-based plugin install has been uninstalled")
ui.bold(plugin_name + " path-based plugin install has been " \
"uninstalled")
else
puts(bold { plugin_name } + " plugin, version #{old_version}, has been uninstalled")
ui.bold(plugin_name + " plugin, version #{old_version}, has " \
"been uninstalled")
end
exit 0
ui.exit Inspec::UI::EXIT_NORMAL
end
private
@ -172,8 +174,8 @@ module InspecPlugins
def install_from_gemfile(gem_file)
unless File.exist? gem_file
puts(red { "No such plugin gem file " } + gem_file + " - installation failed.")
exit 1
ui.red("No such plugin gem file #{gem_file} - installation failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
plugin_name_parts = File.basename(gem_file, ".gem").split("-")
@ -183,14 +185,15 @@ module InspecPlugins
installer.install(plugin_name, gem_file: gem_file)
puts(bold { plugin_name } + " plugin, version #{version}, installed from local .gem file")
exit 0
ui.bold("#{plugin_name} plugin, version #{version}, installed from " \
"local .gem file")
ui.exit Inspec::UI::EXIT_NORMAL
end
def install_from_path(path)
unless File.exist? path
puts(red { "No such source code path " } + path + " - installation failed.")
exit 1
ui.red("No such source code path #{path} - installation failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
plugin_name = File.basename(path, ".rb")
@ -204,8 +207,10 @@ module InspecPlugins
# Already installed?
if registry.known_plugin?(plugin_name.to_sym)
puts(red { "Plugin already installed" } + " - #{plugin_name} - Use '#{EXEC_NAME} plugin list' to see previously installed plugin - installation failed.")
exit 2
ui.red("Plugin already installed - #{plugin_name} - Use '#{EXEC_NAME} " \
"plugin list' to see previously installed plugin - " \
"installation failed.")
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
end
# Can we figure out how to load it?
@ -217,8 +222,9 @@ module InspecPlugins
# OK, install it!
installer.install(plugin_name, path: entry_point)
puts(bold { plugin_name } + " plugin installed via source path reference, resolved to entry point " + entry_point)
exit 0
ui.bold("#{plugin_name} plugin installed via source path reference, " \
"resolved to entry point #{entry_point}")
ui.exit Inspec::UI::EXIT_NORMAL
end
# Rationale for rubocop variances: It's a heuristics method, and will be full of
@ -280,8 +286,10 @@ module InspecPlugins
# Well, if we got here, parts[2] matches an inspec/train prefix, but we have no idea about anything.
# Give up.
puts(red { "Unrecognizable plugin structure" } + " - #{parts[2]} - When installing from a path, please provide the path of the entry point file - installation failed.")
exit 1
ui.red("Unrecognizable plugin structure - #{parts[2]} - When " \
"installing from a path, please provide the path of the " \
"entry point file - installation failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
def install_from_path__probe_load(entry_point, plugin_name)
@ -289,9 +297,11 @@ module InspecPlugins
begin
require entry_point
rescue LoadError => ex
puts(red { "Plugin contains errors" } + " - #{plugin_name} - Encountered errors while trying to test load the plugin entry point, resolved to #{entry_point} - installation failed")
puts ex.message
exit 1
ui.red("Plugin contains errors - #{plugin_name} - Encountered " \
"errors while trying to test load the plugin entry point, " \
"resolved to #{entry_point} - installation failed")
ui.plain ex.message
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
# OK, the wheels didn't fall off. But is it a plugin?
@ -300,13 +310,19 @@ module InspecPlugins
# And the registry is keyed on Strings
registry_key = plugin_name.to_s.sub(/^train-/, "")
unless Train::Plugins.registry.key?(registry_key)
puts(red { "Does not appear to be a plugin" } + " - #{plugin_name} - After probe-loading the supposed plugin, it did not register itself to Train. Ensure something inherits from 'Train.plugin(1)' - installation failed.")
exit 1
ui.red("Does not appear to be a plugin - #{plugin_name} - After " \
"probe-loading the supposed plugin, it did not register " \
"itself to Train. Ensure something inherits from " \
"'Train.plugin(1)' - installation failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
else
unless registry.known_plugin?(plugin_name.to_sym)
puts(red { "Does not appear to be a plugin" } + " - #{plugin_name} - After probe-loading the supposed plugin, it did not register itself to InSpec. Ensure something inherits from 'Inspec.plugin(2)' - installation failed.")
exit 1
ui.red("Does not appear to be a plugin - #{plugin_name} - After " \
"probe-loading the supposed plugin, it did not register " \
"itself to InSpec. Ensure something inherits from " \
"'Inspec.plugin(2)' - installation failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
end
end
@ -326,8 +342,9 @@ module InspecPlugins
post_installed_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
new_version = (post_installed_versions - pre_installed_versions).first
puts(bold { plugin_name } + " plugin, version #{new_version}, installed from rubygems.org")
exit 0
ui.bold("#{plugin_name} plugin, version #{new_version}, installed " \
"from rubygems.org")
ui.exit Inspec::UI::EXIT_NORMAL
end
def install_from_remote_gem_verson_preflight_check(plugin_name, requested_version, pre_installed_versions)
@ -349,38 +366,50 @@ module InspecPlugins
they_explicitly_asked_for_a_version = !options[:version].nil?
what_we_would_install_is_already_installed = pre_installed_versions.include?(requested_version)
if what_we_would_install_is_already_installed && they_explicitly_asked_for_a_version
puts(red { "Plugin already installed at requested version" } + " - plugin #{plugin_name} #{requested_version} - refusing to install.")
ui.red("Plugin already installed at requested version - plugin " \
"#{plugin_name} #{requested_version} - refusing to install.")
elsif what_we_would_install_is_already_installed && !they_explicitly_asked_for_a_version
puts(red { "Plugin already installed at latest version" } + " - plugin #{plugin_name} #{requested_version} - refusing to install.")
ui.red("Plugin already installed at latest version - plugin " \
"#{plugin_name} #{requested_version} - refusing to install.")
else
# There are existing versions installed, but none of them are what was requested
puts(red { "Update required" } + " - plugin #{plugin_name}, requested #{requested_version}, have #{pre_installed_versions.join(", ")}; use `inspec plugin update` - refusing to install.")
ui.red("Update required - plugin #{plugin_name}, requested " \
"#{requested_version}, have " \
"#{pre_installed_versions.join(', ')}; use `inspec " \
"plugin update` - refusing to install.")
end
exit 2
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
end
# Rationale for RuboCop variance: This is a one-line method with heavy UX-focused error handling.
def install_attempt_install(plugin_name) # rubocop: disable Metrics/AbcSize
installer.install(plugin_name, version: options[:version])
rescue Inspec::Plugin::V2::PluginExcludedError => ex
puts(red { "Plugin on Exclusion List" } + " - #{plugin_name} is listed as an incompatible gem - refusing to install.")
puts "Rationale: #{ex.details.rationale}"
puts "Exclusion list location: " + File.join(Inspec.src_root, "etc", "plugin_filters.json")
puts "If you disagree with this determination, please accept our apologies for the misunderstanding, and open an issue at https://github.com/inspec/inspec/issues/new"
exit 2
ui.red("Plugin on Exclusion List - #{plugin_name} is listed as an " \
"incompatible gem - refusing to install.")
ui.plain("Rationale: #{ex.details.rationale}")
ui.plain("Exclusion list location: " +
File.join(Inspec.src_root, "etc", "plugin_filters.json"))
ui.plain("If you disagree with this determination, please accept " \
"our apologies for the misunderstanding, and open an issue " \
"at https://github.com/inspec/inspec/issues/new")
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
rescue Inspec::Plugin::V2::InstallError
raise if Inspec::Log.level == :debug
results = installer.search(plugin_name, exact: true)
if results.empty?
puts(red { "No such plugin gem " } + plugin_name + " could be found on rubygems.org - installation failed.")
ui.red("No such plugin gem #{plugin_name} could be found on " \
"rubygems.org - installation failed.")
elsif options[:version] && !results[plugin_name].include?(options[:version])
puts(red { "No such version" } + " - " + plugin_name + " exists, but no such version #{options[:version]} found on rubygems.org - installation failed.")
ui.red("No such version - #{plugin_name} exists, but no such " \
"version #{options[:version]} found on rubygems.org - " \
"installation failed.")
else
puts(red { "Unknown error occured " } + " - installation failed.")
ui.red("Unknown error occured - installation failed.")
end
exit 1
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
#==================================================================#
@ -391,11 +420,14 @@ module InspecPlugins
# Check for path install
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
if !status
puts(red { "No such plugin installed: " } + "#{plugin_name} - update failed")
exit 1
ui.plain("#{ui.red('No such plugin installed:')} #{plugin_name} - update failed")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
elsif status.installation_type == :path
puts(red { "Cannot update path-based install: " } + "#{plugin_name} is installed via path reference; use `inspec plugin uninstall` to remove - refusing to update")
exit 2
ui.plain("#{ui.red('Cannot update path-based install:')} " \
"#{plugin_name} is installed via path reference; " \
"use `inspec plugin uninstall` to remove - refusing to" \
"update")
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
end
end
@ -404,8 +436,10 @@ module InspecPlugins
latest_version = latest_version[plugin_name]&.last
if pre_update_versions.include?(latest_version)
puts(red { "Already installed at latest version: " } + "#{plugin_name} is at #{latest_version}, which the latest - refusing to update")
exit 2
ui.plain("#{ui.red('Already installed at latest version:')} " \
"#{plugin_name} is at #{latest_version}, which the " \
"latest - refusing to update")
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
end
end
@ -422,8 +456,10 @@ module InspecPlugins
def check_plugin_name(plugin_name, action)
unless plugin_name =~ /^(inspec|train)-/
puts(red { "Invalid plugin name" } + " - #{plugin_name} - All inspec plugins must begin with either 'inspec-' or 'train-' - #{action} failed.")
exit 1
ui.red("Invalid plugin name - #{plugin_name} - All inspec " \
"plugins must begin with either 'inspec-' or 'train-' " \
"- #{action} failed.")
ui.exit Inspec::UI::EXIT_USAGE_ERROR
end
end

View file

@ -718,7 +718,7 @@ class PluginManagerCliUpdate < Minitest::Test
update_result = run_inspec_process_with_this_plugin("plugin update inspec-test-fixture-nonesuch")
skip_windows!
assert_match(/No such plugin installed: .+ - update failed/, update_result.stdout)
assert_match(/No such plugin installed:.+ - update failed/, update_result.stdout)
assert_empty update_result.stderr
@ -808,7 +808,7 @@ class PluginManagerCliUninstall < Minitest::Test
skip_windows!
refute_includes "Inspec::Plugin::V2::UnInstallError", uninstall_result.stdout # Stacktrace marker
assert_match(/No such plugin installed: .+ - uninstall failed/, uninstall_result.stdout)
assert_match(/No such plugin installed:.+ - uninstall failed/, uninstall_result.stdout)
assert_empty uninstall_result.stderr