From fc699a0d75bf5de7b18b5380b565dbc0ee48822a Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Tue, 26 Dec 2023 08:44:37 -0600 Subject: [PATCH 1/3] Point core developers to SBOM devguide on errors --- Tools/build/generate_sbom.py | 53 ++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 93d0d8a3762df3..a0374c5cf8e312 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -82,6 +82,14 @@ def spdx_id(value: str) -> str: return re.sub(r"[^a-zA-Z0-9.\-]+", "-", value) +def error_if_not(value: bool, error_message: str) -> None: + """Prints an error if a value isn't true along with a link to the Dev Guide""" + if not value: + print(error_message) + print("See 'https://devguide.python.org/developer-workflow/sbom' for more information.") + sys.exit(1) + + def filter_gitignored_paths(paths: list[str]) -> list[str]: """ Filter out paths excluded by the gitignore file. @@ -206,22 +214,47 @@ def main() -> None: discover_pip_sbom_package(sbom_data) # Ensure all packages in this tool are represented also in the SBOM file. - assert {package["name"] for package in sbom_data["packages"]} == set(PACKAGE_TO_FILES) + error_if_not( + {package["name"] for package in sbom_data["packages"]} == set(PACKAGE_TO_FILES), + "Packages defined in SBOM tool don't match those defined in SBOM file.", + ) # Make a bunch of assertions about the SBOM data to ensure it's consistent. for package in sbom_data["packages"]: - # Properties and ID must be properly formed. - assert set(package.keys()) == REQUIRED_PROPERTIES_PACKAGE - assert package["SPDXID"] == spdx_id(f"SPDXRef-PACKAGE-{package['name']}") + error_if_not( + "name" in package, + "Package is missing the 'name' field" + ) + error_if_not( + set(package.keys()) == REQUIRED_PROPERTIES_PACKAGE, + f"Package '{package['name']}' is missing required fields", + ) + error_if_not( + package["SPDXID"] == spdx_id(f"SPDXRef-PACKAGE-{package['name']}"), + f"Package '{package['name']}' has a malformed SPDXID", + ) # Version must be in the download and external references. version = package["versionInfo"] - assert version in package["downloadLocation"] - assert all(version in ref["referenceLocator"] for ref in package["externalRefs"]) + error_if_not( + version in package["downloadLocation"], + f"Version '{version}' for package '{package['name']} not in 'downloadLocation' field", + ) + error_if_not( + all(version in ref["referenceLocator"] for ref in package["externalRefs"]), + ( + f"Version '{version}' for package '{package['name']} not in " + f"all 'externalRefs[].referenceLocator' fields" + ), + ) # License must be on the approved list for SPDX. - assert package["licenseConcluded"] in ALLOWED_LICENSE_EXPRESSIONS, package["licenseConcluded"] + license_concluded = package["licenseConcluded"] + error_if_not( + license_concluded in ALLOWED_LICENSE_EXPRESSIONS, + f"License identifier '{license_concluded}' not in SBOM tool allowlist" + ) # Regenerate file information from current data. sbom_files = [] @@ -232,11 +265,13 @@ def main() -> None: package_spdx_id = spdx_id(f"SPDXRef-PACKAGE-{name}") exclude = files.exclude or () for include in sorted(files.include): - # Find all the paths and then filter them through .gitignore. paths = glob.glob(include, root_dir=CPYTHON_ROOT_DIR, recursive=True) paths = filter_gitignored_paths(paths) - assert paths, include # Make sure that every value returns something! + error_if_not( + len(paths) > 0, + f"No valid paths found at path '{include}' for package '{name}", + ) for path in paths: # Skip directories and excluded files From ba6192fb34199078c2e3751bad7f192c1d8e3547 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Wed, 10 Jan 2024 11:32:56 -0600 Subject: [PATCH 2/3] Flip error_if() to be easier to read --- Tools/build/generate_sbom.py | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index a0374c5cf8e312..7cb95ad1ce4842 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -82,9 +82,9 @@ def spdx_id(value: str) -> str: return re.sub(r"[^a-zA-Z0-9.\-]+", "-", value) -def error_if_not(value: bool, error_message: str) -> None: - """Prints an error if a value isn't true along with a link to the Dev Guide""" - if not value: +def error_if(value: bool, error_message: str) -> None: + """Prints an error if a comparison fails along with a link to the Dev Guide""" + if value: print(error_message) print("See 'https://devguide.python.org/developer-workflow/sbom' for more information.") sys.exit(1) @@ -214,35 +214,35 @@ def main() -> None: discover_pip_sbom_package(sbom_data) # Ensure all packages in this tool are represented also in the SBOM file. - error_if_not( - {package["name"] for package in sbom_data["packages"]} == set(PACKAGE_TO_FILES), + error_if( + {package["name"] for package in sbom_data["packages"]} != set(PACKAGE_TO_FILES), "Packages defined in SBOM tool don't match those defined in SBOM file.", ) # Make a bunch of assertions about the SBOM data to ensure it's consistent. for package in sbom_data["packages"]: # Properties and ID must be properly formed. - error_if_not( - "name" in package, + error_if( + "name" not in package, "Package is missing the 'name' field" ) - error_if_not( - set(package.keys()) == REQUIRED_PROPERTIES_PACKAGE, + error_if( + set(package.keys()) != REQUIRED_PROPERTIES_PACKAGE, f"Package '{package['name']}' is missing required fields", ) - error_if_not( - package["SPDXID"] == spdx_id(f"SPDXRef-PACKAGE-{package['name']}"), + error_if( + package["SPDXID"] != spdx_id(f"SPDXRef-PACKAGE-{package['name']}"), f"Package '{package['name']}' has a malformed SPDXID", ) # Version must be in the download and external references. version = package["versionInfo"] - error_if_not( - version in package["downloadLocation"], + error_if( + version not in package["downloadLocation"], f"Version '{version}' for package '{package['name']} not in 'downloadLocation' field", ) - error_if_not( - all(version in ref["referenceLocator"] for ref in package["externalRefs"]), + error_if( + any(version not in ref["referenceLocator"] for ref in package["externalRefs"]), ( f"Version '{version}' for package '{package['name']} not in " f"all 'externalRefs[].referenceLocator' fields" @@ -251,8 +251,8 @@ def main() -> None: # License must be on the approved list for SPDX. license_concluded = package["licenseConcluded"] - error_if_not( - license_concluded in ALLOWED_LICENSE_EXPRESSIONS, + error_if( + license_concluded not in ALLOWED_LICENSE_EXPRESSIONS, f"License identifier '{license_concluded}' not in SBOM tool allowlist" ) @@ -268,8 +268,8 @@ def main() -> None: # Find all the paths and then filter them through .gitignore. paths = glob.glob(include, root_dir=CPYTHON_ROOT_DIR, recursive=True) paths = filter_gitignored_paths(paths) - error_if_not( - len(paths) > 0, + error_if( + len(paths) == 0, f"No valid paths found at path '{include}' for package '{name}", ) From c81fd8d05f5b42cc6f6ad5976ea9edc2e1f376ac Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Wed, 10 Jan 2024 13:02:42 -0600 Subject: [PATCH 3/3] Use devguide Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Tools/build/generate_sbom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 7cb95ad1ce4842..282ee20cc402b0 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -83,7 +83,7 @@ def spdx_id(value: str) -> str: def error_if(value: bool, error_message: str) -> None: - """Prints an error if a comparison fails along with a link to the Dev Guide""" + """Prints an error if a comparison fails along with a link to the devguide""" if value: print(error_message) print("See 'https://devguide.python.org/developer-workflow/sbom' for more information.")