diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index fbcd49e97..e6f6f099a 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -15,7 +15,7 @@ A `reporter` is a facility for formatting and delivering the results of a Chef I Chef InSpec allows you to output your test results to one or more reporters. -Configure the reporter(s) using either the `--reporter` option or as part of the general configuration file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). +Configure the reporter(s) using the `--reporter` option or as part of the general configuration file using the `--config` (or `--json-config`, prior to v3.6) option. Both the --reporter and --config options may be used, in which case the options are merged. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). ## Syntax diff --git a/lib/inspec/cli.rb b/lib/inspec/cli.rb index 3c0f0e91e..0b535a683 100644 --- a/lib/inspec/cli.rb +++ b/lib/inspec/cli.rb @@ -561,7 +561,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI end def run_command(opts) - runner = Inspec::Runner.new(Inspec::Config.new(opts)) + runner = Inspec::Runner.new(opts) res = runner.eval_with_virtual_profile(opts[:command]) runner.load diff --git a/lib/inspec/config.rb b/lib/inspec/config.rb index 54a0ac805..c9314ee4e 100644 --- a/lib/inspec/config.rb +++ b/lib/inspec/config.rb @@ -448,6 +448,13 @@ module Inspec # Reporter options may be defined top-level. options.merge!(config_file_reporter_options) + if @cli_opts["reporter"] + # Add reporter_cli_opts in options to capture reporter cli opts separately + options.merge!({ "reporter_cli_opts" => @cli_opts["reporter"] }) + # Delete reporter from cli_opts to avoid direct merging of reporter info of cli and config + @cli_opts.delete("reporter") + end + # Highest precedence: merge in any options defined via the CLI options.merge!(@cli_opts) @@ -476,13 +483,13 @@ module Inspec end def finalize_parse_reporters(options) # rubocop:disable Metrics/AbcSize - # default to cli report for ad-hoc runners - options["reporter"] = ["cli"] if options["reporter"].nil? + # Default to cli report for ad-hoc runners + options["reporter_cli_opts"] = ["cli"] if (options["reporter"].nil? || options["reporter"].empty?) && options["reporter_cli_opts"].nil? - # parse out cli to proper report format - if options["reporter"].is_a?(Array) + # Parse out reporter_cli_opts to proper report format + if options["reporter_cli_opts"].is_a?(Array) reports = {} - options["reporter"].each do |report| + options["reporter_cli_opts"].each do |report| reporter_name, destination = report.split(":", 2) if destination.nil? || destination.strip == "-" reports[reporter_name] = { "stdout" => true } @@ -494,7 +501,12 @@ module Inspec reports[reporter_name]["target_id"] = options["target_id"] if options["target_id"] end end - options["reporter"] = reports + + if options["reporter"].nil? || options["reporter"].empty? + options["reporter"] = reports + else + options["reporter"].merge!(reports) + end end # add in stdout if not specified @@ -507,6 +519,10 @@ module Inspec end validate_reporters!(options["reporter"]) + + # Delete reporter_cli_opts after graceful merging of cli and config reporters + options.delete("reporter_cli_opts") + options end @@ -548,15 +564,12 @@ module Inspec class Defaults DEFAULTS = { exec: { - "reporter" => ["cli"], "show_progress" => false, "color" => true, "create_lockfile" => true, "backend_cache" => true, }, - shell: { - "reporter" => ["cli"], - }, + shell: {}, }.freeze def self.for_command(command_name) diff --git a/test/fixtures/config_dirs/json-config/reporter-cli-config.json b/test/fixtures/config_dirs/json-config/reporter-cli-config.json new file mode 100644 index 000000000..9ab47b5a7 --- /dev/null +++ b/test/fixtures/config_dirs/json-config/reporter-cli-config.json @@ -0,0 +1,7 @@ +{ + "reporter": { + "cli" : { + "stdout" : true + } + } +} \ No newline at end of file diff --git a/test/functional/inputs_test.rb b/test/functional/inputs_test.rb index d9f403535..59112fc27 100644 --- a/test/functional/inputs_test.rb +++ b/test/functional/inputs_test.rb @@ -81,7 +81,7 @@ describe "inputs" do let(:common_options) do { profile: "#{inputs_profiles_path}/via-runner", - reporter: ["json"], + "reporter" => ["json"], } end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 305af1808..7093f8bc3 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1085,6 +1085,34 @@ describe "inspec exec" do end end + describe "When specifying a config file and --reporter option to configure reporter in a run in a correct manner" do + it "should obey the configurations of both --reporter and config reporter options" do + outpath = Dir.tmpdir + cli_args = "--no-create-lockfile --reporter json:#{outpath}/foo/bar/test.json --config " + File.join(config_dir_path, "json-config", "reporter-cli-config.json") + inspec("exec #{complete_profile} #{cli_args}") + + # File specified with --reporter option - test to see file exists + _(File.exist?("#{outpath}/foo/bar/test.json")).must_equal true + _(File.stat("#{outpath}/foo/bar/test.json").size).must_be :>, 0 + # STDOUT true set using config - test to see if this is obeyed + _(stdout).must_include "complete example profile (complete)" + _(stdout).must_include "1 successful control" + _(stdout).must_include "0 control failures" + _(stdout).must_include "0 controls skipped" + _(stderr).must_be_empty + assert_exit_code 0, out + end + end + + describe "When specifying a config file and --reporter option to configure reporter with stdout true from both the options" do + let(:cli_args) { "--config " + File.join(config_dir_path, "json-config", "reporter-cli-config.json") + " --reporter json html2" } + let(:run_result) { run_inspec_process("exec " + File.join(profile_path, "basic_profile") + " " + cli_args) } + it "should raise error that only single reporter can have output to stdout" do + _(run_result.stderr).wont_equal "" + _(run_result.stderr).must_include "The option --reporter can only have a single report outputting to stdout." + end + end + describe "when specifying the execution target" do let(:local_plat) do json = run_inspec_process("detect --format json", {}).stdout