Skip to content

Commit af6b6d6

Browse files
author
dnichol
committed
Add recursive generation of source reports for sub projects and concatenation to a consolidated source report
1 parent 9e5cd22 commit af6b6d6

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
generate_source_report_for_sub_projects_recursive
3+
4+
Created on February 15, 2021
5+
6+
Adapted from generate_source_report_for_sub_projects.py written by AMacDonald
7+
8+
@author: DNicholls
9+
10+
Script designed to generate and collate Source reports for the master project and it's sub-projects recursively.
11+
12+
If a project has source but also sub projects with source that also have sub projects with source this utility will
13+
generate source reports for each level of project and combine to a consolidated source report. It will also generate
14+
security and components reports for the master project (these automatically include subprojects). It will output the
15+
results to a ./results folder and uses ./temp for work in progress. These can be modified in the below checkdirs, unzip
16+
and concat functions. If you are finding the reports are not generated in time by default it will wait 5 seconds and
17+
retry 10 times, if this is not long enough as your projects are large then increase the retries_per_download variable.
18+
19+
To run this script, you will need to pass arguments for the master project name and the master project version. Once
20+
they are specified, the script will investigate to see if the master project contains sub-projects and will generate
21+
reports for all sub-projects it discovers. Finally, it will combine them into a single report, saving it to a "results"
22+
sub-directory.
23+
24+
For this script to run, the hub-rest-api-python (blackduck) library and pandas library will need to be installed.
25+
26+
"""
27+
28+
import argparse
29+
from blackduck.HubRestApi import HubInstance
30+
import time
31+
from zipfile import ZipFile
32+
import shutil
33+
import os
34+
import glob
35+
import pandas
36+
37+
parser = argparse.ArgumentParser("A program to create consolidated Source report for sub projects")
38+
parser.add_argument("project_name")
39+
parser.add_argument("version_name")
40+
args = parser.parse_args()
41+
hub = HubInstance()
42+
csv_list = []
43+
timestamp = time.strftime('%m_%d_%Y_%H_%M')
44+
retries_per_download=10
45+
file_out = (args.project_name + '_' + "Consolidated_src_report-" + timestamp)
46+
file_out = (file_out + ".csv")
47+
48+
49+
class FailedReportDownload(Exception):
50+
pass
51+
52+
53+
def download_report(location, filename, retries=retries_per_download):
54+
report_id = location.split("/")[-1]
55+
56+
if retries:
57+
print("Retrieving generated report from {}".format(location))
58+
response = hub.download_report(report_id)
59+
if response.status_code == 200:
60+
with open(filename, "wb") as f:
61+
f.write(response.content)
62+
63+
print("Successfully downloaded zip file to {} for report {}".format(filename, report_id))
64+
else:
65+
print("Failed to retrieve report {}".format(report_id))
66+
print("Probably not ready yet, waiting 5 seconds then retrying...")
67+
time.sleep(5)
68+
retries -= 1
69+
download_report(location, filename, retries)
70+
else:
71+
raise FailedReportDownload("Failed to retrieve report {} after multiple retries".format(report_id))
72+
73+
def genreportsforversion(projectname,versionname,reportlist):
74+
print("Generating source report for project {} version {}".format(projectname, versionname))
75+
76+
projversion = hub.get_project_version_by_name(projectname, versionname)
77+
components = hub.get_version_components(projversion)
78+
79+
subname = (projectname + '_' + versionname + '.zip')
80+
## CURRENTLY COMBINES ALL REPORTS TOGETHER.
81+
## SWITCH TO GENERATE COMPONENTS AND SECURITY AFTER SEPARATELY OR EXCLUDE NON SOURCE FROM CONCAT
82+
result = hub.create_version_reports(version=projversion, report_list=reportlist, format="CSV")
83+
# Generates reports in the main project for SECURITY, COMPONENTS and FILES (source) report,
84+
# Using the version object (line 21) to say which reports are needed
85+
# prints out success/error code.
86+
if result.status_code == 201:
87+
print("Successfully created reports ({}) for project {} and version {}".format(
88+
reportlist, projectname, versionname))
89+
location = result.headers['Location']
90+
download_report(location, subname)
91+
else:
92+
print("Failed to create reports for project {} version {}, status code returned {}".format(
93+
projectname, versionname, result.status_code))
94+
95+
for component in components['items']:
96+
subname = (component['componentName'] + '_' + component['componentVersionName'] + '.zip')
97+
subname = (subname.replace(" ", ""))
98+
# Above step is to generate the output from the get_version_components and specifically look at the activityData
99+
# portion to indicate whether a component is a KB component, or a subproject.
100+
if len(component['activityData']) == 0:
101+
# Above checks length of output from activityData is >0. If equals 0, is sub-project.
102+
print("activityData is empty, {} is subproject version {}".format(component['componentName'],component['componentVersionName']))
103+
genreportsforversion(component['componentName'],component['componentVersionName'],reportlist=['FILES'])
104+
elif len(component['activityData']) != 0:
105+
print('is OSS component, no report to download')
106+
107+
108+
def checkdirs():
109+
if os.path.isdir('./temp') == False:
110+
os.makedirs('./temp')
111+
print('made temp directory')
112+
else:
113+
print('temp directory already exists')
114+
if os.path.isdir('./results') == False:
115+
os.makedirs('./results')
116+
print('made results directory')
117+
else:
118+
print('results directory already exists')
119+
120+
121+
def unzip():
122+
for filename in os.listdir("."):
123+
if filename.endswith(".zip"):
124+
shutil.move(filename, './temp/')
125+
curdir = (os.getcwd() + './temp/')
126+
os.chdir(curdir)
127+
for zipfile in os.listdir(curdir):
128+
with ZipFile(zipfile, 'r') as zipObj:
129+
zipObj.extractall()
130+
131+
132+
def concat():
133+
for csv in glob.iglob('**/source*.csv'):
134+
csv_list.append(csv)
135+
consolidated = pandas.concat([pandas.read_csv(csv) for csv in csv_list])
136+
consolidated.to_csv(file_out, index=False, encoding="utf-8")
137+
shutil.move(file_out, '../results/')
138+
139+
# If you do not want the original source reports for each project exclude this.
140+
files = glob.iglob('**/*.csv')
141+
for csv in files:
142+
shutil.move(csv, '../results/')
143+
144+
# Clean up after
145+
shutil.rmtree('../temp', ignore_errors=True)
146+
147+
148+
def main():
149+
checkdirs()
150+
genreportsforversion(args.project_name, args.version_name,reportlist=['FILES','COMPONENTS','SECURITY'])
151+
unzip()
152+
concat()
153+
154+
155+
main()

0 commit comments

Comments
 (0)