Skip to content

Commit 1157082

Browse files
authored
Merge pull request #262 from blackducksoftware/varunkpedapati-patch-1
Add files via upload
2 parents aa20677 + 6e96217 commit 1157082

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
'''
2+
Created on Jan 22, 2024
3+
4+
@author: pedapati
5+
6+
Update component version info for BOM Components with Unknown Versions based on matched filename in a given project version
7+
8+
'''
9+
10+
from blackduck import Client
11+
12+
import requests
13+
import argparse
14+
import json
15+
import logging
16+
import sys
17+
import time
18+
from pprint import pprint
19+
20+
import urllib3
21+
import urllib.parse
22+
23+
NAME = 'update_component_version.py'
24+
VERSION = '2024-01-22'
25+
26+
print(f'{NAME} ({VERSION}). Copyright (c) 2023 Synopsys, Inc.')
27+
28+
29+
logging.basicConfig(
30+
level=logging.DEBUG,
31+
format="[%(asctime)s] {%(module)s:%(lineno)d} %(levelname)s - %(message)s"
32+
)
33+
34+
parser = argparse.ArgumentParser(sys.argv[0])
35+
parser.add_argument("-u", "--bd-url", help="Hub server URL e.g. https://your.blackduck.url")
36+
parser.add_argument("-t", "--token-file", help="File name of a file containing access token")
37+
parser.add_argument("-nv", '--no-verify', dest='verify', action='store_false', help="disable TLS certificate verification")
38+
parser.add_argument("project_name")
39+
parser.add_argument("version_name")
40+
41+
args = parser.parse_args()
42+
43+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
44+
logging.getLogger("requests").setLevel(logging.WARNING)
45+
logging.getLogger("urllib3").setLevel(logging.WARNING)
46+
logging.getLogger("blackduck").setLevel(logging.WARNING)
47+
48+
with open(args.token_file, 'r') as tf:
49+
access_token = tf.readline().strip()
50+
51+
bd = Client(base_url=args.bd_url, token=access_token, verify=args.verify)
52+
53+
params = {
54+
'q': [f"name:{args.project_name}"]
55+
}
56+
projects = [p for p in bd.get_resource('projects', params=params) if p['name'] == args.project_name]
57+
assert len(projects) == 1, f"There should be one, and only one project named {args.project_name}. We found {len(projects)}"
58+
project = projects[0]
59+
project_id = project["_meta"]["href"].split("/")[-1]
60+
print("Project ID: " + project_id)
61+
62+
params = {
63+
'q': [f"versionName:{args.version_name}"]
64+
}
65+
versions = [v for v in bd.get_resource('versions', project, params=params) if v['versionName'] == args.version_name]
66+
assert len(versions) == 1, f"There should be one, and only one version named {args.version_name}. We found {len(versions)}"
67+
version = versions[0]
68+
version_id = version["_meta"]["href"].split("/")[-1]
69+
print("Version ID: " + version_id)
70+
71+
logging.debug(f"Found {project['name']}:{version['versionName']}")
72+
73+
def update_bom_unknown_versions(bd, project_id, version_id):
74+
limit = 1000
75+
offset = 0
76+
paginated_url = f"{bd.base_url}/api/projects/{project_id}/versions/{version_id}/components?limit={limit}&offset={offset}&filter=unknownVersion:true"
77+
print("Looking for BOM Components with Unknown Versions: " + paginated_url)
78+
print()
79+
components_json = bd.session.get(paginated_url).json()
80+
total = str(components_json["totalCount"])
81+
print("Found " + total + " components with unknown versions")
82+
print()
83+
for component in components_json["items"]:
84+
comp_name =component["componentName"]
85+
print("Processing Component: " + comp_name)
86+
comp_url = component["component"]
87+
comp_bom_url = component["_meta"]["href"]
88+
matched_files_url = component["_meta"]["href"] + "/matched-files"
89+
matched_file_json = bd.session.get(matched_files_url).json()
90+
archivecontext = matched_file_json["items"][0]["filePath"]["archiveContext"]
91+
filename = matched_file_json["items"][0]["filePath"]["fileName"]
92+
## Extract Component Name and Version from archivecontext to do a KB lookup
93+
archive_strip = archivecontext.strip("/,!")
94+
archive_partition = archive_strip.rpartition("-")
95+
archive_final_list = archive_partition[0].rpartition("-")
96+
kb_file_lookup_name = archive_final_list[0]
97+
kb_comp_lookup_version = archive_final_list[2]
98+
print("Processing Component Version: " + kb_comp_lookup_version)
99+
## KB Lookup via Component Name
100+
components_url = bd.base_url + "/api/components/autocomplete"
101+
query = { "q": comp_name,
102+
"filter": "componentType:kb_component"
103+
}
104+
url = f"{components_url}?{urllib.parse.urlencode(query)}"
105+
headers = {'Accept': '*/*'}
106+
name_match = bd.session.get(url, headers=headers).json()
107+
# Filtering results for exact name match
108+
exact_name_match = [x for x in name_match['items'] if x['name']==comp_name]
109+
if len(exact_name_match) == 0 :
110+
logging.debug(f"Component {comp_name} is not found in the KB")
111+
return
112+
else:
113+
logging.debug(f"Component {comp_name} is found in the KB")
114+
if kb_comp_lookup_version:
115+
first_match_successful = False
116+
# second_match_successful = False
117+
for match in exact_name_match: # handling OSS components that share same name
118+
url = match['_meta']['href']+'/versions?q=versionName:' + kb_comp_lookup_version
119+
headers = {'Accept': 'application/vnd.blackducksoftware.summary-1+json'}
120+
# Producing version matches
121+
version_match = bd.session.get(url, headers=headers).json()
122+
if version_match['totalCount'] > 0:
123+
print(version_match["items"][0]["versionName"])
124+
print("Found version: " + kb_comp_lookup_version + " in the KB for component " + comp_name)
125+
print("Updating component version for " + comp_name + " to " + kb_comp_lookup_version )
126+
# component_url = version_match[]
127+
# print(version_match)
128+
component_version_url = version_match['items'][0]['_meta']['href']
129+
component_url = component_version_url[:component_version_url.index("versions")-1]
130+
# print(component_url)
131+
post_data = {"component": component_url, "componentVersion": component_version_url}
132+
headers = {'Accept': 'application/vnd.blackducksoftware.bill-of-materials-6+json', 'Content-Type': 'application/vnd.blackducksoftware.bill-of-materials-6+json'}
133+
response = bd.session.put(comp_bom_url, headers=headers, data=json.dumps(post_data))
134+
# print(response)
135+
if response.status_code == 200:
136+
message = f"{response}"
137+
print("Successfully updated " + comp_name + " with version " + kb_comp_lookup_version)
138+
else:
139+
message = f"{response.json()}"
140+
logging.debug(f"Updating BOM component {comp_name} {kb_comp_lookup_version} failed with: {message}")
141+
first_match_successful = True
142+
print("### Proceeding to next component")
143+
print()
144+
break
145+
else:
146+
print("No matching version " + kb_comp_lookup_version + " found for " + comp_name)
147+
if not first_match_successful:
148+
## Trying to locate component name using source archive name
149+
print("Proceeding to KB lookup via matched file name: " + kb_file_lookup_name)
150+
components_url = bd.base_url + "/api/components/autocomplete"
151+
query = { "q": kb_file_lookup_name,
152+
"filter": "componentType:kb_component"
153+
}
154+
url = f"{components_url}?{urllib.parse.urlencode(query)}"
155+
headers = {'Accept': '*/*'}
156+
name_match = bd.session.get(url, headers=headers).json()
157+
# Filtering results for exact name match
158+
exact_name_match = [x for x in name_match['items'] if x['name']==kb_file_lookup_name]
159+
if len(exact_name_match) == 0 :
160+
logging.debug(f"File Match KB Component {kb_file_lookup_name} is not found in the KB")
161+
print("### Proceeding to next component")
162+
print()
163+
continue
164+
else:
165+
logging.debug(f"File Match KB Component {kb_file_lookup_name} is found in the KB")
166+
if kb_comp_lookup_version:
167+
for match in exact_name_match: # handling OSS components that share same name
168+
url = match['_meta']['href']+'/versions?q=versionName:' + kb_comp_lookup_version
169+
headers = {'Accept': 'application/vnd.blackducksoftware.summary-1+json'}
170+
# Producing version matches
171+
version_match = bd.session.get(url, headers=headers).json()
172+
if version_match['totalCount'] > 0:
173+
print(version_match["items"][0]["versionName"])
174+
print("Found version: " + kb_comp_lookup_version + " in the KB for component " + kb_file_lookup_name)
175+
print("Updating component version for " + kb_file_lookup_name + " to " + kb_comp_lookup_version )
176+
# component_url = version_match[]
177+
# print(version_match)
178+
component_version_url = version_match['items'][0]['_meta']['href']
179+
component_url = component_version_url[:component_version_url.index("versions")-1]
180+
# print(component_url)
181+
post_data = {"component": component_url, "componentVersion": component_version_url}
182+
headers = {'Accept': 'application/vnd.blackducksoftware.bill-of-materials-6+json', 'Content-Type': 'application/vnd.blackducksoftware.bill-of-materials-6+json'}
183+
response = bd.session.put(comp_bom_url, headers=headers, data=json.dumps(post_data))
184+
# print(response)
185+
if response.status_code == 200:
186+
message = f"{response}"
187+
print("Successfully updated " + kb_file_lookup_name + " with version " + kb_comp_lookup_version)
188+
# second_match_successful = True
189+
else:
190+
message = f"{response.json()}"
191+
logging.debug(f"Updating BOM component {kb_file_lookup_name} {kb_comp_lookup_version} failed with: {message}")
192+
print("### Proceeding to next component")
193+
print()
194+
break
195+
else:
196+
print("No matching version " + kb_comp_lookup_version + " found for " + kb_file_lookup_name)
197+
print("### Proceeding to next component")
198+
print()
199+
200+
201+
202+
bom = update_bom_unknown_versions(bd, project_id, version_id)
203+
204+
205+

0 commit comments

Comments
 (0)