Skip to content

Commit 2ee751e

Browse files
committed
feat(project CreateBom): add info about clearing results
1 parent 788aa7e commit 2ee751e

File tree

4 files changed

+79
-23
lines changed

4 files changed

+79
-23
lines changed

capycli/common/capycli_bom_support.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,11 @@ class CaPyCliBom():
631631
SOURCE_FILE_COMMENT = "source archive (local copy)"
632632
BINARY_URL_COMMENT = "binary (download location)"
633633
BINARY_FILE_COMMENT = "relativePath"
634+
# machine-readable XML description of licensing situation of a component
635+
# see https://github.com/sw360/clipython for more information
636+
CLI_FILE_COMMENT = "component license information (local copy)"
637+
# human-readable description of licensing situation and obligations
638+
CRT_FILE_COMMENT = "clearing report (local copy)"
634639

635640
@classmethod
636641
def read_sbom(cls, inputfile: str) -> Bom:

capycli/common/script_base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ def release_web_url(self, release_id) -> str:
139139
return (self.sw360_url + "group/guest/components/-/component/release/detailRelease/"
140140
+ release_id)
141141

142+
def attachment_api_url(self, release_id, attachment_id) -> str:
143+
"""Returns the REST API URL for an attachment."""
144+
return (self.sw360_url + "resource/api/releases/" + release_id
145+
+ "/attachments/" + attachment_id)
146+
142147
def find_project(self, name: str, version: str, show_results: bool = False) -> str:
143148
"""Find the project with the matching name and version on SW360"""
144149
print_text(" Searching for project...")

capycli/project/create_bom.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@
2727
class CreateBom(capycli.common.script_base.ScriptBase):
2828
"""Create a SBOM for a project on SW360."""
2929

30+
comments = {
31+
"SOURCE": CaPyCliBom.SOURCE_FILE_COMMENT,
32+
"SOURCE_SELF": CaPyCliBom.SOURCE_FILE_COMMENT,
33+
"BINARY": CaPyCliBom.BINARY_FILE_COMMENT,
34+
"BINARY_SELF": CaPyCliBom.BINARY_FILE_COMMENT,
35+
"COMPONENT_LICENSE_INFO_XML": CaPyCliBom.CLI_FILE_COMMENT,
36+
"CLEARING_REPORT": CaPyCliBom.CRT_FILE_COMMENT
37+
}
38+
3039
def get_external_id(self, name: str, release_details: dict):
3140
"""Returns the external id with the given name or None."""
3241
if "externalIds" not in release_details:
@@ -51,6 +60,7 @@ def create_project_bom(self, project) -> list:
5160
for release in releases:
5261
print_text(" ", release["name"], release["version"])
5362
href = release["_links"]["self"]["href"]
63+
sw360_id = self.client.get_id_from_href(href)
5464

5565
try:
5666
release_details = self.client.get_release_by_url(href)
@@ -85,13 +95,21 @@ def create_project_bom(self, project) -> list:
8595
CycloneDxSupport.set_ext_ref(rel_item, ExternalReferenceType.VCS, comment=None,
8696
value=release_details["repository"]["url"])
8797

88-
for at_type, comment in (("SOURCE", CaPyCliBom.SOURCE_FILE_COMMENT),
89-
("BINARY", CaPyCliBom.BINARY_FILE_COMMENT)):
90-
attachments = self.get_release_attachments(release_details, (at_type, at_type + "_SELF"))
91-
for attachment in attachments:
92-
CycloneDxSupport.set_ext_ref(rel_item, ExternalReferenceType.DISTRIBUTION,
93-
comment, attachment["filename"],
94-
HashAlgorithm.SHA_1, attachment.get("sha1"))
98+
attachments = self.get_release_attachments(release_details)
99+
for attachment in attachments:
100+
at_type = attachment["attachmentType"]
101+
if at_type not in self.comments:
102+
continue
103+
comment = self.comments[at_type]
104+
if at_type in ("SOURCE", "SOURCE_SELF", "BINARY", "BINARY_SELF"):
105+
ext_ref_type = ExternalReferenceType.DISTRIBUTION
106+
else:
107+
ext_ref_type = ExternalReferenceType.OTHER
108+
comment += (", sw360Id: "
109+
+ self.client.get_id_from_href(attachment["_links"]["self"]["href"]))
110+
CycloneDxSupport.set_ext_ref(rel_item, ext_ref_type,
111+
comment, attachment["filename"],
112+
HashAlgorithm.SHA_1, attachment.get("sha1"))
95113

96114
except sw360.SW360Error as swex:
97115
print_red(" ERROR: unable to access project:" + repr(swex))
@@ -101,7 +119,6 @@ def create_project_bom(self, project) -> list:
101119
if state:
102120
CycloneDxSupport.set_property(rel_item, CycloneDxSupport.CDX_PROP_PROJ_STATE, state)
103121

104-
sw360_id = self.client.get_id_from_href(href)
105122
CycloneDxSupport.set_property(rel_item, CycloneDxSupport.CDX_PROP_SW360ID, sw360_id)
106123

