diff --git a/capycli/bom/findsources.py b/capycli/bom/findsources.py index 92c82ba..aff6900 100644 --- a/capycli/bom/findsources.py +++ b/capycli/bom/findsources.py @@ -20,6 +20,7 @@ from cyclonedx.model import ExternalReferenceType from cyclonedx.model.bom import Bom from sw360 import SW360Error +from cyclonedx.model.component import Component import capycli.common.script_base from capycli import get_logger @@ -41,6 +42,7 @@ def __init__(self): self.github_project_name_regex = re.compile(r"^[a-zA-Z0-9-]+(/[a-zA-Z0-9-]+)*$") self.github_name = None self.github_token = None + self.sw360_url = os.environ.get("SW360ServerUrl", None) def is_sourcefile_accessible(self, sourcefile_url: str) -> bool: """Check if the URL is accessible.""" @@ -143,7 +145,7 @@ def get_github_info(repository_url: str, username="", token="") -> Any: tags.extend(tmp) return tags - def to_semver_string(self, version) -> str: + def to_semver_string(self, version: str) -> str: """Bring all version information to a format we can compare.""" result = self.version_regex.search(version) if result is None: @@ -158,7 +160,7 @@ def to_semver_string(self, version) -> str: return str(ver + ".0") return ver - def find_github_url(self, component, use_language=True) -> str: + def find_github_url(self, component: Component, use_language=True) -> str: """ Find github url for component""" if not component: return "" @@ -202,7 +204,7 @@ def get_pkg_go_repo_url(self, name: str) -> str: Style.RESET_ALL) return link_repo - def find_golang_url(self, component) -> str: + def find_golang_url(self, component: Component) -> str: """ Find github url for component""" if not component: return "" @@ -268,7 +270,7 @@ def get_github_source_url(self, github_url: str, version: str) -> str: tag_info = self.get_github_info(repo_name, self.github_name, self.github_token) return self.get_matching_tag(tag_info, version, github_url) - def get_matching_tag(self, tag_info, version, github_url, version_prefix=None): + def get_matching_tag(self, tag_info: list, version: str, github_url: str, version_prefix=None): if not tag_info or (len(tag_info) == 0): print( Fore.LIGHTRED_EX + @@ -321,6 +323,8 @@ def get_matching_tag(self, tag_info, version, github_url, version_prefix=None): # print("matching_tag", matching_tag) source_url = matching_tag.get("zipball_url", "") + if source_url == "": + return None source_url = source_url.replace( "https://api.github.com/repos", "https://github.com").replace( "zipball/refs/tags", "archive/refs/tags") @@ -378,7 +382,7 @@ def find_source_url_from_component(self, component_id: str) -> str: return source_url - def find_source_url_on_release(self, component) -> str: + def find_source_url_on_release(self, component: Component) -> str: """find the url from sourceCodeDownloadurl from the Id or Sw360Id""" url = None release_id = "" @@ -390,7 +394,7 @@ def find_source_url_on_release(self, component) -> str: url = self.get_source_url_from_release(release_id) return url - def find_source_url_recursive_by_sw360(self, component) -> str: + def find_source_url_recursive_by_sw360(self, component: Component) -> str: """find the url via an other release of the parent component""" url = None found_by_component = False @@ -421,7 +425,7 @@ def find_source_url_recursive_by_sw360(self, component) -> str: return url @staticmethod - def find_source_url_by_language(component) -> str: + def find_source_url_by_language(component: Component) -> str: capycli.dependencies.javascript.GetJavascriptDependencies().try_find_component_metadata(component, "") return CycloneDxSupport.get_ext_ref_source_url(component) @@ -548,8 +552,11 @@ def run(self, args): print(" -h, --help show this help message and exit") print(" -i INPUTFILE SBOM file to read from (JSON)") print(" -o OUTPUTFILE output file to write to") + print(" -t SW360_TOKEN (optional) use this token for access to SW360") + print(" -oa, --oauth2 (optional) this is an oauth2 token") + print(" -url SW360_URL (optional) use this URL for access to SW360") print(" -name NAME (optional) GitHub name for login") - print(" -gt TOKEN (optional) GitHub token for login") + print(" -gt TOKEN (optional) GitHub token for login") print(" -v be verbose") return @@ -563,17 +570,18 @@ def run(self, args): self.verbose = args.verbose self.github_name = args.name - self.github_token = args.sw360_token - - if self.login( - token=args.sw360_token, url=args.sw360_url, - oauth2=args.oauth2, exit_no_login=False): + self.github_token = args.github_token + if not self.sw360_url: + self.sw360_url = args.sw360_url + + if self.sw360_url: + self.login( + token=args.sw360_token, url=self.sw360_url, + oauth2=args.oauth2) print("Using SW360 releases and components to detect GitHub url") - self.github_token = args.github_token self.use_sw360 = True else: self.use_sw360 = False - if self.verbose: if self.github_name and self.github_token: print_text("Using provided GitHub credentials") diff --git a/capycli/common/script_base.py b/capycli/common/script_base.py index 90d1a65..8f5f336 100644 --- a/capycli/common/script_base.py +++ b/capycli/common/script_base.py @@ -34,7 +34,7 @@ def __init__(self): self.project = None self.sw360_url = os.environ.get("SW360ServerUrl", None) - def login(self, token: str = "", url: str = "", oauth2: bool = False, exit_no_login: bool = True) -> bool: + def login(self, token: str = "", url: str = "", oauth2: bool = False) -> bool: """Login to SW360""" self.sw360_url = os.environ.get("SW360ServerUrl", None) sw360_api_token = os.environ.get("SW360ProductionToken", None) @@ -46,21 +46,15 @@ def login(self, token: str = "", url: str = "", oauth2: bool = False, exit_no_lo self.sw360_url = url if not self.sw360_url: - if exit_no_login: print_red(" No SW360 server URL specified!") sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360) - else: - return False if self.sw360_url[-1] != "/": self.sw360_url += "/" if not sw360_api_token: - if exit_no_login: print_red(" No SW360 API token specified!") sys.exit(ResultCode.RESULT_AUTH_ERROR) - else: - return False self.client = sw360.sw360_api.SW360(self.sw360_url, sw360_api_token, oauth2) diff --git a/tests/test_base.py b/tests/test_base.py index b03a28a..a2fb8e9 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -63,6 +63,7 @@ def __init__(self): self.outputformat = "" self.remote_granularity_list = None self.local_granularity_list = None + self.github_token = "" class TestBasePytest: diff --git a/tests/test_find_sources.py b/tests/test_find_sources.py index 9b83303..eb80c64 100644 --- a/tests/test_find_sources.py +++ b/tests/test_find_sources.py @@ -258,4 +258,71 @@ def test_find_golang_url_non_github(self): component.version = 'v1.0.0' source_url = find_sources.find_golang_url(component) - self.assertEqual(source_url, '') \ No newline at end of file + self.assertEqual(source_url, '') + + def test_no_matching_tag(self): + + validTag = "3.2.0" + invalidTag = "0.03" + emptyString = "" + githubUrl = "https://github.com/apache/kafka" + zipball_url = "https://api.github.com/repos/apache/kafka/zipball/refs/tags/" + validTag + sourceUrl = "https://github.com/apache/kafka/archive/refs/tags/" + validTag + ".zip" + findResource = capycli.bom.findsources.FindSources() + # test Empty tagInfo array + tagInfo = [] + actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl) + self.assertEqual(actual, None) + # test Empty tag string + tagInfo = [{"name": emptyString, "zipball_url": zipball_url}] + actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl) + self.assertEqual(actual, '') + # test Empty url string + tagInfo = [{"name": validTag, "zipball_url": emptyString}] + actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl) + self.assertEqual(actual, None) + # test non-matching tag + tagInfo = [{"name": invalidTag, "zipball_url": zipball_url}] + actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl) + self.assertEqual(actual, '') + # test valid tag + tagInfo = [{"name": validTag, "zipball_url": zipball_url}] + actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl) + self.assertEqual(actual, sourceUrl) + + @patch("time.sleep") + def test_get_source_url_success(self, mock_sleep): + mock_client = MagicMock() + mock_release_id = "123" + mock_source_url = "https://example.com/source.zip" + + mock_client.get_release.return_value = {"sourceCodeDownloadurl": mock_source_url} + + findsources = FindSources() + findsources.client = mock_client + result = findsources.get_source_url_from_release(mock_release_id) + self.assertEqual(result, mock_source_url) + mock_sleep.assert_not_called() + + def test_get_source_url_no_source_url(self): + mock_client = MagicMock() + mock_release_id = "123" + + mock_client.get_release.return_value = {"sourceCodeDownloadurl": ""} + findsources = FindSources() + findsources.client = mock_client + + result = findsources.get_source_url_from_release(mock_release_id) + self.assertIsNone(result) + mock_client.get_release.assert_called_once_with(mock_release_id) + + def test_get_source_url_exception(self): + mock_client = MagicMock() + mock_release_id = "123" + + mock_client.get_release.side_effect = Exception("Unexpected error") + findsources = FindSources() + findsources.client = mock_client + with self.assertRaises(Exception): + findsources.get_source_url_from_release(mock_release_id) + mock_client.get_release.assert_called_once_with(mock_release_id)