Skip to content

Commit 502676b

Browse files
A program that pulls license terms (including FulfillmentRequired and Fulfilled) for each of the licenses attached to each of the components in a specified project version.
1 parent 96b4589 commit 502676b

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import argparse
2+
import csv
3+
import glob
4+
import logging
5+
import os
6+
import shutil
7+
import requests
8+
import sys
9+
import time
10+
import timeit
11+
12+
import pandas
13+
from pandas.errors import EmptyDataError
14+
15+
from blackduck.HubRestApi import HubInstance
16+
17+
parser = argparse.ArgumentParser("A program that pulls license terms (including FulfillmentRequired and Fulfilled) for each of the licenses attached to each of the "
18+
"components in a specified project version ")
19+
parser.add_argument("project")
20+
parser.add_argument("version")
21+
parser.add_argument('-r', '--refresh', action='store_true',
22+
help='delete existing reports in the results directory and regenerate')
23+
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='turn on DEBUG logging')
24+
25+
args = parser.parse_args()
26+
27+
28+
def set_logging_level(log_level):
29+
logging.basicConfig(stream=sys.stderr, level=log_level)
30+
31+
32+
if args.verbose:
33+
set_logging_level(logging.DEBUG)
34+
else:
35+
set_logging_level(logging.INFO)
36+
37+
projname = args.project
38+
timestamp = time.strftime('%m_%d_%Y_%H_%M')
39+
file_out = (projname + '_' + "Consolidated_license_report-" + timestamp)
40+
file_out = (file_out + ".csv")
41+
hub = HubInstance()
42+
rootDir = os.getcwd()
43+
44+
45+
def do_refresh(dir_name):
46+
temp_dir = os.path.join(rootDir, dir_name)
47+
print("tempDir=%s" % temp_dir)
48+
for fileName in os.listdir(temp_dir):
49+
print("Removing stale files %s" % fileName)
50+
os.remove(os.path.join(temp_dir, fileName))
51+
52+
53+
def check_dirs():
54+
os.chdir(rootDir)
55+
if not os.path.isdir('./temp'):
56+
os.makedirs('./temp')
57+
print('made temp directory')
58+
elif len(os.listdir('./temp')) != 0:
59+
do_refresh('temp')
60+
else:
61+
print('temp directory already exists')
62+
63+
if not os.path.isdir('./license_terms_results'):
64+
os.makedirs('./license_terms_results')
65+
print('made license_terms_results directory')
66+
elif args.refresh and len(os.listdir('./license_terms_results')) != 0:
67+
print('refreshing license_terms_results')
68+
do_refresh('license_terms_results')
69+
else:
70+
print('license_terms_results directory already exists')
71+
72+
73+
def get_header():
74+
return ["Component Name", "Component Version", "Name", "Description", "Responsibility",
75+
"Deactivated", "Deprecated", "FulfillmentRequired", "Fulfilled"]
76+
77+
78+
def get_license_terms(component_licenses):
79+
license_terms_json = []
80+
try:
81+
license_name_key = [*component_licenses.keys()][0]
82+
license_terms_links = [cl for cl in component_licenses[license_name_key]['license_info']['_meta']['links']
83+
if cl.get("rel") == "bom-component-license-terms"]
84+
url = license_terms_links[0]['href']
85+
except (KeyError, IndexError) as err:
86+
logging.debug("no license name for:{}, with {}, writing an empty field ".format(component_licenses, err))
87+
return license_terms_json
88+
headers = {
89+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
90+
'application/signed-exchange;v=b3;q=0.9'}
91+
response = hub.execute_get(url, custom_headers=headers)
92+
try:
93+
if response.status_code in [200, 201]:
94+
license_terms_json = response.json()
95+
else:
96+
response.raise_for_status()
97+
return license_terms_json
98+
except requests.exceptions.HTTPError as err:
99+
logging.debug("no license terms for:{}, with {}, writing an empty field ".format(license_name_key, err))
100+
return license_terms_json
101+
102+
103+
def genreport():
104+
project = hub.get_project_by_name(args.project)
105+
version = hub.get_version_by_name(project, args.version)
106+
bom_components = hub.get_version_components(version)
107+
print("Component count returned for {} {} = {} ".format(args.project, args.version,
108+
bom_components['totalCount']))
109+
all_licenses = dict()
110+
curdir = os.getcwd()
111+
tempdir = os.path.join(curdir, 'temp')
112+
os.chdir(tempdir)
113+
with open(file_out, 'a', newline='') as f:
114+
writer = csv.writer(f)
115+
first_file = True
116+
for bom_component in bom_components['items']:
117+
# Retrieve the licenses and license text for this bom component and insert the license info along with
118+
# the bom component info into the all_licenses dictionary
119+
component_licenses = hub.get_license_info_for_bom_component(bom_component)
120+
component_name = bom_component['componentName']
121+
component_version_name = bom_component['componentVersionName']
122+
name_version_key = "{}, {}".format(component_name, component_version_name)
123+
license_terms = get_license_terms(component_licenses)
124+
125+
all_licenses.update({name_version_key: {
126+
"component_info": bom_component,
127+
"component_licenses": component_licenses,
128+
"license_terms": license_terms
129+
}
130+
})
131+
if first_file:
132+
header = get_header()
133+
writer.writerow(header)
134+
first_file = False
135+
136+
for lt in all_licenses[name_version_key]['license_terms']:
137+
row_list = [component_name, component_version_name, lt['name'], lt['description'], lt['responsibility'],
138+
lt['deactivated'], lt['deprecated'], lt['fulfillmentRequired'], lt['fulfilled']]
139+
writer.writerow(row_list)
140+
141+
142+
def concat():
143+
curdir = os.getcwd()
144+
os.chdir(curdir)
145+
all_csvs = glob.glob(os.path.join(curdir, '*.csv'))
146+
all_data_frames = []
147+
for a_csv in all_csvs:
148+
try:
149+
data_frame = pandas.read_csv(a_csv, index_col=None)
150+
except EmptyDataError:
151+
data_frame = pandas.DataFrame()
152+
153+
all_data_frames.append(data_frame)
154+
data_frame_concat = pandas.concat(all_data_frames, axis=0, ignore_index=True)
155+
data_frame_concat.to_csv(file_out, index=False)
156+
shutil.move(file_out, '../license_terms_results/')
157+
shutil.rmtree('../temp', ignore_errors=True)
158+
159+
160+
def main():
161+
check_dirs()
162+
start = timeit.default_timer()
163+
genreport()
164+
print("Time spent generating license report: {} seconds".format(int(timeit.default_timer() - start)))
165+
concat()
166+
167+
168+
main()

0 commit comments

Comments
 (0)