capycli 2.0.0.dev8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- License.md +27 -0
- capycli/__init__.py +214 -0
- capycli/__main__.py +13 -0
- capycli/bom/__init__.py +10 -0
- capycli/bom/bom_convert.py +163 -0
- capycli/bom/check_bom.py +187 -0
- capycli/bom/check_bom_item_status.py +197 -0
- capycli/bom/check_granularity.py +244 -0
- capycli/bom/create_components.py +644 -0
- capycli/bom/csv.py +69 -0
- capycli/bom/diff_bom.py +279 -0
- capycli/bom/download_sources.py +227 -0
- capycli/bom/filter_bom.py +323 -0
- capycli/bom/findsources.py +278 -0
- capycli/bom/handle_bom.py +134 -0
- capycli/bom/html.py +67 -0
- capycli/bom/legacy.py +312 -0
- capycli/bom/legacy_cx.py +151 -0
- capycli/bom/map_bom.py +1039 -0
- capycli/bom/merge_bom.py +155 -0
- capycli/bom/plaintext.py +69 -0
- capycli/bom/show_bom.py +77 -0
- capycli/common/__init__.py +9 -0
- capycli/common/capycli_bom_support.py +629 -0
- capycli/common/comparable_version.py +161 -0
- capycli/common/component_cache.py +240 -0
- capycli/common/dependencies_base.py +48 -0
- capycli/common/file_support.py +28 -0
- capycli/common/html_support.py +119 -0
- capycli/common/json_support.py +36 -0
- capycli/common/map_result.py +116 -0
- capycli/common/print.py +55 -0
- capycli/common/purl_service.py +169 -0
- capycli/common/purl_store.py +100 -0
- capycli/common/purl_utils.py +85 -0
- capycli/common/script_base.py +165 -0
- capycli/common/script_support.py +78 -0
- capycli/data/__init__.py +9 -0
- capycli/data/granularity_list.csv +1338 -0
- capycli/dependencies/__init__.py +9 -0
- capycli/dependencies/handle_dependencies.py +70 -0
- capycli/dependencies/javascript.py +261 -0
- capycli/dependencies/maven_list.py +333 -0
- capycli/dependencies/maven_pom.py +150 -0
- capycli/dependencies/nuget.py +184 -0
- capycli/dependencies/python.py +345 -0
- capycli/main/__init__.py +9 -0
- capycli/main/application.py +165 -0
- capycli/main/argument_parser.py +101 -0
- capycli/main/cli.py +28 -0
- capycli/main/exceptions.py +14 -0
- capycli/main/options.py +424 -0
- capycli/main/result_codes.py +41 -0
- capycli/mapping/handle_mapping.py +46 -0
- capycli/mapping/mapping_to_html.py +182 -0
- capycli/mapping/mapping_to_xlsx.py +197 -0
- capycli/moverview/handle_moverview.py +46 -0
- capycli/moverview/moverview_to_html.py +122 -0
- capycli/moverview/moverview_to_xlsx.py +170 -0
- capycli/project/__init__.py +9 -0
- capycli/project/check_prerequisites.py +304 -0
- capycli/project/create_bom.py +190 -0
- capycli/project/create_project.py +335 -0
- capycli/project/create_readme.py +546 -0
- capycli/project/find_project.py +128 -0
- capycli/project/get_license_info.py +246 -0
- capycli/project/handle_project.py +118 -0
- capycli/project/show_ecc.py +200 -0
- capycli/project/show_licenses.py +211 -0
- capycli/project/show_project.py +215 -0
- capycli/project/show_vulnerabilities.py +238 -0
- capycli-2.0.0.dev8.dist-info/LICENSES/CC0-1.0.txt +121 -0
- capycli-2.0.0.dev8.dist-info/LICENSES/MIT.txt +27 -0
- capycli-2.0.0.dev8.dist-info/License.md +27 -0
- capycli-2.0.0.dev8.dist-info/METADATA +268 -0
- capycli-2.0.0.dev8.dist-info/RECORD +78 -0
- capycli-2.0.0.dev8.dist-info/WHEEL +4 -0
- capycli-2.0.0.dev8.dist-info/entry_points.txt +3 -0
capycli/main/options.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2019-23 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
"""Contains the logic for all of the default options for CaPyCli."""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import tomli
|
|
14
|
+
|
|
15
|
+
import capycli
|
|
16
|
+
from capycli.bom.bom_convert import BomFormat
|
|
17
|
+
from capycli.main.argument_parser import ArgumentParser
|
|
18
|
+
|
|
19
|
+
LOG = capycli.get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CommandlineSupport():
|
|
23
|
+
CONFIG_FILE_NAME = ".capycli.cfg"
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
custom_prog = "CaPyCli, " + capycli.get_app_version()
|
|
27
|
+
custom_usage = "CaPyCli command subcommand [options]"
|
|
28
|
+
command_help = """Commands and Sub-Commands
|
|
29
|
+
getdependencies dependency detection specific commands
|
|
30
|
+
Nuget determine dependencies for a .Net/Nuget project
|
|
31
|
+
Python determine dependencies for a Python project
|
|
32
|
+
Javascript determine dependencies for a JavaScript project
|
|
33
|
+
MavenPom determine dependencies for a Java/Maven project using the pom.xml file
|
|
34
|
+
MavenList determine dependencies for a Java/Maven project using a Maven command
|
|
35
|
+
|
|
36
|
+
bom bill of material (BOM) specific commands
|
|
37
|
+
Show display contents of a SBOM
|
|
38
|
+
Convert convert SBOM formats
|
|
39
|
+
Filter apply filter file to a SBOM
|
|
40
|
+
Check check that all releases in the SBOM exist on target SW360 instance
|
|
41
|
+
CheckItemStatus show additional information about SBOM items on SW360
|
|
42
|
+
Map map a given SBOM to data on SW360
|
|
43
|
+
CreateReleases create new releases for existing components on SW360
|
|
44
|
+
CreateComponents create new components and releases on SW360 (use with care!)
|
|
45
|
+
DownloadSources download source files from the URL specified in the SBOM
|
|
46
|
+
Granularity check a bill of material for potential component granularity issues
|
|
47
|
+
Diff compare two bills of material.
|
|
48
|
+
Merge merge two bills of material.
|
|
49
|
+
Findsources determine the source code for SBOM items.
|
|
50
|
+
|
|
51
|
+
mapping
|
|
52
|
+
ToHtml create a HTML page showing the mapping result
|
|
53
|
+
ToXlsx create an Excel sheet showing the mapping result
|
|
54
|
+
|
|
55
|
+
moverview
|
|
56
|
+
ToHtml create a HTML page showing the mapping result overview
|
|
57
|
+
ToXlsx create an Excel sheet showing the mapping result overview
|
|
58
|
+
|
|
59
|
+
project
|
|
60
|
+
Find find a project by name
|
|
61
|
+
Prerequisites checks whether all prerequisites for a successfull
|
|
62
|
+
software clearing are fulfilled
|
|
63
|
+
Show show project details
|
|
64
|
+
Licenses show licenses of all cleared compponents
|
|
65
|
+
Create create or update a project on SW360
|
|
66
|
+
Update update an exiting project, preserving linked releases
|
|
67
|
+
GetLicenseInfo get license info of all project components
|
|
68
|
+
CreateReadme create a Readme_OSS
|
|
69
|
+
Vulnerabilities show security vulnerabilities of a project
|
|
70
|
+
ECC show export control status of a project
|
|
71
|
+
|
|
72
|
+
Note that each command has also its own help display, i.e. if you enter
|
|
73
|
+
`capycli project vulnerabilities -h` you will get a help that only shows the options
|
|
74
|
+
for this specific sub-command.
|
|
75
|
+
Entering `capycli project -h` shows all available sub-commands of the project command.
|
|
76
|
+
"""
|
|
77
|
+
self.parser = ArgumentParser(
|
|
78
|
+
prog=custom_prog,
|
|
79
|
+
usage=custom_usage,
|
|
80
|
+
description="SW360 Clearing Automation Command Line Interface, version " + capycli.get_app_version())
|
|
81
|
+
self.parser.add_command_help(command_help)
|
|
82
|
+
|
|
83
|
+
# store all positional argument in command
|
|
84
|
+
self.parser.add_argument(
|
|
85
|
+
"command",
|
|
86
|
+
nargs="+",
|
|
87
|
+
help="command and subcommand to process")
|
|
88
|
+
|
|
89
|
+
self.parser.add_argument(
|
|
90
|
+
"-h",
|
|
91
|
+
"--help",
|
|
92
|
+
help="show a help message and exit",
|
|
93
|
+
action="store_true",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.register_options()
|
|
97
|
+
|
|
98
|
+
def register_options(self):
|
|
99
|
+
input_formats = []
|
|
100
|
+
input_formats.append(BomFormat.TEXT)
|
|
101
|
+
input_formats.append(BomFormat.CSV)
|
|
102
|
+
input_formats.append(BomFormat.LEGACY)
|
|
103
|
+
input_formats.append(BomFormat.LEGACY_CX)
|
|
104
|
+
input_formats.append(BomFormat.SBOM)
|
|
105
|
+
input_formats.append(BomFormat.CAPYCLI)
|
|
106
|
+
|
|
107
|
+
output_formats = []
|
|
108
|
+
output_formats.append(BomFormat.CAPYCLI)
|
|
109
|
+
output_formats.append(BomFormat.SBOM)
|
|
110
|
+
output_formats.append(BomFormat.TEXT)
|
|
111
|
+
output_formats.append(BomFormat.CSV)
|
|
112
|
+
output_formats.append(BomFormat.LEGACY)
|
|
113
|
+
output_formats.append(BomFormat.HTML)
|
|
114
|
+
|
|
115
|
+
self.parser.add_argument(
|
|
116
|
+
"-i",
|
|
117
|
+
"--inputfile",
|
|
118
|
+
dest="inputfile",
|
|
119
|
+
help="input file to read from",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
self.parser.add_argument(
|
|
123
|
+
"-ri",
|
|
124
|
+
"--raw-input",
|
|
125
|
+
dest="raw_input",
|
|
126
|
+
help="raw data input file to parse repository urls"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
self.parser.add_argument(
|
|
130
|
+
"-o",
|
|
131
|
+
"--outputfile",
|
|
132
|
+
dest="outputfile",
|
|
133
|
+
help="output file to write to",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self.parser.add_argument(
|
|
137
|
+
"-filterfile",
|
|
138
|
+
dest="filterfile",
|
|
139
|
+
help="filter file to use",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.parser.add_argument(
|
|
143
|
+
"-v",
|
|
144
|
+
help="be verbose",
|
|
145
|
+
dest="verbose",
|
|
146
|
+
action="store_true",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
self.parser.add_argument(
|
|
150
|
+
"-t",
|
|
151
|
+
"--token",
|
|
152
|
+
dest="sw360_token",
|
|
153
|
+
help="use this token for access to SW360",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.parser.add_argument(
|
|
157
|
+
"-oa",
|
|
158
|
+
"--oauth2",
|
|
159
|
+
help="this is an oauth2 token",
|
|
160
|
+
action="store_true",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.parser.add_argument(
|
|
164
|
+
"-url",
|
|
165
|
+
dest="sw360_url",
|
|
166
|
+
help="use this URL for access to SW360"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
self.parser.add_argument(
|
|
170
|
+
"--nocache",
|
|
171
|
+
dest="nocache",
|
|
172
|
+
help="do not use component cache",
|
|
173
|
+
action="store_true"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
self.parser.add_argument(
|
|
177
|
+
"-cf",
|
|
178
|
+
"--cachefile",
|
|
179
|
+
dest="cachefile",
|
|
180
|
+
help="cache file name to use",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.parser.add_argument(
|
|
184
|
+
"-rc",
|
|
185
|
+
"--refresh_cache",
|
|
186
|
+
dest="refresh_cache",
|
|
187
|
+
help="refresh component cache",
|
|
188
|
+
action="store_true",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
self.parser.add_argument(
|
|
192
|
+
"-sc",
|
|
193
|
+
"--similar",
|
|
194
|
+
help="look for components with similar name",
|
|
195
|
+
action="store_true",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
self.parser.add_argument(
|
|
199
|
+
"-ov",
|
|
200
|
+
"--overview",
|
|
201
|
+
dest="create_overview",
|
|
202
|
+
help="create an mapping overview JSON file",
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
self.parser.add_argument(
|
|
206
|
+
"-mr",
|
|
207
|
+
"--mapresult",
|
|
208
|
+
dest="write_mapresult",
|
|
209
|
+
help="create a JSON file with the mapping details",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# used by project commands
|
|
213
|
+
self.parser.add_argument(
|
|
214
|
+
"-name",
|
|
215
|
+
help="name of the project"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# used by project commands
|
|
219
|
+
self.parser.add_argument(
|
|
220
|
+
"-version",
|
|
221
|
+
help="version of the project"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# used by project commands
|
|
225
|
+
self.parser.add_argument(
|
|
226
|
+
"-id",
|
|
227
|
+
dest="id",
|
|
228
|
+
help="SW360 id of the project, supersedes name and version parameters"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# used by GetLicenseInfo
|
|
232
|
+
self.parser.add_argument(
|
|
233
|
+
"-ncli",
|
|
234
|
+
"--no-overwrite-cli",
|
|
235
|
+
dest="ncli",
|
|
236
|
+
help="do not overwrite existing CLI files",
|
|
237
|
+
action="store_true",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# used by GetLicenseInfo
|
|
241
|
+
self.parser.add_argument(
|
|
242
|
+
"-nconf",
|
|
243
|
+
"--no-overwrite-config",
|
|
244
|
+
dest="nconf",
|
|
245
|
+
help="do not overwrite an existing configuration file",
|
|
246
|
+
action="store_true",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# used by GetLicenseInfo
|
|
250
|
+
self.parser.add_argument(
|
|
251
|
+
"-dest",
|
|
252
|
+
"--destination",
|
|
253
|
+
dest="destination",
|
|
254
|
+
help="the destination folder",
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# used by CreateProject
|
|
258
|
+
self.parser.add_argument(
|
|
259
|
+
"-source",
|
|
260
|
+
dest="source",
|
|
261
|
+
help="source folder or additional source file"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# special parsing flag for MapBom
|
|
265
|
+
self.parser.add_argument(
|
|
266
|
+
"--dbx",
|
|
267
|
+
dest="dbx",
|
|
268
|
+
help="relaxed handling of debian version numbers (check subcommand help!)",
|
|
269
|
+
action="store_true",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
self.parser.add_argument(
|
|
273
|
+
"--download",
|
|
274
|
+
help="enable automatic download of missing sources",
|
|
275
|
+
action="store_true",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# used by getdependencies python
|
|
279
|
+
self.parser.add_argument(
|
|
280
|
+
"--search-meta-data",
|
|
281
|
+
dest="search_meta_data",
|
|
282
|
+
help="search for component meta-data",
|
|
283
|
+
action="store_true",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# used by project create
|
|
287
|
+
self.parser.add_argument(
|
|
288
|
+
"-old-version",
|
|
289
|
+
dest="old_version",
|
|
290
|
+
help="previous version "
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
self.parser.add_argument(
|
|
294
|
+
"-ex",
|
|
295
|
+
help="show exit code",
|
|
296
|
+
action="store_true",
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# used by bom map
|
|
300
|
+
self.parser.add_argument(
|
|
301
|
+
"-rr",
|
|
302
|
+
dest="result_required",
|
|
303
|
+
help="there must be a clearing result available",
|
|
304
|
+
action="store_true"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# used by getdependencies python
|
|
308
|
+
self.parser.add_argument(
|
|
309
|
+
"-package-source",
|
|
310
|
+
dest="package_source",
|
|
311
|
+
help="URL of the package manager to use",
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# used by bom CheckItemStatus
|
|
315
|
+
self.parser.add_argument(
|
|
316
|
+
"-all",
|
|
317
|
+
help="show/use all items",
|
|
318
|
+
action="store_true",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# used by ShowSecurityVulnerability
|
|
322
|
+
self.parser.add_argument(
|
|
323
|
+
"-format",
|
|
324
|
+
dest="format",
|
|
325
|
+
help="format to use (text, json, xml)",
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# used by ShowSecurityVulnerability
|
|
329
|
+
self.parser.add_argument(
|
|
330
|
+
"-fe",
|
|
331
|
+
"--forceexit",
|
|
332
|
+
dest="force_exit",
|
|
333
|
+
help="force a specific exit code",
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# used by MapBom
|
|
337
|
+
self.parser.add_argument(
|
|
338
|
+
"-m",
|
|
339
|
+
"--mode",
|
|
340
|
+
dest="mode",
|
|
341
|
+
help="specific mode for some commands",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# used by bom convert
|
|
345
|
+
self.parser.add_argument(
|
|
346
|
+
"-if",
|
|
347
|
+
choices=input_formats,
|
|
348
|
+
dest="inputformat",
|
|
349
|
+
help="Specify input file format")
|
|
350
|
+
|
|
351
|
+
# used by bom convert
|
|
352
|
+
self.parser.add_argument(
|
|
353
|
+
"-of",
|
|
354
|
+
choices=output_formats,
|
|
355
|
+
dest="outputformat",
|
|
356
|
+
help="Specify output file format")
|
|
357
|
+
|
|
358
|
+
self.parser.add_argument(
|
|
359
|
+
"-X",
|
|
360
|
+
dest="debug", action="store_true",
|
|
361
|
+
help="Enable debug output")
|
|
362
|
+
|
|
363
|
+
def read_config(self, filename: str = None, config_string: str = None) -> dict:
|
|
364
|
+
"""
|
|
365
|
+
Read configuration from string or config file.
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
toml_dict = None
|
|
369
|
+
try:
|
|
370
|
+
if config_string:
|
|
371
|
+
toml_dict = tomli.loads(config_string)
|
|
372
|
+
elif filename:
|
|
373
|
+
with open(filename, "rb") as f:
|
|
374
|
+
toml_dict = tomli.load(f)
|
|
375
|
+
else:
|
|
376
|
+
if os.path.isfile(self.CONFIG_FILE_NAME):
|
|
377
|
+
with open(self.CONFIG_FILE_NAME, "rb") as f:
|
|
378
|
+
toml_dict = tomli.load(f)
|
|
379
|
+
|
|
380
|
+
if not toml_dict:
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
if "capycli" not in toml_dict:
|
|
384
|
+
return None
|
|
385
|
+
|
|
386
|
+
return toml_dict["capycli"]
|
|
387
|
+
except tomli.TOMLDecodeError as tex:
|
|
388
|
+
LOG.warning("Config file has invalid format: " + repr(tex))
|
|
389
|
+
except Exception as ex:
|
|
390
|
+
LOG.warning("Error reading config file: " + repr(ex))
|
|
391
|
+
|
|
392
|
+
def process_commandline(self, argv):
|
|
393
|
+
"""Reads the command line arguments"""
|
|
394
|
+
args = self.parser.parse_args(argv)
|
|
395
|
+
cfg = self.read_config()
|
|
396
|
+
|
|
397
|
+
if cfg:
|
|
398
|
+
for key in cfg:
|
|
399
|
+
args_key = key
|
|
400
|
+
|
|
401
|
+
# handle some common naming mistakes
|
|
402
|
+
if args_key == "url":
|
|
403
|
+
args_key = "sw360_url"
|
|
404
|
+
if args_key == "url":
|
|
405
|
+
args_key = "sw360_url"
|
|
406
|
+
if args_key == "raw-input":
|
|
407
|
+
args_key = "raw_input"
|
|
408
|
+
if args_key == "token":
|
|
409
|
+
args_key = "sw360_token"
|
|
410
|
+
if args_key == "oa":
|
|
411
|
+
args_key = "oauth2"
|
|
412
|
+
if args_key == "search-meta-data":
|
|
413
|
+
args_key = "search_meta_data"
|
|
414
|
+
if args_key == "old-version":
|
|
415
|
+
args_key = "old_version"
|
|
416
|
+
if args_key == "package-source":
|
|
417
|
+
args_key = "package_source"
|
|
418
|
+
if args_key == "forceexit":
|
|
419
|
+
args_key = "force_exit"
|
|
420
|
+
|
|
421
|
+
if hasattr(args, args_key) and not args.__getattribute__(args_key):
|
|
422
|
+
args.__setattr__(args_key, cfg[key])
|
|
423
|
+
|
|
424
|
+
return args
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright 2023 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
class ResultCode(object):
|
|
10
|
+
# application result codes
|
|
11
|
+
|
|
12
|
+
# default
|
|
13
|
+
RESULT_OPERATION_SUCCEEDED = 0
|
|
14
|
+
|
|
15
|
+
# general errors as defined in https://tldp.org/LDP/abs/html/exitcodes.html
|
|
16
|
+
RESULT_GENERAL_ERROR = 1
|
|
17
|
+
|
|
18
|
+
# predefined errors from /usr/include/sysexits.h
|
|
19
|
+
RESULT_COMMAND_ERROR = 64 # command was used incorrectly
|
|
20
|
+
RESULT_ERROR_READING_BOM = 65 # input data was incorrect
|
|
21
|
+
RESULT_FILE_NOT_FOUND = 66 # input file did not exist or was not readable
|
|
22
|
+
RESULT_ERROR_ACCESSING_SERVICE = 69 # a service is unavailable
|
|
23
|
+
RESULT_ERROR_WRITING_FILE = 73 # output file cannot be created
|
|
24
|
+
RESULT_AUTH_ERROR = 77 # insufficient permission to perform some operation, SW360 login failed
|
|
25
|
+
RESULT_ERROR_WRITING_BOM = 78
|
|
26
|
+
|
|
27
|
+
# use 80-113 for our exit codes, see https://tldp.org/LDP/abs/html/exitcodes.html
|
|
28
|
+
|
|
29
|
+
# custom result codes
|
|
30
|
+
RESULT_NO_UNIQUE_MAPPING = 80
|
|
31
|
+
RESULT_INCOMPLETE_MAPPING = 81
|
|
32
|
+
RESULT_UNHANDLED_SECURITY_VULNERABILITY_FOUND = 82
|
|
33
|
+
|
|
34
|
+
# custom errors
|
|
35
|
+
RESULT_ERROR_CREATING_COMPONENT = 90
|
|
36
|
+
RESULT_ERROR_CREATING_RELEASE = 91
|
|
37
|
+
RESULT_ERROR_CREATING_ITEM = 92
|
|
38
|
+
RESULT_NO_CACHED_RELEASES = 93
|
|
39
|
+
RESULT_PROJECT_NOT_FOUND = 94
|
|
40
|
+
RESULT_ERROR_ACCESSING_SW360 = 95
|
|
41
|
+
RESULT_FILTER_ERROR = 96
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2019-23 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import capycli.mapping.mapping_to_html
|
|
12
|
+
import capycli.mapping.mapping_to_xlsx
|
|
13
|
+
from capycli.common.print import print_red
|
|
14
|
+
from capycli.main.result_codes import ResultCode
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_mapping_command(args):
|
|
18
|
+
command = args.command[0].lower()
|
|
19
|
+
if command != "mapping":
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
if len(args.command) < 2:
|
|
23
|
+
print_red("No subcommand specified!")
|
|
24
|
+
print()
|
|
25
|
+
|
|
26
|
+
# display `mapping` related help
|
|
27
|
+
print("mapping - mapping sub-commands")
|
|
28
|
+
print(" ToHtml create a HTML page showing the mapping result")
|
|
29
|
+
print(" ToXlsx create an Excel sheet showing the mapping result")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
subcommand = args.command[1].lower()
|
|
33
|
+
if subcommand == "tohtml":
|
|
34
|
+
"""Create a HTML page showing the mapping result."""
|
|
35
|
+
app = capycli.mapping.mapping_to_html.MappingToHtml()
|
|
36
|
+
app.run(args)
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
if subcommand == "toxlsx":
|
|
40
|
+
"""Create an Excel sheet showing the mapping result."""
|
|
41
|
+
app = capycli.mapping.mapping_to_xlsx.MappingToExcelXlsx()
|
|
42
|
+
app.run(args)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
print_red("Unknown sub-command: " + subcommand)
|
|
46
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2019-23 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
import capycli.common.html_support
|
|
13
|
+
import capycli.common.json_support
|
|
14
|
+
import capycli.common.script_base
|
|
15
|
+
from capycli import get_logger
|
|
16
|
+
from capycli.bom.map_bom import MapBom
|
|
17
|
+
from capycli.common.map_result import MapResult
|
|
18
|
+
from capycli.common.print import print_red, print_text
|
|
19
|
+
from capycli.main.result_codes import ResultCode
|
|
20
|
+
|
|
21
|
+
LOG = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MappingToHtml(capycli.common.script_base.ScriptBase):
|
|
25
|
+
"""Create a HTML page showing the mapping result"""
|
|
26
|
+
|
|
27
|
+
RELEASE_URL = os.environ.get("SW360ServerUrl", "") + "/group/guest/components/-/component/release/detailRelease/"
|
|
28
|
+
|
|
29
|
+
def mapping_result_to_html(self, details, outputfile):
|
|
30
|
+
"""Create a HTML page showing the mapping overview"""
|
|
31
|
+
myhtml = capycli.common.html_support.HtmlSupport()
|
|
32
|
+
lineend = myhtml.get_lineend()
|
|
33
|
+
|
|
34
|
+
with open(outputfile, "w") as htmlfile:
|
|
35
|
+
myhtml.write_start(htmlfile)
|
|
36
|
+
style = myhtml.create_style()
|
|
37
|
+
myhtml.write_header(htmlfile, "Mapping Result Details", style)
|
|
38
|
+
myhtml.start_body(htmlfile)
|
|
39
|
+
myhtml.write_title_heading(htmlfile, "Mapping Result Details")
|
|
40
|
+
|
|
41
|
+
htmlfile.write("<table>" + lineend)
|
|
42
|
+
htmlfile.write(
|
|
43
|
+
"<tr><th>BOM Component</th><th>Mapping Result</th><th>Matching Component</th></tr>"
|
|
44
|
+
+ lineend
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
full_match = True
|
|
48
|
+
for mapresult in details:
|
|
49
|
+
versiontext = mapresult["BomItem"]["Name"]
|
|
50
|
+
if "Version" in mapresult["BomItem"]:
|
|
51
|
+
versiontext = versiontext + ", " + mapresult["BomItem"]["Version"]
|
|
52
|
+
|
|
53
|
+
color = "black"
|
|
54
|
+
if (mapresult["Result"] == MapResult.INVALID) or (
|
|
55
|
+
mapresult["Result"] == MapResult.NO_MATCH
|
|
56
|
+
):
|
|
57
|
+
color = "red"
|
|
58
|
+
full_match = False
|
|
59
|
+
else:
|
|
60
|
+
if MapBom.is_good_match(mapresult["Result"]):
|
|
61
|
+
color = "blue"
|
|
62
|
+
else:
|
|
63
|
+
color = "orange"
|
|
64
|
+
full_match = False
|
|
65
|
+
|
|
66
|
+
htmlfile.write("<tr>" + lineend)
|
|
67
|
+
|
|
68
|
+
htmlfile.write("<td>")
|
|
69
|
+
htmlfile.write('<span style="color:' + color + ';">')
|
|
70
|
+
htmlfile.write(versiontext)
|
|
71
|
+
htmlfile.write("</span>")
|
|
72
|
+
htmlfile.write("</td>" + lineend)
|
|
73
|
+
|
|
74
|
+
htmlfile.write("<td>")
|
|
75
|
+
htmlfile.write('<span style="color:' + color + ';">')
|
|
76
|
+
htmlfile.write(
|
|
77
|
+
MapResult.map_code_to_string(mapresult["Result"])
|
|
78
|
+
+ " ("
|
|
79
|
+
+ str(mapresult["Result"])
|
|
80
|
+
+ ")"
|
|
81
|
+
)
|
|
82
|
+
htmlfile.write("</span>")
|
|
83
|
+
htmlfile.write("</td>" + lineend)
|
|
84
|
+
|
|
85
|
+
htmlfile.write("<td>")
|
|
86
|
+
htmlfile.write('<span style="color:' + color + ';">')
|
|
87
|
+
if (mapresult["Result"] == MapResult.INVALID) or (
|
|
88
|
+
mapresult["Result"] == MapResult.NO_MATCH
|
|
89
|
+
):
|
|
90
|
+
htmlfile.write("(none)")
|
|
91
|
+
else:
|
|
92
|
+
for matchitem in mapresult["Matches"]:
|
|
93
|
+
htmlfile.write(matchitem["Name"] + ", " + matchitem["Version"])
|
|
94
|
+
htmlfile.write("<br/>")
|
|
95
|
+
|
|
96
|
+
if "Sw360Id" in matchitem:
|
|
97
|
+
id = matchitem["Sw360Id"]
|
|
98
|
+
else:
|
|
99
|
+
id = matchitem["Id"]
|
|
100
|
+
|
|
101
|
+
# htmlfile.write("Sw360Id = " + id)
|
|
102
|
+
htmlfile.write(
|
|
103
|
+
'<a href="'
|
|
104
|
+
+ self.RELEASE_URL
|
|
105
|
+
+ id
|
|
106
|
+
+ '" target="_blank">'
|
|
107
|
+
+ id
|
|
108
|
+
+ "</a>"
|
|
109
|
+
)
|
|
110
|
+
htmlfile.write("<br/>")
|
|
111
|
+
mid = ""
|
|
112
|
+
if "RepositoryType" in matchitem:
|
|
113
|
+
mid = matchitem["RepositoryType"]
|
|
114
|
+
if "RepositoryId" in matchitem:
|
|
115
|
+
mid = mid + " = " + matchitem["RepositoryId"]
|
|
116
|
+
|
|
117
|
+
if mid:
|
|
118
|
+
htmlfile.write(mid)
|
|
119
|
+
htmlfile.write("<br/>")
|
|
120
|
+
|
|
121
|
+
htmlfile.write("<br/>")
|
|
122
|
+
|
|
123
|
+
htmlfile.write("</span>")
|
|
124
|
+
htmlfile.write("</td>" + lineend)
|
|
125
|
+
|
|
126
|
+
htmlfile.write("</tr>" + lineend)
|
|
127
|
+
|
|
128
|
+
htmlfile.write("</table>" + lineend)
|
|
129
|
+
|
|
130
|
+
htmlfile.write("<p>")
|
|
131
|
+
htmlfile.write("Overall Result: <span")
|
|
132
|
+
if full_match:
|
|
133
|
+
htmlfile.write(' style="color:blue;">COMPLETE')
|
|
134
|
+
else:
|
|
135
|
+
htmlfile.write(' style="color:red;">INCOMPLETE')
|
|
136
|
+
htmlfile.write("</span></p>" + lineend)
|
|
137
|
+
|
|
138
|
+
myhtml.end_body_and_finish(htmlfile)
|
|
139
|
+
|
|
140
|
+
def run(self, args):
|
|
141
|
+
"""Main method()"""
|
|
142
|
+
if args.debug:
|
|
143
|
+
global LOG
|
|
144
|
+
LOG = capycli.get_logger(__name__)
|
|
145
|
+
|
|
146
|
+
print_text(
|
|
147
|
+
"\n" + capycli.APP_NAME + ", " + capycli.get_app_version() +
|
|
148
|
+
" - Create a HTML page showing the mapping result\n")
|
|
149
|
+
|
|
150
|
+
if args.help:
|
|
151
|
+
print("usage: CaPyCli mapping tohtml -i <mapping_result.json> -o <mapping_result.html>")
|
|
152
|
+
print("")
|
|
153
|
+
print("optional arguments:")
|
|
154
|
+
print("-h, --help show this help message and exit")
|
|
155
|
+
print("-i INPUTFILE, input mapping result JSON file to read from")
|
|
156
|
+
print("-o OUTPUTFILE, output HTML file to write to")
|
|
157
|
+
print("")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if not args.inputfile:
|
|
161
|
+
print_red("No input file specified!")
|
|
162
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
163
|
+
|
|
164
|
+
if not os.path.isfile(args.inputfile):
|
|
165
|
+
print_red("Input file not found!")
|
|
166
|
+
sys.exit(ResultCode.RESULT_FILE_NOT_FOUND)
|
|
167
|
+
|
|
168
|
+
if not args.outputfile:
|
|
169
|
+
print_red("No output file specified!")
|
|
170
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
171
|
+
|
|
172
|
+
print_text("Loading mapping result " + args.inputfile)
|
|
173
|
+
try:
|
|
174
|
+
mapping_result = capycli.common.json_support.load_json_file(args.inputfile)
|
|
175
|
+
except Exception as ex:
|
|
176
|
+
print_red("Error reading input file: " + repr(ex))
|
|
177
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
178
|
+
|
|
179
|
+
print_text("Creating HTML page " + args.outputfile)
|
|
180
|
+
self.mapping_result_to_html(mapping_result, args.outputfile)
|
|
181
|
+
|
|
182
|
+
print()
|