Skip to content

Commit f5b4f3f

Browse files
committed
mk
1 parent 7088262 commit f5b4f3f

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
'''
2+
Created on April 25, 2023
3+
@author: kumykov
4+
5+
Copyright (C) 2023 Synopsys, Inc.
6+
http://www.blackducksoftware.com/
7+
8+
Licensed to the Apache Software Foundation (ASF) under one
9+
or more contributor license agreements. See the NOTICE file
10+
distributed with this work for additional information
11+
regarding copyright ownership. The ASF licenses this file
12+
to you under the Apache License, Version 2.0 (the
13+
"License"); you may not use this file except in compliance
14+
with the License. You may obtain a copy of the License at
15+
16+
http://www.apache.org/licenses/LICENSE-2.0
17+
18+
Unless required by applicable law or agreed to in writing,
19+
software distributed under the License is distributed on an
20+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21+
KIND, either express or implied. See the License for the
22+
specific language governing permissions and limitations
23+
under the License.
24+
25+
This script will perform bulk deletion of Project versions based on
26+
the content of an EXCEL file. Each row of a file is expected to contain
27+
a field for Project Name Project Version.
28+
Script will iterate through the rows of a spreadsheet and issue an API
29+
call per row.
30+
If the project version is the last in the project, entire project
31+
will be deleted.
32+
33+
Requirements
34+
35+
- python3 version 3.8 or newer recommended
36+
- the following packages are used by the script and should be installed
37+
prior to use:
38+
argparse
39+
blackduck
40+
csv
41+
logging
42+
re
43+
openpyxl
44+
requests
45+
sys
46+
- Blackduck instance
47+
- API token with sufficient privileges to perform project version phase
48+
change.
49+
50+
Install python packages with the following command:
51+
52+
pip3 install argparse blackduck csv logging re openpyxl requests sys
53+
54+
Using
55+
56+
place the token into a file (token in this example) then execute:
57+
58+
python3 batch_delete_project_version.py -u https://blackduck-host -t token -nv -i excel-file-with-data
59+
60+
Projects and project versions that are listed in the file but are not
61+
present on the blackduck instance will be flagged in the summary.
62+
63+
usage: python3.10 examples/client/batch_delete_project_version.py [-h] -u BASE_URL -t TOKEN_FILE -i INPUT_FILE [-nv] [--dry-run]
64+
65+
options:
66+
-h, --help show this help message and exit
67+
-u BASE_URL, --base-url BASE_URL
68+
Hub server URL e.g. https://your.blackduck.url
69+
-t TOKEN_FILE, --token-file TOKEN_FILE
70+
File containing access token
71+
-i INPUT_FILE, --input-file INPUT_FILE
72+
Project Name
73+
-nv, --no-verify Disable TLS certificate verification
74+
--dry-run Do not delete, dry run
75+
76+
77+
'''
78+
79+
import csv
80+
import sys
81+
import argparse
82+
import logging
83+
from pprint import pprint
84+
85+
from blackduck import Client
86+
from blackduck.constants import VERSION_PHASES
87+
88+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
89+
logging.getLogger("requests").setLevel(logging.WARNING)
90+
logging.getLogger("urllib3").setLevel(logging.WARNING)
91+
logging.getLogger("blackduck").setLevel(logging.DEBUG)
92+
93+
# Values for the variables below should match corresponding column headers
94+
project_name_column = 'Project Name'
95+
project_version_column = 'Version Name'
96+
97+
summary={}
98+
99+
def append_to_summary(message):
100+
global summary_report
101+
summary_report += message + '\n'
102+
103+
def process_csv_file(args):
104+
file = open(args.input_file)
105+
type(file)
106+
csvreader = csv.reader(file)
107+
project_name_idx = None
108+
project_version_idx = None
109+
for row in csvreader:
110+
row_number = csvreader.line_num
111+
if not (project_name_idx and project_version_idx):
112+
project_name_idx = row.index(project_name_column)
113+
project_version_idx = row.index(project_version_column)
114+
elif project_name_idx and project_version_idx:
115+
project_name = row[project_name_idx].strip() if project_name_idx < len(row) else ''
116+
version_name = row[project_version_idx].strip() if project_version_idx < len(row) else ''
117+
if project_name and version_name:
118+
logging.info(f"Processing row {row_number:4}: {row[project_name_idx]} : {row[project_version_idx]}")
119+
process_project_version(project_name, version_name, args)
120+
else:
121+
message = f"Processing row {row_number:4}. Project '{project_name}' version '{version_name}' is not present, skipping"
122+
logging.info(message)
123+
append_to_summary(message)
124+
continue
125+
else:
126+
logging.info("Could not parse input file")
127+
sys.exit(1)
128+
129+
130+
def process_excel_file(args):
131+
wb = openpyxl.load_workbook(args.input_file)
132+
ws = wb.active
133+
project_name_idx = None
134+
project_version_idx = None
135+
row_number = 0
136+
for row in ws.values:
137+
row_number += 1
138+
if not (project_name_idx and project_version_idx):
139+
project_name_idx = row.index(project_name_column)
140+
project_version_idx = row.index(project_version_column)
141+
elif project_name_idx and project_version_idx:
142+
project_name = row[project_name_idx] if project_name_idx < len(row) else ''
143+
version_name = row[project_version_idx] if project_version_idx < len(row) else ''
144+
if project_name and version_name:
145+
logging.info(f"Processing row {row_number:4}: {row[project_name_idx]} : {row[project_version_idx]}")
146+
process_project_version(project_name.strip(), version_name.strip(), args)
147+
else:
148+
message = f"Processing row {row_number:}. Project '{project_name}' version '{version_name}' is not present, skipping"
149+
logging.info(message)
150+
append_to_summary(message)
151+
continue
152+
else:
153+
logging.info("Could not parse input file")
154+
sys.exit(1)
155+
156+
def process_project_version(project_name, version_name, args):
157+
params = {
158+
'q': [f"name:{project_name}"]
159+
}
160+
try:
161+
projects = [p for p in bd.get_resource('projects', params=params) if p['name'] == project_name]
162+
assert len(projects) == 1, f"There should be one, and only one project named {project_name}. We found {len(projects)}"
163+
project = projects[0]
164+
except AssertionError:
165+
message = f"Project named '{project_name}' not found. Skipping"
166+
logging.warning(message)
167+
append_to_summary(message)
168+
return
169+
170+
params = {
171+
'q': [f"versionName:{version_name}"]
172+
}
173+
174+
num_versions = bd.get_resource('versions', project, items=False)['totalCount']
175+
print(num_versions)
176+
177+
try:
178+
versions = [v for v in bd.get_resource('versions', project, params=params) if v['versionName'] == version_name]
179+
assert len(versions) == 1, f"There should be one, and only one version named {version_name}. We found {len(versions)}"
180+
version = versions[0]
181+
except AssertionError:
182+
message = f"Version name '{version_name}' for project {project_name} was not found, skipping"
183+
logging.warning(message)
184+
append_to_summary(message)
185+
return
186+
logging.debug(f"Found {project['name']}:{version['versionName']}")
187+
for component in bd.get_resource('components',version):
188+
license_display = component['licenses'][0]['licenseDisplay']
189+
if license_display not in summary:
190+
summary[license_display] = []
191+
l = summary[license_display]
192+
l.append((component['componentName'],component['componentVersionName']))
193+
194+
def parse_command_args():
195+
196+
parser = argparse.ArgumentParser("batch_delete_project_version.py")
197+
parser.add_argument("-u", "--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
198+
parser.add_argument("-t", "--token-file", required=True, help="File containing access token")
199+
parser.add_argument("-nv", "--no-verify", action='store_false', help="Disable TLS certificate verification")
200+
parser.add_argument("project_name", help="Name of the project")
201+
parser.add_argument("version_name", help="Name of the project version")
202+
return parser.parse_args()
203+
204+
def main():
205+
args = parse_command_args()
206+
207+
with open(args.token_file, 'r') as tf:
208+
access_token = tf.readline().strip()
209+
global bd
210+
bd = Client(base_url=args.base_url, token=access_token, verify=args.no_verify, timeout=60.0, retries=4)
211+
212+
process_project_version(args.project_name, args.version_name, args)
213+
214+
pprint (summary)
215+
216+
if __name__ == "__main__":
217+
sys.exit(main())

0 commit comments

Comments
 (0)