-
Notifications
You must be signed in to change notification settings - Fork 1
Improve source files downloading with local package detection #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| # SPDX-License-Identifier: MIT | ||
|
|
||
| import argparse | ||
| import glob | ||
| import textwrap | ||
| import sys | ||
| import os | ||
|
|
@@ -107,11 +108,30 @@ def pass1_map_bom(bom, sw360_url, sw360_token): | |
| return result | ||
|
|
||
|
|
||
| def pass3_download_sources(bom): | ||
| def pass3_download_sources(bom, pkg_dir): | ||
| for item in bom.components: | ||
| if not (get_cdx(item, "MapResult") == MapResult.NO_MATCH | ||
| or (MapBom.is_good_match(get_cdx(item, "MapResult")) | ||
| and get_cdx(item, "Sw360SourceFileCheck") != "passed")): | ||
| map_result = get_cdx(item, "MapResult") | ||
| is_missing_from_sw360 = map_result == MapResult.NO_MATCH | ||
| is_in_sw360_but_unverified = ( | ||
| MapBom.is_good_match(map_result) | ||
| and get_cdx(item, "Sw360SourceFileCheck") != "passed" | ||
| ) | ||
| needs_download = is_missing_from_sw360 or is_in_sw360_but_unverified | ||
|
|
||
| if needs_download and pkg_dir: | ||
| package_name = item.purl.name | ||
| version = item.version.removesuffix(".debian") | ||
| version = version.split(":", 1)[-1] # Remove epoch if present | ||
| pattern = f"{package_name}_{version}*" | ||
| matches = glob.glob(os.path.join(pkg_dir, pattern)) | ||
| if matches: | ||
| set_cdx(item, "SourceFileComment", "sources locally available") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The SourceFileComment is a description used in SW360 for the uploaded attachment. main() abuses it to understand if the source file exists, but it's not a flag. |
||
| print( | ||
| f"Found local source for {item.name} {item.version}: " | ||
| f"{os.path.basename(matches[0])}" | ||
| ) | ||
|
|
||
| if not needs_download: | ||
| set_cdx(item, "SourceFileDownload", "skip") | ||
| return bom | ||
|
|
||
|
|
@@ -352,7 +372,7 @@ def main(): | |
| print("== Pass 3: Download missing and unchecked sources ==") | ||
| print() | ||
|
|
||
| bom = pass3_download_sources(bom) | ||
| bom = pass3_download_sources(bom, args.sources) | ||
| outputbom = write_bom(bom, filename+"-3-download"+extension) | ||
| missing_source_count = len( | ||
| [item for item in bom.components | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -171,6 +171,8 @@ def main(): | |
|
|
||
| bom = lst_to_sbom(format=args.format, package_list=args.package_list) | ||
|
|
||
| if os.path.exists(args.output_file): | ||
| os.remove(args.output_file) | ||
|
Comment on lines
+174
to
+175
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First of all, this might be a topic for a separate PR or issue. But in general, what's the goal of this change? I don't think silently overwriting an existing file is a good idea. Perhaps we want a better error message here to avoid a Traceback output? |
||
| JsonV1Dot6(bom=bom).output_to_file(args.output_file, indent=2) | ||
| print(f"SBOM written to {args.output_file}") | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # SPDX-FileCopyrightText: 2026 Siemens | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| import os | ||
| import tempfile | ||
|
|
||
| from cyclonedx.model.bom import Bom | ||
| from cyclonedx.model.component import Component, ComponentType | ||
| from packageurl import PackageURL | ||
|
|
||
| from capywfa.capywfa import pass3_download_sources, MapResult | ||
| from capywfa.cdx_support import set_cdx, get_cdx | ||
|
|
||
|
|
||
| def create_test_component(name, version, map_result=None, source_check=None): | ||
| """Helper function to create a test component with CDX properties.""" | ||
| component = Component( | ||
| name=name, | ||
| type=ComponentType.LIBRARY, | ||
| version=version, | ||
| purl=PackageURL("deb", "debian", name, version, {"arch": "source"}) | ||
| ) | ||
| if map_result is not None: | ||
| set_cdx(component, "MapResult", map_result) | ||
| if source_check is not None: | ||
| set_cdx(component, "Sw360SourceFileCheck", source_check) | ||
| return component | ||
|
|
||
|
|
||
| def test_pass3_download_sources_no_pkg_dir(): | ||
| """Test that pass3_download_sources works when no package directory is provided.""" | ||
| bom = Bom(components=[ | ||
| create_test_component("testpkg", "1.0-1", MapResult.NO_MATCH) | ||
| ]) | ||
| result = pass3_download_sources(bom, None) | ||
| assert result == bom | ||
| # Should not set SourceFileComment when pkg_dir is None | ||
| assert not get_cdx(bom.components[0], "SourceFileComment") | ||
|
|
||
|
|
||
| def test_pass3_download_sources_local_package_found(): | ||
| """Test that local packages are detected and marked as available.""" | ||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| test_file = os.path.join(tmpdir, "testpkg_1.0-1.dsc") | ||
| with open(test_file, "w") as f: | ||
| f.write("test") | ||
|
|
||
| bom = Bom(components=[ | ||
| create_test_component("testpkg", "1.0-1", MapResult.NO_MATCH) | ||
| ]) | ||
|
|
||
| result = pass3_download_sources(bom, tmpdir) | ||
|
|
||
| assert get_cdx(result.components[0], "SourceFileComment") == "sources locally available" | ||
|
|
||
|
|
||
| def test_pass3_download_sources_local_package_not_found(): | ||
| """Test that packages are not marked when local files don't exist.""" | ||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| bom = Bom(components=[ | ||
| create_test_component("testpkg", "1.0-1", MapResult.NO_MATCH) | ||
| ]) | ||
|
|
||
| result = pass3_download_sources(bom, tmpdir) | ||
|
|
||
| # Should not set SourceFileComment when no matching file found | ||
| assert not get_cdx(result.components[0], "SourceFileComment") | ||
|
|
||
|
|
||
| def test_pass3_download_sources_version_with_epoch(): | ||
| """Test that version with epoch is handled correctly.""" | ||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| test_file = os.path.join(tmpdir, "testpkg_1.0-1.dsc") | ||
| with open(test_file, "w") as f: | ||
| f.write("test") | ||
|
|
||
| # Component has epoch in version | ||
| bom = Bom(components=[ | ||
| create_test_component("testpkg", "2:1.0-1", MapResult.NO_MATCH) | ||
| ]) | ||
|
|
||
| result = pass3_download_sources(bom, tmpdir) | ||
| assert get_cdx(result.components[0], "SourceFileComment") == "sources locally available" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get your idea to make this code more readable, but NO_MATCH doesn't necessarily mean it's missing, but only we didn't find it. So better use something like is_not_found or is_no_match 😉