diff --git a/DemaConsulting.SpdxTool.sln.DotSettings b/DemaConsulting.SpdxTool.sln.DotSettings index af14ace..d0efa3a 100644 --- a/DemaConsulting.SpdxTool.sln.DotSettings +++ b/DemaConsulting.SpdxTool.sln.DotSettings @@ -1,4 +1,6 @@  True True + True + True True \ No newline at end of file diff --git a/README.md b/README.md index e2cbb92..60044fa 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ Commands: copy-package Copy package between SPDX documents (workflow only). find-package [criteria] Find package ID in SPDX document print Print text to the console - query [arguments] Query program output for value + query [arguments] Query program output for value rename-id Rename an element ID in an SPDX document. run-workflow Runs the workflow file sha256 Generate or verify sha256 hashes of files to-markdown Create Markdown summary for SPDX document update-package Update package in SPDX document (workflow only). + validate [ntia] Validate SPDX document for issues ``` @@ -232,4 +233,10 @@ steps: summary: # Optional new package summary description: # Optional new package description license: # Optional new package license + + # Validate an SPDX document +- command: validate + inputs: + spdx: # SPDX file name + ntia: true # Optional NTIA checking ``` diff --git a/spdx-workflow.yaml b/spdx-workflow.yaml index a620b69..71b7abc 100644 --- a/spdx-workflow.yaml +++ b/spdx-workflow.yaml @@ -46,7 +46,13 @@ steps: operation: generate file: ${{ spdx }} + # Validate the SPDX document +- command: validate + inputs: + spdx: ${{ spdx }} + + # Generate the summary - command: to-markdown inputs: spdx: ${{ spdx }} - markdown: ${{ summary-markdown }} \ No newline at end of file + markdown: ${{ summary-markdown }} diff --git a/src/DemaConsulting.SpdxTool/Commands/CommandRegistry.cs b/src/DemaConsulting.SpdxTool/Commands/CommandRegistry.cs index 0e0d780..516ed7a 100644 --- a/src/DemaConsulting.SpdxTool/Commands/CommandRegistry.cs +++ b/src/DemaConsulting.SpdxTool/Commands/CommandRegistry.cs @@ -20,7 +20,8 @@ public static class CommandsRegistry { RunWorkflow.Entry.Name, RunWorkflow.Entry }, { Sha256Command.Entry.Name, Sha256Command.Entry }, { ToMarkdown.Entry.Name, ToMarkdown.Entry }, - { UpdatePackage.Entry.Name, UpdatePackage.Entry } + { UpdatePackage.Entry.Name, UpdatePackage.Entry }, + { Validate.Entry.Name, Validate.Entry } }; /// diff --git a/src/DemaConsulting.SpdxTool/Commands/Validate.cs b/src/DemaConsulting.SpdxTool/Commands/Validate.cs new file mode 100644 index 0000000..c881e47 --- /dev/null +++ b/src/DemaConsulting.SpdxTool/Commands/Validate.cs @@ -0,0 +1,108 @@ +using DemaConsulting.SpdxTool.Spdx; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace DemaConsulting.SpdxTool.Commands; + +/// +/// Command to validate SPDX documents +/// +public class Validate : Command +{ + /// + /// Singleton instance of this command + /// + public static readonly Validate Instance = new(); + + /// + /// Entry information for this command + /// + public static readonly CommandEntry Entry = new( + "validate", + "validate [ntia]", + "Validate SPDX document for issues", + new[] + { + "This command validates an SPDX document for issues.", + "", + "From the command-line this can be used as:", + " spdx-tool validate [ntia]", + "", + "From a YAML file this can be used as:", + " - command: validate", + " inputs:", + " spdx: # SPDX file name", + " ntia: true # Optional NTIA checking" + }, + Instance); + + /// + /// Private constructor - this is a singleton + /// + private Validate() + { + } + + /// + public override void Run(string[] args) + { + // Report an error if for missing arguments + if (args.Length == 0) + throw new CommandUsageException("'validate' command missing arguments"); + + // Process the arguments + var spdxFile = args[0]; + var ntia = args.Skip(1).Any(a => a == "ntia"); + + // Perform validation + DoValidate(spdxFile, ntia); + } + + /// + public override void Run(YamlMappingNode step, Dictionary variables) + { + // Get the step inputs + var inputs = GetMapMap(step, "inputs"); + + // Get the 'spdx' input + var spdxFile = GetMapString(inputs, "spdx", variables) ?? + throw new YamlException(step.Start, step.End, "'to-markdown' command missing 'spdx' input"); + + // Get the 'ntia' input + var ntiaValue = GetMapString(inputs, "ntia", variables); + var ntia = ntiaValue?.ToLowerInvariant() == "true"; + + // Perform validation + DoValidate(spdxFile, ntia); + } + + /// + /// Validate SPDX document for issues + /// + /// SPDX document file name + /// NTIA flag + /// on issues + public static void DoValidate(string spdxFile, bool ntia) + { + // Load the SPDX document + var doc = SpdxHelpers.LoadJsonDocument(spdxFile); + + // Get the issues + var issues = new List(); + doc.Validate(issues, ntia); + + // Skip if no issues detected + if (issues.Count == 0) + return; + + // Report issues to console + Console.ForegroundColor = ConsoleColor.DarkYellow; + foreach (var issue in issues) + Console.WriteLine(issue); + Console.ResetColor(); + Console.WriteLine(); + + // Throw error + throw new CommandErrorException($"Found {issues.Count} Issues in {spdxFile}"); + } +} \ No newline at end of file diff --git a/src/DemaConsulting.SpdxTool/DemaConsulting.SpdxTool.csproj b/src/DemaConsulting.SpdxTool/DemaConsulting.SpdxTool.csproj index ff31f9e..e289ca2 100644 --- a/src/DemaConsulting.SpdxTool/DemaConsulting.SpdxTool.csproj +++ b/src/DemaConsulting.SpdxTool/DemaConsulting.SpdxTool.csproj @@ -40,7 +40,7 @@ - + diff --git a/test/DemaConsulting.SpdxTool.Tests/DemaConsulting.SpdxTool.Tests.csproj b/test/DemaConsulting.SpdxTool.Tests/DemaConsulting.SpdxTool.Tests.csproj index ac6a278..da6a374 100644 --- a/test/DemaConsulting.SpdxTool.Tests/DemaConsulting.SpdxTool.Tests.csproj +++ b/test/DemaConsulting.SpdxTool.Tests/DemaConsulting.SpdxTool.Tests.csproj @@ -18,9 +18,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + +