From 83fabbe3f9423ca605134c7eecfeb26585f5a5e9 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 16 Jul 2026 13:57:23 +0100 Subject: [PATCH 1/4] Extract Verify main() logic into testable run() method run() returns an exit-status int instead of calling System.exit() directly. No behavior change. Signed-off-by: Arthit Suriyawongkul --- src/main/java/org/spdx/tools/Verify.java | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/spdx/tools/Verify.java b/src/main/java/org/spdx/tools/Verify.java index 00ff30e..6c1fec5 100644 --- a/src/main/java/org/spdx/tools/Verify.java +++ b/src/main/java/org/spdx/tools/Verify.java @@ -65,10 +65,20 @@ public class Verify { * @param args args[0] SPDX file path; args[1] [RDFXML|JSON|XLS|XLSX|YAML|TAG] an optional file type - if not present, file type of the to file will be used */ public static void main(String[] args) { + System.exit(run(args)); + } + + /** + * Runs the Verify command logic and reports results to standard out/error, + * without terminating the JVM - allows the logic to be unit tested. + * @param args args[0] SPDX file path; args[1] [RDFXML|JSON|XLS|XLSX|YAML|TAG] an optional file type - if not present, file type of the to file will be used + * @return process exit status + */ + static int run(String[] args) { if (args.length < MIN_ARGS) { System.err .println("Usage:\n Verify file\nwhere file is the file path to an SPDX file"); - System.exit(ERROR_STATUS); + return ERROR_STATUS; } if (args.length > MAX_ARGS) { System.out.println("Warning: Extra arguments will be ignored"); @@ -82,7 +92,7 @@ public static void main(String[] args) { fileType = SpdxToolsHelper.strToFileType(args[1]); } catch (Exception ex) { System.err.println("Invalid file type: "+args[1]); - System.exit(ERROR_STATUS); + return ERROR_STATUS; } } else { fileType = SpdxToolsHelper.fileToFileType(new File(args[0])); @@ -90,10 +100,10 @@ public static void main(String[] args) { verify = verify(args[0], fileType); } catch (SpdxVerificationException e) { System.out.println(e.getMessage()); - System.exit(ERROR_STATUS); + return ERROR_STATUS; } catch (InvalidFileNameException e) { System.err.println("Invalid file name: "+args[0]); - System.exit(ERROR_STATUS); + return ERROR_STATUS; } // separate out the warning from errors List warnings = new ArrayList<>(); @@ -120,8 +130,9 @@ public static void main(String[] args) { } if (errors.isEmpty()) { System.out.println("This SPDX Document is valid."); + return 0; } else { - System.exit(ERROR_STATUS); + return ERROR_STATUS; } } From cf4b163d71e2b94dcef18094e280789718683501 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 16 Jul 2026 13:58:06 +0100 Subject: [PATCH 2/4] Add distinct exit code for Verify CLI usage errors USAGE_ERROR_STATUS=2 for bad invocation (missing args, invalid file type, invalid file name); ERROR_STATUS=1 stays for invalid or unreadable SPDX documents. Signed-off-by: Arthit Suriyawongkul --- src/main/java/org/spdx/tools/Verify.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/spdx/tools/Verify.java b/src/main/java/org/spdx/tools/Verify.java index 6c1fec5..2a6fdec 100644 --- a/src/main/java/org/spdx/tools/Verify.java +++ b/src/main/java/org/spdx/tools/Verify.java @@ -48,6 +48,13 @@ /** * Verifies an SPDX document and lists any verification errors + *
+ * Exit codes: + *
    + *
  • 0 - the SPDX document is valid
  • + *
  • 1 - the SPDX document is invalid, or could not be read/parsed
  • + *
  • 2 - the command was invoked incorrectly (missing/invalid arguments)
  • + *
* @author Gary O'Neall */ public class Verify { @@ -55,6 +62,7 @@ public class Verify { static final int MIN_ARGS = 1; static final int MAX_ARGS = 2; static final int ERROR_STATUS = 1; + static final int USAGE_ERROR_STATUS = 2; public static final String JSON_SCHEMA_RESOURCE_V2_3 = "resources/spdx-schema-v2.3.json"; public static final String JSON_SCHEMA_RESOURCE_V2_2 = "resources/spdx-schema-v2.2.json"; public static final String JSON_SCHEMA_RESOURCE_V3 = "resources/spdx-schema-v3.0.1.json"; @@ -72,13 +80,13 @@ public static void main(String[] args) { * Runs the Verify command logic and reports results to standard out/error, * without terminating the JVM - allows the logic to be unit tested. * @param args args[0] SPDX file path; args[1] [RDFXML|JSON|XLS|XLSX|YAML|TAG] an optional file type - if not present, file type of the to file will be used - * @return process exit status + * @return process exit status - 0 valid, {@link #ERROR_STATUS} invalid document, {@link #USAGE_ERROR_STATUS} invalid usage */ static int run(String[] args) { if (args.length < MIN_ARGS) { System.err .println("Usage:\n Verify file\nwhere file is the file path to an SPDX file"); - return ERROR_STATUS; + return USAGE_ERROR_STATUS; } if (args.length > MAX_ARGS) { System.out.println("Warning: Extra arguments will be ignored"); @@ -92,7 +100,7 @@ static int run(String[] args) { fileType = SpdxToolsHelper.strToFileType(args[1]); } catch (Exception ex) { System.err.println("Invalid file type: "+args[1]); - return ERROR_STATUS; + return USAGE_ERROR_STATUS; } } else { fileType = SpdxToolsHelper.fileToFileType(new File(args[0])); @@ -103,7 +111,7 @@ static int run(String[] args) { return ERROR_STATUS; } catch (InvalidFileNameException e) { System.err.println("Invalid file name: "+args[0]); - return ERROR_STATUS; + return USAGE_ERROR_STATUS; } // separate out the warning from errors List warnings = new ArrayList<>(); From f2ca04ac5b78803c94f066fa39b19c06b92d0717 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 16 Jul 2026 14:43:47 +0100 Subject: [PATCH 3/4] Add shared ExitCode class, adopt in Verify Signed-off-by: Arthit Suriyawongkul --- src/main/java/org/spdx/tools/ExitCode.java | 43 ++++++++++++++++++++++ src/main/java/org/spdx/tools/Verify.java | 22 +++++++---- 2 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/spdx/tools/ExitCode.java diff --git a/src/main/java/org/spdx/tools/ExitCode.java b/src/main/java/org/spdx/tools/ExitCode.java new file mode 100644 index 0000000..98b8a3b --- /dev/null +++ b/src/main/java/org/spdx/tools/ExitCode.java @@ -0,0 +1,43 @@ +/** + * SPDX-FileCopyrightText: 2026 SPDX Contributors + * SPDX-FileType: SOURCE + * SPDX-License-Identifier: Apache-2.0 + */ +package org.spdx.tools; + +/** + * Shared process exit status codes for the command line tools. + *
+ * Individual tools are free to only use a subset of these - e.g. a tool + * with no document-level validity concept may only ever return + * {@link #SUCCESS} or {@link #ERROR}. + *
+ * Values match the {@code CommandLine.ExitCode} constants used by + * picocli, a common Java CLI framework + * ({@code OK=0}, {@code SOFTWARE=1}, {@code USAGE=2}). + * + * @author Arthit Suriyawongkul + */ +public class ExitCode { + + private ExitCode() { + // Static constants only, no instances + } + + /** + * The command completed successfully. + */ + public static final int SUCCESS = 0; + + /** + * The command failed - e.g. the SPDX document is invalid, or could + * not be read/parsed. + */ + public static final int ERROR = 1; + + /** + * The command was invoked incorrectly - e.g. missing or invalid + * arguments. + */ + public static final int USAGE_ERROR = 2; +} diff --git a/src/main/java/org/spdx/tools/Verify.java b/src/main/java/org/spdx/tools/Verify.java index 2a6fdec..c627340 100644 --- a/src/main/java/org/spdx/tools/Verify.java +++ b/src/main/java/org/spdx/tools/Verify.java @@ -61,8 +61,11 @@ public class Verify { static final int MIN_ARGS = 1; static final int MAX_ARGS = 2; + /** + * @deprecated unused - retained for compatibility, use {@link ExitCode} instead + */ + @Deprecated static final int ERROR_STATUS = 1; - static final int USAGE_ERROR_STATUS = 2; public static final String JSON_SCHEMA_RESOURCE_V2_3 = "resources/spdx-schema-v2.3.json"; public static final String JSON_SCHEMA_RESOURCE_V2_2 = "resources/spdx-schema-v2.2.json"; public static final String JSON_SCHEMA_RESOURCE_V3 = "resources/spdx-schema-v3.0.1.json"; @@ -70,6 +73,8 @@ public class Verify { static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); /** + * Main entry point for the Verify tool + * * @param args args[0] SPDX file path; args[1] [RDFXML|JSON|XLS|XLSX|YAML|TAG] an optional file type - if not present, file type of the to file will be used */ public static void main(String[] args) { @@ -79,14 +84,15 @@ public static void main(String[] args) { /** * Runs the Verify command logic and reports results to standard out/error, * without terminating the JVM - allows the logic to be unit tested. + * * @param args args[0] SPDX file path; args[1] [RDFXML|JSON|XLS|XLSX|YAML|TAG] an optional file type - if not present, file type of the to file will be used - * @return process exit status - 0 valid, {@link #ERROR_STATUS} invalid document, {@link #USAGE_ERROR_STATUS} invalid usage + * @return process exit status, see {@link ExitCode} */ static int run(String[] args) { if (args.length < MIN_ARGS) { System.err .println("Usage:\n Verify file\nwhere file is the file path to an SPDX file"); - return USAGE_ERROR_STATUS; + return ExitCode.USAGE_ERROR; } if (args.length > MAX_ARGS) { System.out.println("Warning: Extra arguments will be ignored"); @@ -100,7 +106,7 @@ static int run(String[] args) { fileType = SpdxToolsHelper.strToFileType(args[1]); } catch (Exception ex) { System.err.println("Invalid file type: "+args[1]); - return USAGE_ERROR_STATUS; + return ExitCode.USAGE_ERROR; } } else { fileType = SpdxToolsHelper.fileToFileType(new File(args[0])); @@ -108,10 +114,10 @@ static int run(String[] args) { verify = verify(args[0], fileType); } catch (SpdxVerificationException e) { System.out.println(e.getMessage()); - return ERROR_STATUS; + return ExitCode.ERROR; } catch (InvalidFileNameException e) { System.err.println("Invalid file name: "+args[0]); - return USAGE_ERROR_STATUS; + return ExitCode.USAGE_ERROR; } // separate out the warning from errors List warnings = new ArrayList<>(); @@ -138,9 +144,9 @@ static int run(String[] args) { } if (errors.isEmpty()) { System.out.println("This SPDX Document is valid."); - return 0; + return ExitCode.SUCCESS; } else { - return ERROR_STATUS; + return ExitCode.ERROR; } } From 08426325b3a1336099d4102d89b935755cd33db9 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 16 Jul 2026 15:14:59 +0100 Subject: [PATCH 4/4] CompareDocs: Returns 1 if comparison failed; 2 if bad args Signed-off-by: Arthit Suriyawongkul --- .../java/org/spdx/tools/CompareSpdxDocs.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/spdx/tools/CompareSpdxDocs.java b/src/main/java/org/spdx/tools/CompareSpdxDocs.java index fdc9629..984c1f8 100644 --- a/src/main/java/org/spdx/tools/CompareSpdxDocs.java +++ b/src/main/java/org/spdx/tools/CompareSpdxDocs.java @@ -44,37 +44,63 @@ * where output.xls is a file name for the output spreadsheet file * and docX are SPDX document files to compare or directories containing SPDX documents. * Document files can be either in RDF/XML or tag/value format + *
+ * Exit codes: + *
    + *
  • 0 - the documents were compared and the output spreadsheet was written
  • + *
  • 1 - the comparison failed, or the output spreadsheet could not be written
  • + *
  • 2 - the command was invoked incorrectly (missing/invalid arguments)
  • + *
* * @author Gary O'Neall */ public class CompareSpdxDocs { static final int MIN_ARGS = 2; static final int MAX_ARGS = MultiDocumentSpreadsheet.MAX_DOCUMENTS + 1; + /** + * @deprecated unused - retained for binary/source compatibility, use {@link ExitCode} instead + */ + @Deprecated static final int ERROR_STATUS = 1; static final Logger logger = LoggerFactory.getLogger(CompareSpdxDocs.class); /** + * Main entry point for the CompareSpdxDocs tool. + * Delegates to {@link #run(String[])} and terminates the JVM with its exit status. + * * @param args args[0] is the output Excel file name, all other args are SPDX document file names */ public static void main(String[] args) { + System.exit(run(args)); + } + + /** + * Runs the CompareSpdxDocs command logic and reports results to standard + * out, without terminating the JVM - allows the logic to be unit tested. + * + * @param args args[0] is the output Excel file name, all other args are SPDX document file names + * @return process exit status, see {@link ExitCode} + */ + static int run(String[] args) { if (args.length < MIN_ARGS) { System.out.println("Insufficient arguments"); usage(); - System.exit(ERROR_STATUS); + return ExitCode.USAGE_ERROR; } if (args.length > MAX_ARGS) { System.out.println("Too many SPDX documents specified. Must be less than "+String.valueOf(MAX_ARGS-1)+" document filenames"); usage(); - System.exit(ERROR_STATUS); + return ExitCode.USAGE_ERROR; } SpdxToolsHelper.initialize(); try { onlineFunction(args); } catch (OnlineToolException e){ System.out.println(e.getMessage()); - System.exit(ERROR_STATUS); + return ExitCode.ERROR; } + return ExitCode.SUCCESS; } /**