Skip to content

Commit c217db7

Browse files
committed
batch update of project phase script
1 parent c07209c commit c217db7

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
'''
2+
Created on April 4, 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 update of Project version PHASE
26+
based on the content of an EXCEL file
27+
28+
Each row of a file is expected to containe a field for Project Name
29+
Project Version and desired phase
30+
31+
Sript will iterate through the rows of a spreadsheet and issue
32+
an API call per row.
33+
34+
Requirements
35+
36+
python3 vresion 3.8 or newer recommended
37+
38+
the following packages are used by the script and should be installed prior to use:
39+
40+
argparse
41+
blackduck
42+
csv
43+
logging
44+
re
45+
openpyxl
46+
sys
47+
48+
install them with the following command:
49+
50+
pip3 install argparse blackduck csv logging re openpyxl sys
51+
52+
Blackduck instance
53+
API token with sufficient privileges to perform project version phase change
54+
55+
Using
56+
57+
place the token into a file (token in this example)
58+
59+
execute
60+
61+
python3 batch_update_project_phase.py -u https://blackduck-host -t token -nv -i excel-file-with-data
62+
63+
Projects and project versions that are listed in the file but are not present on the blackduck instance will be skipped.
64+
If a project version is already at a requested phase, no update will be executed
65+
66+
'''
67+
68+
import csv
69+
import sys
70+
import argparse
71+
import logging
72+
import re
73+
import openpyxl
74+
75+
from blackduck import Client
76+
from blackduck.constants import VERSION_PHASES
77+
78+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
79+
logging.getLogger("requests").setLevel(logging.WARNING)
80+
logging.getLogger("urllib3").setLevel(logging.WARNING)
81+
logging.getLogger("blackduck").setLevel(logging.DEBUG)
82+
83+
# Values for the variables below should match corresponding column headers
84+
project_name_column = 'Project Name'
85+
project_version_column = 'Version Name'
86+
project_phase_column = 'Version phase'
87+
88+
def process_csv_file(filename):
89+
file = open(filename)
90+
type(file)
91+
csvreader = csv.reader(file)
92+
project_name_idx = None
93+
project_version_idx = None
94+
project_phase_idx = None
95+
for row in csvreader:
96+
if not (project_name_idx and project_version_idx and project_phase_idx):
97+
project_name_idx = row.index(project_name_column)
98+
project_version_idx = row.index(project_version_column)
99+
project_phase_idx = row.index(project_phase_column)
100+
elif project_name_idx and project_version_idx and project_phase_idx:
101+
logging.info(f"Processing {row[project_name_idx]} : {row[project_version_idx]} with {row[project_phase_idx]}")
102+
process_project_version(row[project_name_idx], row[project_version_idx], row[project_phase_idx])
103+
else:
104+
logging.info("Could not parse input file")
105+
sys.exit(1)
106+
107+
108+
def process_excel_file(filename):
109+
wb = openpyxl.load_workbook(filename)
110+
ws = wb.active
111+
project_name_idx = None
112+
project_version_idx = None
113+
project_phase_idx = None
114+
for row in ws.values:
115+
if not (project_name_idx and project_version_idx and project_phase_idx):
116+
project_name_idx = row.index(project_name_column)
117+
project_version_idx = row.index(project_version_column)
118+
project_phase_idx = row.index(project_phase_column)
119+
elif project_name_idx and project_version_idx and project_phase_idx:
120+
logging.info(f"Processing {row[project_name_idx]} : {row[project_version_idx]} with {row[project_phase_idx]}")
121+
process_project_version(row[project_name_idx], row[project_version_idx], row[project_phase_idx])
122+
else:
123+
logging.info("Could not parse input file")
124+
sys.exit(1)
125+
126+
def process_project_version(project_name, version_name, phase):
127+
params = {
128+
'q': [f"name:{project_name}"]
129+
}
130+
try:
131+
projects = [p for p in bd.get_resource('projects', params=params) if p['name'] == project_name]
132+
assert len(projects) == 1, f"There should be one, and only one project named {project_name}. We found {len(projects)}"
133+
project = projects[0]
134+
except AssertionError:
135+
logging.warning(f"Project named {project_name} not found. Skipping")
136+
return
137+
138+
params = {
139+
'q': [f"versionName:{version_name}"]
140+
}
141+
142+
try:
143+
versions = [v for v in bd.get_resource('versions', project, params=params) if v['versionName'] == version_name]
144+
assert len(versions) == 1, f"There should be one, and only one version named {version_name}. We found {len(versions)}"
145+
version = versions[0]
146+
except AssertionError:
147+
logging.warning(f"Version name {version_name} for project {project_name} was not found, skipping")
148+
return
149+
logging.debug(f"Found {project['name']}:{version['versionName']}")
150+
151+
try:
152+
assert phase in VERSION_PHASES, f"Invalid version phase {phase}for {project_name} {version_name}"
153+
except AssertionError:
154+
logging.warning(f"Invalid version phase {phase}for {project_name} {version_name}. Skipping")
155+
156+
if phase == version['phase' ]:
157+
logging.info(f"Project {project_name} version {version_name} is already at {phase}. No update")
158+
return
159+
160+
logging.debug(f"Updating {project['name']}:{version['versionName']} settings to {phase}")
161+
version['phase'] = phase
162+
try:
163+
r = bd.session.put(version['_meta']['href'], json=version)
164+
r.raise_for_status()
165+
logging.debug(f"updated version settings to the new phase ({phase})")
166+
except requests.HTTPError as err:
167+
bd.http_error_handler(err)
168+
169+
170+
def parse_command_args():
171+
172+
parser = argparse.ArgumentParser("Print copyrights for BOM using upstream origin or prior version if not available.")
173+
parser.add_argument("-u", "--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
174+
parser.add_argument("-t", "--token-file", required=True, help="File containing access token")
175+
parser.add_argument("-i", "--input-file", required=True, help="Project Name")
176+
parser.add_argument("-nv", "--no-verify", action='store_false', help="Disable TLS certificate verification")
177+
178+
return parser.parse_args()
179+
180+
def main():
181+
args = parse_command_args()
182+
with open(args.token_file, 'r') as tf:
183+
access_token = tf.readline().strip()
184+
global bd
185+
bd = Client(base_url=args.base_url, token=access_token, verify=args.no_verify, timeout=60.0, retries=4)
186+
187+
if re.match(".+xlsx?$", args.input_file):
188+
logging.info(f"Processing EXCEL file {args.input_file}")
189+
process_excel_file(args.input_file)
190+
else:
191+
logging.info(f"Processing CSV file {args.input_file}")
192+
process_csv_file(args.input_file)
193+
194+
if __name__ == "__main__":
195+
sys.exit(main())

0 commit comments

Comments
 (0)