From 09f15daa898743ce41ba1eeb54d2fb1dd7868d59 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 14:12:30 +0200 Subject: [PATCH 01/14] 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/14] 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 e64d7f64a92c1eb09308d8e73be4ffc0263554a9 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 15:09:27 +0200 Subject: [PATCH 03/14] 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..d5ebc24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ All notable changes to this project will be documented in this file. ### Removed +- Refocus 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, follwing arguments are removed: + - `--full` + - `--summary` + ## [1.0.1] - 2026-02-12 ### Added From 82ff054add14c8ce284e8a884e4f24f0a074fff6 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 17:27:22 +0200 Subject: [PATCH 04/14] 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 0c544d27ce7b6f4f37f158e01d8c6e6d9f4c23c0 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 16:15:25 +0200 Subject: [PATCH 05/14] 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 4bce4d98b2e886a28dc2458b58383c7db2c95d74 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 13:44:07 +0200 Subject: [PATCH 06/14] 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..7340374 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 +### Show on console no PACKAGECONFIG differences: + ./spdx-diff reference.json new.json --no-packageconfig Console output example: ``` From a5dc964cee9fe21d9d0f91d6e88d3d18d2bdb6b3 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Tue, 7 Apr 2026 17:42:06 +0200 Subject: [PATCH 07/14] CHANGELOG: Update for parameters renamed --- CHANGELOG.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5ebc24..35311db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,18 +8,25 @@ All notable changes to this project will be documented in this file. ### Changed +- The following filtering category arguments have been renamed and converted to +boolean options, all defaulting to `True`: + - `--show-packages` to `--packages`(default) and `--no-packages` + - `--show-config` to `--kernel-config`(default) and `--no-kernel-config` + - `--show-packageconfig` to `--packageconfig`(default) and `--no-packageconfig` + - `--ignore-proprietary` to `--packages-proprietary`(default) and `--no-packages-proprietary` + ### Fixed ### Removed - Refocus 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, follwing arguments are removed: - - `--full` - - `--summary` + - `--show-added` + - `--show-changed` + - `--show-removed` + - The default behavior is to show the full spdx diff, follwing arguments are removed: + - `--full` + - `--summary` ## [1.0.1] - 2026-02-12 From a2064abe219cb67f570c0ae8e66762c9575645cb Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 09:33:53 +0200 Subject: [PATCH 08/14] spdx-diff: cli: Add quiet argument Added the following new argument: - `-q`,`--quiet`: enables the quiet mode. Decrease verbosity to ERROR only. Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 76df455..efe9f2a 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -412,6 +412,12 @@ def main() -> None: default=0, help="Increase verbosity (-v for INFO, -vv for DEBUG)", ) + parser.add_argument( + "-q", + "--quiet", + action="store_true", + help="", + ) parser.add_argument( "reference", type=path_is_file, @@ -469,7 +475,9 @@ def main() -> None: args = parser.parse_args() log_level = logging.WARNING - if args.verbose >= 2: + if args.quiet: + log_level = logging.ERROR + elif args.verbose >= 2: log_level = logging.DEBUG elif args.verbose == 1: log_level = logging.INFO From 477a93adf53ec5c5004c9c43fb9951ad6112b8d6 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 09:36:28 +0200 Subject: [PATCH 09/14] CHANGELOG: add quiet command Signed-off-by: Christophe Guerreiro --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35311db..5bc45d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Added +- Added quiet argument for decreasing verbosity to ERROR only. ### Changed - - The following filtering category arguments have been renamed and converted to boolean options, all defaulting to `True`: - `--show-packages` to `--packages`(default) and `--no-packages` From ec50712bc5e15e6c5ea6934e3ef4d86828585b51 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 09:55:04 +0200 Subject: [PATCH 10/14] spdx-diff: cli: Redirect print_diff text output to stderr This change ensures that textual output from print_diff are properly separated from standard output, improving compatibility with tools and pipelines that rely on stdout for structured data or program output. Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index efe9f2a..7a597b0 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -5,8 +5,10 @@ import logging import pathlib import re +import sys from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction from collections import defaultdict +from contextlib import redirect_stdout from datetime import datetime, timezone from typing import Any @@ -509,24 +511,26 @@ 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 on stderr + with redirect_stdout(sys.stderr): + 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 __name__ == "__main__": From c51962fbebea1bbdce0c05ede9cf1609dd91863e Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 10:54:51 +0200 Subject: [PATCH 11/14] spdx-diff: cli: Change output format to JSON and enfore 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 also emitted in JSON format to ensure consistency and to simplify integration with automated pipelines and tooling. Signed-off-by: Christophe Guerreiro --- src/spdx_diff/cli.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/spdx_diff/cli.py b/src/spdx_diff/cli.py index 7a597b0..a274b3b 100644 --- a/src/spdx_diff/cli.py +++ b/src/spdx_diff/cli.py @@ -9,7 +9,6 @@ from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction from collections import defaultdict from contextlib import redirect_stdout -from datetime import datetime, timezone from typing import Any from . import __version__ @@ -386,8 +385,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: @@ -430,21 +436,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", + "--json-output", "-o", 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)", ) # Output filtering category options @@ -511,8 +509,6 @@ def main() -> None: pcfg_diff[2], ) - if args.format in ["json", "both"]: - write_diff_to_json(pkg_diff, cfg_diff, pcfg_light_diff, args.output) # Print human readable information on stderr with redirect_stdout(sys.stderr): if show_packages: @@ -531,6 +527,7 @@ def main() -> None: ) + write_diff_to_json(pkg_diff, cfg_diff, pcfg_light_diff, args.json_output) if __name__ == "__main__": From 9a0c71f89852f034531e0ae45fa212d3b8ac94b1 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 16:20:13 +0200 Subject: [PATCH 12/14] 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 307c4153c711088191a0c86df2f2e631cbec23c8 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 13:49:37 +0200 Subject: [PATCH 13/14] doc: Update INSTALL.md and README.md for console output and JSON handling Signed-off-by: Christophe Guerreiro --- INSTALL.md | 6 +++--- README.md | 25 ++++++++++++------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f6ed6d7..d190b91 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -46,11 +46,11 @@ After installation: # Show help spdx-diff --help -# Compare two SPDX files +# Compare two SPDX files, human-readable output on stderr and JSON output on stdout spdx-diff reference.json new.json -# JSON output for automation -spdx-diff reference.json new.json --format json --output results.json +# 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 7340374..5b9b829 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. + +- **stderr** is used for human-readable (text) output intended for debugging. +- **stdout** always emits structured **JSON output**, making it suitable for consumption by scripts and CI/CD pipelines. +- When a JSON filename parameter is provided, the JSON result is also written to the specified file. Usage ----- @@ -21,12 +25,7 @@ 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. Text output filtering - category : - `--[no-]packages`: show|hide package differences. @@ -128,22 +127,22 @@ The script uses Python's logging module: Examples -------- -### Basic comparison with both console and JSON output: +### Basic comparison with both console(stderr) and JSON(stdout) output: ./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 --quiet -### 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 --quiet --json-output result.json ### Show on console no PACKAGECONFIG differences: ./spdx-diff reference.json new.json --no-packageconfig -Console output example: +Console output(stderr) example: ``` Packages - Added: + libfoo: 2.0 From 212a248d39f5bc9a97250e7bfa81bbb2a09faa65 Mon Sep 17 00:00:00 2001 From: Christophe Guerreiro Date: Wed, 8 Apr 2026 11:18:54 +0200 Subject: [PATCH 14/14] CHANGELOG: Update for JSON format and console output handling Signed-off-by: Christophe Guerreiro --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc45d1..d8e41f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,16 @@ boolean options, all defaulting to `True`: - `--show-packageconfig` to `--packageconfig`(default) and `--no-packageconfig` - `--ignore-proprietary` to `--packages-proprietary`(default) and `--no-packages-proprietary` +- Console output handling has been updated: + - **Human-readable** output is now redirected to **stderr** + - **Structured JSON** output is emitted on **stdout** to facilitate seamless + integration of the tool into automated pipelines and scripting workflows. + +- 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