107124
CycloneDxSupport.set_property(

tests/test_create_bom.py

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ def test_project_by_id(self):
169169
}
170170
}
171171
})
172+
release["_embedded"]["sw360:attachments"].append({
173+
"filename": "clipython-1.3.0.docx",
174+
"sha1": "f0d8f2ddd017bdeaecbaec72ff76a6c0a045ec66",
175+
"attachmentType": "CLEARING_REPORT",
176+
"_links": {
177+
"self": {
178+
"href": "https://my.server.com/resource/api/attachments/r002a003"
179+
}
180+
}
181+
})
182+
172183
responses.add(
173184
responses.GET,
174185
url=self.MYURL + "resource/api/releases/r002",
@@ -182,21 +193,39 @@ def test_project_by_id(self):
182193
cx_comp = cdx_bom.components[0]
183194
self.assertEqual(cx_comp.purl, release["externalIds"]["package-url"])
184195

185-
ext_refs_src_url = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_URL_COMMENT]
186-
self.assertEqual(len(ext_refs_src_url), 1)
187-
self.assertEqual(ext_refs_src_url[0].url, release["sourceCodeDownloadurl"])
188-
self.assertEqual(ext_refs_src_url[0].type, ExternalReferenceType.DISTRIBUTION)
189-
190-
ext_refs_src_file = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_FILE_COMMENT]
191-
self.assertEqual(len(ext_refs_src_file), 2)
192-
self.assertEqual(ext_refs_src_file[0].url, release["_embedded"]["sw360:attachments"][0]["filename"])
193-
self.assertEqual(ext_refs_src_file[0].type, ExternalReferenceType.DISTRIBUTION)
194-
self.assertEqual(ext_refs_src_file[0].hashes[0].alg, "SHA-1")
195-
self.assertEqual(ext_refs_src_file[0].hashes[0].content, release["_embedded"]["sw360:attachments"][0]["sha1"])
196-
197-
ext_refs_vcs = [e for e in cx_comp.external_references if e.type == ExternalReferenceType.VCS]
198-
self.assertEqual(len(ext_refs_vcs), 1)
199-
self.assertEqual(ext_refs_vcs[0].url, release["repository"]["url"])
196+
ext_refs = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_URL_COMMENT]
197+
self.assertEqual(len(ext_refs), 1)
198+
self.assertEqual(ext_refs[0].url, release["sourceCodeDownloadurl"])
199+
self.assertEqual(ext_refs[0].type, ExternalReferenceType.DISTRIBUTION)
200+
201+
ext_refs = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_FILE_COMMENT]
202+
self.assertEqual(len(ext_refs), 2)
203+
self.assertEqual(ext_refs[0].url, release["_embedded"]["sw360:attachments"][0]["filename"])
204+
self.assertEqual(ext_refs[0].type, ExternalReferenceType.DISTRIBUTION)
205+
self.assertEqual(ext_refs[0].hashes[0].alg, "SHA-1")
206+
self.assertEqual(ext_refs[0].hashes[0].content, release["_embedded"]["sw360:attachments"][0]["sha1"])
207+
208+
ext_refs = [e for e in cx_comp.external_references
209+
if e.comment and e.comment.startswith(CaPyCliBom.CLI_FILE_COMMENT)]
210+
self.assertEqual(len(ext_refs), 1)
211+
self.assertEqual(ext_refs[0].url, release["_embedded"]["sw360:attachments"][1]["filename"])
212+
self.assertEqual(ext_refs[0].type, ExternalReferenceType.OTHER)
213+
self.assertEqual(ext_refs[0].comment, CaPyCliBom.CLI_FILE_COMMENT + ", sw360Id: r002a002")
214+
self.assertEqual(ext_refs[0].hashes[0].alg, "SHA-1")
215+
self.assertEqual(ext_refs[0].hashes[0].content, release["_embedded"]["sw360:attachments"][1]["sha1"])
216+
217+
ext_refs = [e for e in cx_comp.external_references
218+
if e.comment and e.comment.startswith(CaPyCliBom.CRT_FILE_COMMENT)]
219+
self.assertEqual(len(ext_refs), 1)
220+
self.assertEqual(ext_refs[0].url, release["_embedded"]["sw360:attachments"][3]["filename"])
221+
self.assertEqual(ext_refs[0].comment, CaPyCliBom.CRT_FILE_COMMENT + ", sw360Id: r002a003")
222+
self.assertEqual(ext_refs[0].type, ExternalReferenceType.OTHER)
223+
self.assertEqual(ext_refs[0].hashes[0].alg, "SHA-1")
224+
self.assertEqual(ext_refs[0].hashes[0].content, release["_embedded"]["sw360:attachments"][3]["sha1"])
225+
226+
ext_refs = [e for e in cx_comp.external_references if e.type == ExternalReferenceType.VCS]
227+
self.assertEqual(len(ext_refs), 1)
228+
self.assertEqual(ext_refs[0].url, release["repository"]["url"])
200229

201230
self.assertEqual(cdx_bom.metadata.component.name, project["name"])
202231
self.assertEqual(cdx_bom.metadata.component.version, project["version"])

0 commit comments

Comments
 (0)