diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bb4db711b..334f1dba8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,10 @@ Changelog v33.0.0 (unreleased) -------------------- +- Enhance the ``output`` management command to support providing multiple formats at + once. + https://github.com/nexB/scancode.io/issues/646 + - Remove the admin app and views. https://github.com/nexB/scancode.io/issues/645 diff --git a/docs/command-line-interface.rst b/docs/command-line-interface.rst index 8c7897348..0f28963c1 100644 --- a/docs/command-line-interface.rst +++ b/docs/command-line-interface.rst @@ -195,12 +195,21 @@ Displays status information about the ``PROJECT`` project. This can be disabled providing the ``--verbosity 0`` option. -`$ scanpipe output --project PROJECT --format {json,csv,xlsx}` --------------------------------------------------------------- +`$ scanpipe output --project PROJECT --format {json,csv,xlsx,spdx,cyclonedx}` +----------------------------------------------------------------------------- -Outputs the ``PROJECT`` results as JSON, CSV or XLSX. +Outputs the ``PROJECT`` results as JSON, XLSX, CSV, SPDX, and CycloneDX. The output files are created in the ``PROJECT`` :guilabel:`output/` directory. +Multiple formats can be provided at once:: + + $ scanpipe output --project foo --format json xlsx spdx cyclonedx + +Optional arguments: + +- ``--print`` Print the output to stdout instead of creating a file. This is not + compatible with the XLSX and CSV formats. + It cannot be used when multiple formats are provided. `$ scanpipe graph [PIPELINE_NAME ...]` -------------------------------------- diff --git a/scanpipe/management/commands/output.py b/scanpipe/management/commands/output.py index 240d8c23b..504cbb484 100644 --- a/scanpipe/management/commands/output.py +++ b/scanpipe/management/commands/output.py @@ -33,7 +33,8 @@ def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( "--format", - default="json", + default=["json"], + nargs="+", choices=["json", "csv", "xlsx", "spdx", "cyclonedx"], help="Specifies the output serialization format for the results.", ) @@ -46,25 +47,31 @@ def add_arguments(self, parser): def handle(self, *args, **options): super().handle(*args, **options) print_to_stdout = options["print"] - format = options["format"] + formats = options["format"] - output_function = { - "json": output.to_json, - "csv": output.to_csv, - "xlsx": output.to_xlsx, - "spdx": output.to_spdx, - "cyclonedx": output.to_cyclonedx, - }.get(format) + if print_to_stdout and len(formats) > 1: + raise CommandError( + "--print cannot be used when multiple formats are provided." + ) - if print_to_stdout and format in ["xlsx", "csv"]: + if print_to_stdout and ("xlsx" in formats or "csv" in formats): raise CommandError("--print is not compatible with xlsx and csv formats.") - output_file = output_function(self.project) + for format_ in formats: + output_function = { + "json": output.to_json, + "csv": output.to_csv, + "xlsx": output.to_xlsx, + "spdx": output.to_spdx, + "cyclonedx": output.to_cyclonedx, + }.get(format_) - if isinstance(output_file, list): - output_file = "\n".join([str(path) for path in output_file]) + output_file = output_function(self.project) - if options["print"]: - self.stdout.write(output_file.read_text()) - else: - self.stdout.write(str(output_file), self.style.SUCCESS) + if isinstance(output_file, list): + output_file = "\n".join([str(path) for path in output_file]) + + if options["print"]: + self.stdout.write(output_file.read_text()) + else: + self.stdout.write(str(output_file), self.style.SUCCESS) diff --git a/scanpipe/tests/test_commands.py b/scanpipe/tests/test_commands.py index 52ff5699b..99300cf21 100644 --- a/scanpipe/tests/test_commands.py +++ b/scanpipe/tests/test_commands.py @@ -421,6 +421,15 @@ def test_scanpipe_management_command_output(self): filename = output_file.split("/")[-1] self.assertIn(filename, project.output_root) + out = StringIO() + options = ["--project", project.name, "--no-color"] + options.extend(["--format", "spdx", "xlsx"]) + call_command("output", *options, stdout=out) + out_value = out.getvalue().strip() + for output_file in out_value.split("\n"): + filename = output_file.split("/")[-1] + self.assertIn(filename, project.output_root) + out = StringIO() options = ["--project", project.name, "--no-color"] options.extend(["--format", "WRONG"])