From 09f15daa898743ce41ba1eeb54d2fb1dd7868d59 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 14:12:30 +0200 Subject: [PATCH 01/13] spdx-diff: cli: refactor by removing show-[added|removed|changed],full and summary filtering options The following arguments are removed: - '--summary' - '--full' - '--show-added' - '--show-changed' - '--show-removed' The purpose of this tool is to highlight differences between two spdx files. Built-in filtering adds extra logic that dilutes this goal and overlaps with the other tools that already provide their own filtering capabilities. By removing filtering, the tool now focuses on producing a complete and reliable spdx diff, leaving filtering decisions to users or downstream tooling. Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 153 ++++++------------------------------------- 1 file changed, 20 insertions(+), 133 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 74860ae..76b0c42 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -274,11 +274,6 @@ def print_diff( added: dict[str, Any], removed: dict[str, Any], changed: dict[str, Any], - *, - show_all: bool = False, - show_added: bool = True, - show_removed: bool = True, - show_changed: bool = True, ) -> None: """ Print differences between items. @@ -288,23 +283,19 @@ def print_diff( added: Added items removed: Removed items changed: Changed items - show_all: Whether to show even if empty - show_added: Whether to show added items - show_removed: Whether to show removed items - show_changed: Whether to show changed items """ - if show_added and (show_all or added): + if added: print(f"\n{title} - Added:") for k in sorted(added): print(f" + {k}" if isinstance(added, list) else f" + {k}: {added[k]}") - if show_removed and (show_all or removed): + if removed: print(f"\n{title} - Removed:") for k in sorted(removed): print(f" - {k}" if isinstance(removed, list) else f" - {k}: {removed[k]}") - if show_changed and changed and (show_all or changed): + if changed: print(f"\n{title} - Changed:") for k in sorted(changed): print(f" ~ {k}: {changed[k]['from']} -> {changed[k]['to']}") @@ -314,11 +305,6 @@ def print_packageconfig_diff( added: dict[str, dict[str, str]], removed: dict[str, dict[str, str]], changed: dict[str, dict[str, Any]], - *, - show_all: bool = False, - show_added: bool = True, - show_removed: bool = True, - show_changed: bool = True, ) -> None: """ Print PACKAGECONFIG differences. @@ -327,27 +313,23 @@ def print_packageconfig_diff( added: Added packages with their features removed: Removed packages with their features changed: Changed packages with feature differences - show_all: Whether to show even if empty - show_added: Whether to show added items - show_removed: Whether to show removed items - show_changed: Whether to show changed items """ - if show_added and (show_all or added): + if added: print("\nPACKAGECONFIG - Added Packages:") for pkg in sorted(added): print(f" + {pkg}:") for feature, value in sorted(added[pkg].items()): print(f" {feature}: {value}") - if show_removed and (show_all or removed): + if removed: print("\nPACKAGECONFIG - Removed Packages:") for pkg in sorted(removed): print(f" - {pkg}:") for feature, value in sorted(removed[pkg].items()): print(f" {feature}: {value}") - if show_changed and (show_all or changed): + if changed: print("\nPACKAGECONFIG - Changed Packages:") for pkg in sorted(changed): print(f" ~ {pkg}:") @@ -363,56 +345,6 @@ def print_packageconfig_diff( print(f" ~ {feature}: {change['from']} -> {change['to']}") -def print_summary( - pkg_diff: tuple[dict[str, Any], dict[str, Any], dict[str, Any]], - cfg_diff: tuple[dict[str, Any], dict[str, Any], dict[str, Any]], - pcfg_diff: tuple[ - dict[str, dict[str, str]], dict[str, dict[str, str]], dict[str, dict[str, Any]] - ], -) -> None: - """ - Print summary statistics of differences. - - Args: - pkg_diff: Package differences - cfg_diff: Kernel config differences - pcfg_diff: PACKAGECONFIG differences - - """ - print("\nSPDX-Diff Summary:\n") - - print("Packages:") - print(f" Added: {len(pkg_diff[0])}") - print(f" Removed: {len(pkg_diff[1])}") - print(f" Changed: {len(pkg_diff[2])}") - - print("\nKernel Config:") - print(f" Added: {len(cfg_diff[0])}") - print(f" Removed: {len(cfg_diff[1])}") - print(f" Changed: {len(cfg_diff[2])}") - - print("\nPACKAGECONFIG:") - print(f" Packages Added: {len(pcfg_diff[0])}") - print(f" Packages Removed: {len(pcfg_diff[1])}") - print(f" Packages Changed: {len(pcfg_diff[2])}") - - # Count total feature changes - total_features_added = sum(len(v.get("added", {})) for v in pcfg_diff[2].values()) - total_features_removed = sum( - len(v.get("removed", {})) for v in pcfg_diff[2].values() - ) - total_features_changed = sum( - len(v.get("changed", {})) for v in pcfg_diff[2].values() - ) - - if total_features_added or total_features_removed or total_features_changed: - print(f" Features Added: {total_features_added}") - print(f" Features Removed: {total_features_removed}") - print(f" Features Changed: {total_features_changed}") - - print() - - def write_diff_to_json( pkg_diff: tuple[dict[str, Any], dict[str, Any], dict[str, Any]], cfg_diff: tuple[dict[str, Any], dict[str, Any], dict[str, Any]], @@ -487,11 +419,6 @@ def main() -> None: type=path_is_file, help="New SPDX3 JSON file", ) - parser.add_argument( - "--full", - action="store_true", - help="For console output, always show section names (added, removed, changed)", - ) timestamp = datetime.now(tz=timezone.utc).astimezone().strftime("%Y%m%d-%H%M%S") default_output = f"spdx_diff_{timestamp}.json" parser.add_argument( @@ -507,11 +434,6 @@ def main() -> None: action="store_true", help="Ignore packages with LicenseRef-Proprietary", ) - parser.add_argument( - "--summary", - action="store_true", - help="Show only summary statistics without detailed differences", - ) parser.add_argument( "--format", choices=["text", "json", "both"], @@ -520,21 +442,6 @@ def main() -> None: ) # Output filtering options - parser.add_argument( - "--show-added", - action="store_true", - help="Show only added items", - ) - parser.add_argument( - "--show-removed", - action="store_true", - help="Show only removed items", - ) - parser.add_argument( - "--show-changed", - action="store_true", - help="Show only changed items", - ) parser.add_argument( "--show-packages", action="store_true", @@ -563,11 +470,6 @@ def main() -> None: # Determine what to show based on flags # If no specific show flags are set, show everything - show_all_change = not (args.show_added or args.show_removed or args.show_changed) - show_added = args.show_added or show_all_change - show_removed = args.show_removed or show_all_change - show_changed = args.show_changed or show_all_change - show_all_category = not ( args.show_packages or args.show_config or args.show_packageconfig ) @@ -594,35 +496,20 @@ def main() -> None: ) # Print summary or full output - if args.summary: - print_summary(pkg_diff, cfg_diff, pcfg_light_diff) - elif args.format in {"text", "both"}: - if show_packages: - print_diff( - "Packages", - *pkg_diff, - show_all=args.full, - show_added=show_added, - show_removed=show_removed, - show_changed=show_changed, - ) - if show_config: - print_diff( - "Kernel Config", - *cfg_diff, - show_all=args.full, - show_added=show_added, - show_removed=show_removed, - show_changed=show_changed, - ) - if show_packageconfig: - print_packageconfig_diff( - *pcfg_light_diff, - show_all=args.full, - show_added=show_added, - show_removed=show_removed, - show_changed=show_changed, - ) + if show_packages: + print_diff( + "Packages", + *pkg_diff, + ) + if show_config: + print_diff( + "Kernel Config", + *cfg_diff, + ) + if show_packageconfig: + print_packageconfig_diff( + *pcfg_light_diff, + ) if args.format in ["json", "both"]: write_diff_to_json(pkg_diff, cfg_diff, pcfg_light_diff, args.output) From bae12eb9d81c39130354438b71a458133ed59d0b Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 13:42:41 +0200 Subject: [PATCH 02/13] doc: Update INSTALL.md and README.md for filtering text output removal Signed-off-by: Christophe Guerreiro --- INSTALL.md | 3 --- README.md | 47 ++--------------------------------------------- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index b971d7e..f6ed6d7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -49,9 +49,6 @@ spdx-diff --help # Compare two SPDX files spdx-diff reference.json new.json -# Show summary only -spdx-diff reference.json new.json --summary - # JSON output for automation spdx-diff reference.json new.json --format json --output results.json ``` diff --git a/README.md b/README.md index 6ee7133..4b182a5 100644 --- a/README.md +++ b/README.md @@ -21,22 +21,14 @@ Required arguments: - `new`: Path to the newer SPDX3 JSON file. Optional arguments: - - `--full`: For console output, always show section names (added, removed, - changed) even if there is no difference. - `--output `: Save diff results to the given JSON file. Default: `spdx_diff_.json` - `--ignore-proprietary`: Ignore packages with LicenseRef-Proprietary. - - `--summary`: Show only summary statistics without detailed differences. - `--format {text,json,both}`: Control output format: - `text`: Console output only (no JSON file) - `json`: JSON file only (silent mode for automation) - `both`: Both console and JSON output (**default**) -Output filtering - change type: - - `--show-added`: Show only added items. - - `--show-removed`: Show only removed items. - - `--show-changed`: Show only changed items. - Output filtering - category: - `--show-packages`: Show only package differences. - `--show-config`: Show only kernel config differences. @@ -67,27 +59,6 @@ Symbols: - removed ~ changed -Summary Mode ------------- -When using --summary, the tool displays aggregate statistics: - -``` -SPDX-Diff Summary: - -Packages: - Added: 5 - Removed: 2 - Changed: 3 - -Kernel Config: - Added: 10 - Removed: 3 - Changed: 7 - -PACKAGECONFIG: - Features Added: 12 - Features Removed: 4 - Features Changed: 6 ``` JSON Diff File @@ -161,28 +132,14 @@ Examples ./spdx-diff reference.json new.json ### Full details with proprietary packages excluded: - ./spdx-diff reference.json new.json --ignore-proprietary --full + ./spdx-diff reference.json new.json --ignore-proprietary -### Quick summary check: - ./spdx-diff reference.json new.json --summary ### Silent mode for CI/CD (JSON output only): ./spdx-diff reference.json new.json --format json --output results.json ### Console output only (no JSON file): - ./spdx-diff reference.json new.json --format text --full - -### Show only changed packages: - ./spdx-diff reference.json new.json --show-packages --show-changed - -### Show only added packages: - ./spdx-diff reference.json new.json --show-packages --show-added - -### Show only kernel config changes: - ./spdx-diff reference.json new.json --show-config --show-changed - -### Show added and changed items across all categories: - ./spdx-diff reference.json new.json --show-added --show-changed + ./spdx-diff reference.json new.json --format text ### Show only PACKAGECONFIG differences: ./spdx-diff reference.json new.json --show-packageconfig From 28d15d63da12cfbc6bf9ff7491da941e99270e99 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 15:09:27 +0200 Subject: [PATCH 03/13] CHANGELOG: Refactor tool by removing filtering options. Signed-off-by: Christophe Guerreiro --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18001c6..16f12e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ All notable changes to this project will be documented in this file. ### Removed +- Refocused the tool on producing complete and reliable spdx diff: + - Removing output filtering options: + - `--show-added` + - `--show-changed` + - `--show-removed` + - The default behaviour is to show the full spdx diff, following arguments are removed: + - `--full` + - `--summary` + ## [1.0.1] - 2026-02-12 ### Added From 954b546d340ef2546aa3b403e8c343a823e24d49 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 17:27:22 +0200 Subject: [PATCH 04/13] spdx-diff: cli: Refactor filtering category arguments names and behavior The following filtering category arguments have been renamed and converted to boolean options, all defaulting to `True`: - `--show-packages` to `--[no-]packages` - `--show-config` to `--[no-]kernel-config` - `--show-packageconfig` to `--[no-]packageconfig` - `--ignore-proprietary` to `--[no-]packages-proprietary` Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 68 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 76b0c42..76df455 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -5,7 +5,7 @@ import logging import pathlib import re -from argparse import ArgumentParser, ArgumentTypeError +from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction from collections import defaultdict from datetime import datetime, timezone from typing import Any @@ -131,14 +131,14 @@ def normalize_package_name(name: str) -> str: match = re.search(kernel_version_pattern, name) return name[: match.start()] if match else name - def extract_spdx_data(self, ignore_proprietary: bool = False) -> None: + def extract_spdx_data(self, include_packages_proprietary: bool = True) -> None: """ Extract SPDX information (packages, kernel CONFIG, and PACKAGECONFIG). Extract SPDX package data, kernel CONFIG options, and PACKAGECONFIG entries from the SPDX JSON file. Kernel packages are automatically normalized. - :param ignore_proprietary: Whether to skip proprietary packages + :param include_packages_proprietary: Whether to skip proprietary packages """ build_count = 0 @@ -150,7 +150,10 @@ def extract_spdx_data(self, ignore_proprietary: bool = False) -> None: if not pkg_name or not version: continue - if ignore_proprietary and self.is_package_proprietary(item): + if ( + not include_packages_proprietary + and self.is_package_proprietary(item) + ): _logger.info("Ignoring proprietary package: %s", pkg_name) continue @@ -429,11 +432,6 @@ def main() -> None: default=default_output, help="Optional output file name (JSON)", ) - parser.add_argument( - "--ignore-proprietary", - action="store_true", - help="Ignore packages with LicenseRef-Proprietary", - ) parser.add_argument( "--format", choices=["text", "json", "both"], @@ -441,21 +439,31 @@ def main() -> None: help="Output format: text (console only), json (file only), or both (default)", ) - # Output filtering options - parser.add_argument( - "--show-packages", - action="store_true", - help="Show only package differences", + # Output filtering category options + text_output_group = parser.add_argument_group("for text output") + text_output_group.add_argument( + "--kernel-config", + action=BooleanOptionalAction, + default=True, + help="show|hide kernel config differences (default: yes)", ) - parser.add_argument( - "--show-config", - action="store_true", - help="Show only kernel config differences", + text_output_group.add_argument( + "--packageconfig", + action=BooleanOptionalAction, + default=True, + help="show|hide PACKAGECONFIG differences (default: yes)", ) - parser.add_argument( - "--show-packageconfig", - action="store_true", - help="Show only PACKAGECONFIG differences", + text_output_group.add_argument( + "--packages", + action=BooleanOptionalAction, + default=True, + help="show|hide package differences (default: yes)", + ) + text_output_group.add_argument( + "--packages-proprietary", + action=BooleanOptionalAction, + default=True, + help="show|hide packages with LicenseRef-Proprietary (default: yes)", ) args = parser.parse_args() @@ -470,19 +478,17 @@ def main() -> None: # Determine what to show based on flags # If no specific show flags are set, show everything - show_all_category = not ( - args.show_packages or args.show_config or args.show_packageconfig - ) - show_packages = args.show_packages or show_all_category - show_config = args.show_config or show_all_category - show_packageconfig = args.show_packageconfig or show_all_category + + show_packages = args.packages + show_kernel_config = args.kernel_config + show_packageconfig = args.packageconfig try: sbom_ref = Spdx3Sbom(args.reference) - sbom_ref.extract_spdx_data(args.ignore_proprietary) + sbom_ref.extract_spdx_data(args.packages_proprietary) sbom_new = Spdx3Sbom(args.new) - sbom_new.extract_spdx_data(args.ignore_proprietary) + sbom_new.extract_spdx_data(args.packages_proprietary) except (ValueError, TypeError) as e: parser.error(str(e)) @@ -501,7 +507,7 @@ def main() -> None: "Packages", *pkg_diff, ) - if show_config: + if show_kernel_config: print_diff( "Kernel Config", *cfg_diff, From 242af476757fe2530f1845898dc780b18cae3b55 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 16:15:25 +0200 Subject: [PATCH 05/13] tests: Align tests with renamed command-line option The filtering option `--ignore-proprietary` has been renamed to `--packages-proprietary` and now follows a new boolean behavior: - `--packages-proprietary` includes proprietary packages (default) - `--no-packages-proprietary` excludes proprietary packages Tests have been updated accordingly to reflect this new option and logic. Signed-off-by: Christophe Guerreiro --- tests/helper.py | 2 +- tests/test_package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/helper.py b/tests/helper.py index f71f72c..d60df3e 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -145,5 +145,5 @@ def run_spdx_diff_check( sbom_ref, sbom_new, exp_diff, - ["--ignore-proprietary", *extra_args], + ["--no-packages-proprietary", *extra_args], ) diff --git a/tests/test_package.py b/tests/test_package.py index 36ac913..5455838 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -61,7 +61,7 @@ def test_new_pkg_ign_proprietary( "reference-sbom.spdx.json", sbom_new_name, exp, - ["--ignore-proprietary"], + ["--no-packages-proprietary"], ) From c8f7050690bc7dcebc154076592ba68b3fcce179 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Mon, 11 May 2026 11:49:31 +0200 Subject: [PATCH 06/13] spdx-diff: cli: Format BooleanOptionalAction help as --[no-]flag Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 76df455..a1f6d52 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -5,7 +5,13 @@ import logging import pathlib import re -from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction +from argparse import ( + Action, + ArgumentParser, + ArgumentTypeError, + BooleanOptionalAction, + HelpFormatter, +) from collections import defaultdict from datetime import datetime, timezone from typing import Any @@ -203,6 +209,32 @@ def extract_spdx_data(self, include_packages_proprietary: bool = True) -> None: ) +class CustomBooleanOptionalActionFormatter(HelpFormatter): + """ + Custom argparse help formatter for BooleanOptionalAction arguments. + + This formatter changes the default argparse rendering of boolean + optional flags from: + + --foo, --no-foo + + to the more compact form: + + --[no-]foo + + This only affects the help text display. Argument parsing behavior + remains unchanged. + """ + + def _format_action_invocation(self, action: Action) -> str: + if isinstance(action, BooleanOptionalAction): + # On prend le nom principal + opt = action.option_strings[0] + name = opt[2:] # enlève '--' + return f"--[no-]{name}" + return super()._format_action_invocation(action) + + def compare_dicts( ref: dict[str, Any], new: dict[str, Any] ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]: @@ -401,7 +433,8 @@ def main() -> None: Parse arguments, extract SPDX data, compare, and print/write diffs. """ - parser = ArgumentParser(description="Compare SPDX3 JSON files") + parser = ArgumentParser(description="Compare SPDX3 JSON files", + formatter_class=CustomBooleanOptionalActionFormatter) parser.add_argument( "--version", action="version", version=f"spdx-diff {__version__}" ) From ea7db8b28d8602dbf4a8b2355cc5b970aea047e3 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 13:44:07 +0200 Subject: [PATCH 07/13] doc: Update README.md for tools parameters renamed Signed-off-by: Christophe Guerreiro --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4b182a5..f88f470 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,16 @@ Required arguments: Optional arguments: - `--output `: Save diff results to the given JSON file. Default: `spdx_diff_.json` - - `--ignore-proprietary`: Ignore packages with LicenseRef-Proprietary. - `--format {text,json,both}`: Control output format: - `text`: Console output only (no JSON file) - `json`: JSON file only (silent mode for automation) - `both`: Both console and JSON output (**default**) -Output filtering - category: - - `--show-packages`: Show only package differences. - - `--show-config`: Show only kernel config differences. - - `--show-packageconfig`: Show only PACKAGECONFIG differences. +Text output filtering - category : + - `--[no-]packages`: show|hide package differences. + - `--[no-]kernel-config`: show|hide kernel config differences. + - `--[no-]packageconfig`: show|hide PACKAGECONFIG differences. + - `--[no-]packages-proprietary`: show|hide packages with LicenseRef-Proprietary. Output ------ @@ -132,8 +132,7 @@ Examples ./spdx-diff reference.json new.json ### Full details with proprietary packages excluded: - ./spdx-diff reference.json new.json --ignore-proprietary - + ./spdx-diff reference.json new.json --no-packages-proprietary ### Silent mode for CI/CD (JSON output only): ./spdx-diff reference.json new.json --format json --output results.json @@ -141,8 +140,8 @@ Examples ### Console output only (no JSON file): ./spdx-diff reference.json new.json --format text -### Show only PACKAGECONFIG differences: - ./spdx-diff reference.json new.json --show-packageconfig +### Exclude on console PACKAGECONFIG differences: + ./spdx-diff reference.json new.json --no-packageconfig Console output example: ``` From a32be16e3e02e1d6b74ecc226a009dcaadf02f40 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Mon, 11 May 2026 14:37:03 +0200 Subject: [PATCH 08/13] CHANGELOG: Filtering category arguments converted to boolean options Signed-off-by: Christophe Guerreiro --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f12e1..1b1b2e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ All notable changes to this project will be documented in this file. ### Changed +- Add custom `HelpFormatter` to render boolean flags in a compact `--[no-]flag` format instead of the default `--flag, --no-flag`. +- The following filtering category arguments have been renamed and converted to +boolean options, all defaulting to `True`: + - `--show-packages` to `--[no-]packages`(default: --packages) + - `--show-config` to `--[no-]kernel-config`(default: --kernel-config) + - `--show-packageconfig` to `--[no-]packageconfig`(default: --packageconfig) + - `--ignore-proprietary` to `--[no-]packages-proprietary`(default: --packages-proprietary) + ### Fixed ### Removed From 34bcf405b07c47c0ffa163850d58f093f8cb61f7 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 09:55:04 +0200 Subject: [PATCH 09/13] spdx-diff: cli: Add human readable option Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 45 +++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index a1f6d52..54482af 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -5,6 +5,7 @@ import logging import pathlib import re +import sys from argparse import ( Action, ArgumentParser, @@ -471,6 +472,12 @@ def main() -> None: default="both", help="Output format: text (console only), json (file only), or both (default)", ) + parser.add_argument( + "-H", + "--human-readable", + action="store_true", + help="Output results in a human-readable text format", + ) # Output filtering category options text_output_group = parser.add_argument_group("for text output") @@ -515,6 +522,7 @@ def main() -> None: show_packages = args.packages show_kernel_config = args.kernel_config show_packageconfig = args.packageconfig + human_readable_output = args.human_readable try: sbom_ref = Spdx3Sbom(args.reference) @@ -534,25 +542,24 @@ def main() -> None: pcfg_diff[2], ) - # Print summary or full output - if show_packages: - print_diff( - "Packages", - *pkg_diff, - ) - if show_kernel_config: - print_diff( - "Kernel Config", - *cfg_diff, - ) - if show_packageconfig: - print_packageconfig_diff( - *pcfg_light_diff, - ) - - if args.format in ["json", "both"]: - write_diff_to_json(pkg_diff, cfg_diff, pcfg_light_diff, args.output) - + # Print human readable information if --human-readable is set + if human_readable_output: + if show_packages: + print_diff( + "Packages", + *pkg_diff, + ) + if show_kernel_config: + print_diff( + "Kernel Config", + *cfg_diff, + ) + if show_packageconfig: + print_packageconfig_diff( + *pcfg_light_diff, + ) + else: + write_diff_to_json(pkg_diff, cfg_diff, pcfg_light_diff, args.json_output) if __name__ == "__main__": main() From 7546540e24ea650596bdaedf585b16ad54966597 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 10:54:51 +0200 Subject: [PATCH 10/13] spdx-diff: cli: Change output format to JSON and enforce JSON filename argument Previously, the tool was printing human-readable text to stdout and generating a JSON file separately. This change enforces the presence of an explicit argument used as the JSON output filename. The console output is now emitted in JSON format by default to ensure consistency and to simplify integration with automated pipelines and tooling. Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 54482af..3b2396b 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -417,8 +417,15 @@ def write_diff_to_json( "changed": dict(sorted(pcfg_diff[2].items())), }, } - with output_file.open("w", encoding="utf-8") as f: - json.dump(delta, f, indent=2, ensure_ascii=False) + # Write the resulting SPDX diff JSON to stdout for piping + json.dump(delta, sys.stdout, indent=2, ensure_ascii=False) + sys.stdout.write("\n") + + # Write the resulting SPDX diff JSON to file + if output_file is not None: + _logger.info("Writing diff results to %s", output_file) + with output_file.open("w", encoding="utf-8") as f: + json.dump(delta, f, indent=2, ensure_ascii=False) def path_is_file(value: str) -> pathlib.Path: @@ -456,21 +463,13 @@ def main() -> None: type=path_is_file, help="New SPDX3 JSON file", ) - timestamp = datetime.now(tz=timezone.utc).astimezone().strftime("%Y%m%d-%H%M%S") - default_output = f"spdx_diff_{timestamp}.json" parser.add_argument( - "--output", - "-o", + "-j", + "--json-output", metavar="PATH", type=pathlib.Path, - default=default_output, - help="Optional output file name (JSON)", - ) - parser.add_argument( - "--format", - choices=["text", "json", "both"], - default="both", - help="Output format: text (console only), json (file only), or both (default)", + default=None, + help="JSON Output file name (default: none)", ) parser.add_argument( "-H", From a14f50932ec6d2c1b7f0d8137901c9f8fe26201c Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 16:20:13 +0200 Subject: [PATCH 11/13] tests: Update CLI usage to match new JSON output option Tests have been updated to reflect the new command-line interface. The previous `--format json --output` options are replaced with the `--json-output` option, matching the current tool behavior. Signed-off-by: Christophe Guerreiro --- tests/helper.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/helper.py b/tests/helper.py index d60df3e..68834ec 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -102,9 +102,7 @@ def _run_spdx_diff_check( [ os.fspath(sbom_data_path.joinpath(sbom_ref).resolve(strict=True)), os.fspath(sbom_data_path.joinpath(sbom_new).resolve(strict=True)), - "--format", - "json", - "--output", + "--json-output", os.fspath(out_path), *extra_args, ], From 0f528b447538bec5b76ed0a91f651bbaf5ac88a3 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 13:49:37 +0200 Subject: [PATCH 12/13] doc: Update INSTALL.md and README.md for console output and JSON handling Signed-off-by: Christophe Guerreiro --- INSTALL.md | 9 ++++++--- README.md | 29 ++++++++++++++++------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f6ed6d7..ffba52d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -46,11 +46,14 @@ After installation: # Show help spdx-diff --help -# Compare two SPDX files +# Compare two SPDX files, JSON output on stdout by default spdx-diff reference.json new.json -# JSON output for automation -spdx-diff reference.json new.json --format json --output results.json +# Compare two SPDX files, human readable output on stdout +spdx-diff reference.json new.json --human-readable + +# With JSON file output generated +spdx-diff reference.json new.json --json-output results.json ``` See `README.md` for full documentation. diff --git a/README.md b/README.md index f88f470..eb0d3c6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ This tool compares two SPDX3 JSON documents and reports differences in: - Kernel configuration parameters (CONFIG_*) - PACKAGECONFIG entries per package -It produces both human-readable output (console) and a structured JSON diff file. +The application separates human-readable and machine-readable outputs to improve automation and pipeline integration. + +- By default, the tool emits structured **JSON output**, making it suitable for consumption by scripts and CI/CD pipelines. +- The default output format can be changed into a human-readable (text) using --human-readable optional argument. +- When a JSON filename parameter is provided, the JSON result is also written to the specified file. Usage ----- @@ -21,12 +25,8 @@ Required arguments: - `new`: Path to the newer SPDX3 JSON file. Optional arguments: - - `--output `: Save diff results to the given JSON file. - Default: `spdx_diff_.json` - - `--format {text,json,both}`: Control output format: - - `text`: Console output only (no JSON file) - - `json`: JSON file only (silent mode for automation) - - `both`: Both console and JSON output (**default**) + - `--json-output `: Save diff results to the given JSON file. + - `--human-readable`: Output results in a human-readable text format. Text output filtering - category : - `--[no-]packages`: show|hide package differences. @@ -128,22 +128,25 @@ The script uses Python's logging module: Examples -------- -### Basic comparison with both console and JSON output: +### Basic comparison with default JSON output on stdout: ./spdx-diff reference.json new.json ### Full details with proprietary packages excluded: ./spdx-diff reference.json new.json --no-packages-proprietary -### Silent mode for CI/CD (JSON output only): - ./spdx-diff reference.json new.json --format json --output results.json +### Console output for CI/CD: + ./spdx-diff reference.json new.json + +### Console output human-readable: + ./spdx-diff reference.json new.json --human-readable -### Console output only (no JSON file): - ./spdx-diff reference.json new.json --format text +### Console and JSON output with JSON file generated: + ./spdx-diff reference.json new.json --json-output result.json ### Exclude on console PACKAGECONFIG differences: ./spdx-diff reference.json new.json --no-packageconfig -Console output example: +Human readable console output example: ``` Packages - Added: + libfoo: 2.0 From 0b34f875a5369d7671f538705f4b2285f97920ed Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 11:18:54 +0200 Subject: [PATCH 13/13] CHANGELOG: Update for JSON format and console output handling Signed-off-by: Christophe Guerreiro --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1b2e1..0e6a3f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,17 @@ boolean options, all defaulting to `True`: - `--show-packageconfig` to `--[no-]packageconfig`(default: --packageconfig) - `--ignore-proprietary` to `--[no-]packages-proprietary`(default: --packages-proprietary) +- Console output handling has been updated: + - **Structured JSON** output is emitted on **stdout** by default to facilitate seamless + integration of the tool into automated pipelines and scripting workflows. + - **Human-readable** output is now provided by using --human-readable argument which replace + the **Structured JSON** output + +- The JSON output file has been updated: + - The JSON output file is no longer generated by default. + - The `--output ` option have been renamed to `--json-output ` + - The `--json-output ` option must now be explicitly specified to enable JSON file generation. + ### Fixed ### Removed