Skip to content

Commit

Permalink
Merge pull request #60 from sw360/59-feature-force-error-code-for-pro…
Browse files Browse the repository at this point in the history
…ject-prerequisites

59 feature force error code for project prerequisites
  • Loading branch information
tngraf committed Apr 5, 2024
2 parents 6cbe2d1 + d9a3ab6 commit e67ab4e
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 7 deletions.
8 changes: 8 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

# CaPyCli - Clearing Automation Python Command Line Tool for SW360

## 2.3.0

* Have an updated granularity list.
* New feature that adds a flag `force error` to `project prerequisites` to exit the application
with an error code in case of a failed prerequisites check.
* The flag `force error` is also available for `project getlicenseinfo` and results in an error
code if a CLI file is missing.

## 2.2.1 (2024-03-08)

* Update dependencies, especially use sw360, version 1.4.1. to fix a problem in `project update`.
Expand Down
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Options:
-if INPUTFORMAT Specify input file format
-of OUTPUTFORMAT Specify output file format
-X DEBUG Enable debug output
--forceerror FORCE_ERROR force an error exit code in case of visual errors
```

## Use Cases
Expand Down
10 changes: 9 additions & 1 deletion capycli/main/options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -------------------------------------------------------------------------------
# Copyright (c) 2019-23 Siemens
# Copyright (c) 2019-24 Siemens
# All Rights Reserved.
# Author: thomas.graf@siemens.com
#
Expand Down Expand Up @@ -392,6 +392,14 @@ def register_options(self) -> None:
dest="debug", action="store_true",
help="Enable debug output")

# used by CheckPrerequisites
self.parser.add_argument(
"--forceerror",
dest="force_error",
action="store_true",
help="force an error exit code in case of visual errors",
)

def read_config(self, filename: str = "", config_string: str = "") -> Dict[str, Any]:
"""
Read configuration from string or config file.
Expand Down
4 changes: 3 additions & 1 deletion capycli/main/result_codes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -------------------------------------------------------------------------------
# Copyright 2023 Siemens
# Copyright 2023-2024 Siemens
# All Rights Reserved.
# Author: thomas.graf@siemens.com
#
Expand Down Expand Up @@ -39,3 +39,5 @@ class ResultCode(object):
RESULT_PROJECT_NOT_FOUND = 94
RESULT_ERROR_ACCESSING_SW360 = 95
RESULT_FILTER_ERROR = 96
RESULT_PREREQUISITE_ERROR = 97
RESULT_LICENSE_INFO_ERROR = 98
16 changes: 13 additions & 3 deletions capycli/project/check_prerequisites.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -------------------------------------------------------------------------------
# Copyright (c) 2019-23 Siemens
# Copyright (c) 2019-24 Siemens
# All Rights Reserved.
# Author: thomas.graf@siemens.com
#
Expand Down Expand Up @@ -98,7 +98,7 @@ def any_sw360id_in_bom(self, sbom: Bom) -> bool:

return False

def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> bool:
if not self.client:
print_red(" No client!")
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
Expand All @@ -113,16 +113,19 @@ def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
print_red("Error retrieving project details")
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)

has_errors = False
print_text(" Project name: " + project["name"] + ", " + project["version"])
print_text(" Clearing state: " + project.get("clearingState", "UNKNOWN"))

if not project.get("projectOwner", None):
print_yellow(" No project owner specified!")
has_errors = True
else:
print_green(" Project owner: " + project["projectOwner"])

if not project.get("projectResponsible", None):
print_yellow(" No project responsible specified!")
has_errors = True
else:
print_green(
" Project responsible: "
Expand Down Expand Up @@ -157,6 +160,7 @@ def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
release = self.client.get_release_by_url(href)
if not release:
print_red("Error accessign release " + href)
has_errors = True
continue

state = self.get_clearing_state(project, href)
Expand Down Expand Up @@ -185,6 +189,7 @@ def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
cx_comp, CycloneDxSupport.CDX_PROP_SW360ID) == release_id]
if len(bom_item) == 0:
print_red(" Item not in specified SBOM!")
has_errors = True
else:
assert len(bom_item) == 1
bom_sha1 = CycloneDxSupport.get_source_file_hash(bom_item[0])
Expand Down Expand Up @@ -213,6 +218,7 @@ def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
if len(source) != 1:
if state == "OPEN":
print(Fore.LIGHTRED_EX, end="")
has_errors = True
else:
print(Fore.LIGHTYELLOW_EX, end="")
else:
Expand Down Expand Up @@ -250,6 +256,8 @@ def check_project_prerequisites(self, id: str, sbom: Optional[Bom]) -> None:
else:
print_text(" No linked releases")

return has_errors

def run(self, args: Any) -> None:
"""Main method()"""
if args.debug:
Expand Down Expand Up @@ -280,6 +288,7 @@ def run(self, args: Any) -> None:
print(" -t SW360_TOKEN, use this token for access to SW360")
print(" -oa, --oauth2 this is an oauth2 token")
print(" -url SW360_URL use this URL for access to SW360")
print(" --forceerror force an error exit code in case of prerequisite errors")
return

if not self.login(token=args.sw360_token, url=args.sw360_url, oauth2=args.oauth2):
Expand Down Expand Up @@ -313,7 +322,8 @@ def run(self, args: Any) -> None:
# find_project() is part of script_base.py
pid = self.find_project(name, version)
if pid:
self.check_project_prerequisites(pid, sbom)
if (self.check_project_prerequisites(pid, sbom) and args.force_error):
sys.exit(ResultCode.RESULT_PREREQUISITE_ERROR)
else:
print_yellow(" No matching project found")
else:
Expand Down
2 changes: 1 addition & 1 deletion capycli/project/create_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def update_project(self, project_id: str, project: Optional[Dict[str, Any]],
print_red(" You are not authorized - do you have a valid write token?")
sys.exit(ResultCode.RESULT_AUTH_ERROR)
if swex.response:
print_red(" " + swex.response.status_code + ": " + swex.response.text)
print_red(" " + str(swex.response.status_code) + ": " + swex.response.text)
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
if swex.details:
print_red(" " + swex.details.get("error", "") + ": " + swex.details.get("message", ""))
Expand Down
12 changes: 12 additions & 0 deletions capycli/project/get_license_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@


class GetLicenseInfo(capycli.common.script_base.ScriptBase):
def __init__(self) -> None:
self.has_error = False

"""
Get license info on all project components.
"""
Expand Down Expand Up @@ -131,6 +134,7 @@ def get_project_info(
release = self.client.get_release_by_url(href)
if not release:
print_red(" ERROR: unable to access release")
self.has_error = True
continue

component_name = release["name"]
Expand Down Expand Up @@ -166,6 +170,10 @@ def get_project_info(

complist.append(comp)

if len(cli_files) == 0:
print_red(" No CLI file exist for this component!")
self.has_error = True

complist.sort(key=lambda s: s["ComponentName"].lower())

rdm_info["Components"] = complist
Expand All @@ -188,6 +196,7 @@ def show_command_help(self) -> None:
-ncli, --no-overwrite-cli do not overwrite existing CLI files
-nconf, --no-overwrite-config do not overwrite an existing configuration file
-all add all available CLI files of a component
--forceerror force an error exit code in case of missing information
""")

print()
Expand Down Expand Up @@ -260,3 +269,6 @@ def run(self, args: Any) -> None:
self.write_result(rdm_info, args.outputfile, args.nconf)

print_text("\ndone.")

if args.force_error and self.has_error:
sys.exit(ResultCode.RESULT_LICENSE_INFO_ERROR)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

[tool.poetry]
name = "capycli"
version = "2.2.1"
version = "2.3.0"
description = "CaPyCli - Clearing Automation Python Command Line Interface for SW360"
authors = ["Thomas Graf <thomas.graf@siemens.com>"]
license = "MIT"
Expand Down
1 change: 1 addition & 0 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(self) -> None:
self.remote_granularity_list: str = ""
self.local_granularity_list: str = ""
self.github_token: str = ""
self.force_error: bool = False


class TestBasePytest:
Expand Down

0 comments on commit e67ab4e

Please sign in to comment.