From e67849e81e5ec4fbdd0001817a76e94a6a4d08bb Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Mon, 27 May 2024 08:05:17 -0500 Subject: [PATCH] refactor: validate repos with git commands & improve readability --- .../infrastructure/package_git_repository.py | 1 + .../infrastructure/git_package_repository.py | 72 +++++++++++++++++-- .../distro/share/domain/cloud_package.py | 30 +++----- .../infraestructure/theme_git_repository.py | 1 + 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/tutordistro/distro/packages/infrastructure/package_git_repository.py b/tutordistro/distro/packages/infrastructure/package_git_repository.py index e7e6442..d779cd6 100644 --- a/tutordistro/distro/packages/infrastructure/package_git_repository.py +++ b/tutordistro/distro/packages/infrastructure/package_git_repository.py @@ -52,6 +52,7 @@ def clone(self, package: Package, path: str) -> None: package (Package): The package to be cloned. path (str): The destination path for cloning the package. """ + repo = None if "https" == package.extra["protocol"]: repo = ( f"https://{package.domain}/" diff --git a/tutordistro/distro/repository_validator/infrastructure/git_package_repository.py b/tutordistro/distro/repository_validator/infrastructure/git_package_repository.py index 751bb6a..dfb5ffd 100644 --- a/tutordistro/distro/repository_validator/infrastructure/git_package_repository.py +++ b/tutordistro/distro/repository_validator/infrastructure/git_package_repository.py @@ -5,7 +5,8 @@ """ -import requests +import subprocess +from typing import Optional from tutordistro.distro.share.domain.cloud_package import CloudPackage from tutordistro.distro.share.domain.cloud_package_repository import CloudPackageRepository @@ -16,10 +17,69 @@ class GitPackageRepository(CloudPackageRepository): """ Repository class for validating CloudPackages using a Git repository. - It inherits from CloudPackageRepository and provides the implementation for - the validation method. + This class inherits from CloudPackageRepository and provides the implementation for + the validation method. It verifies the existence of the repository and checks + if the specified branch or tag exists. """ def validate(self, package: CloudPackage) -> None: - response = requests.get(package.to_url(), timeout=5) - if response.status_code != 200: - raise PackageDoesNotExist(f"The package {package.name} or branch doesn't exist or is private") + package_url = package.to_url() + repo_url, version_name = self._parse_package_url(package_url) + + self._verify_repository_exists(repo_url) + if version_name: + self._verify_version_exists(repo_url, version_name) + + def _parse_package_url(self, package_url: str) -> tuple[str, Optional[str]]: + """ + Parse the package URL to extract the repository URL and the version name. + + Args: + package_url (str): The full URL of the package. + + Returns: + tuple: A tuple containing the repository URL and the version name (branch/tag). + """ + split_url = package_url.split('/tree/') + repo_url = split_url[0] + version_name = split_url[1] if len(split_url) > 1 else None + return repo_url, version_name + + def _verify_repository_exists(self, repo_url: str) -> None: + """ + Verify that the repository exists. + + Args: + repo_url (str): The URL of the repository. + + Raises: + PackageDoesNotExist: If the repository does not exist or is private. + """ + result = subprocess.run( + ['git', 'ls-remote', repo_url], + capture_output=True, text=True, check=False + ) + if result.returncode != 0: + raise PackageDoesNotExist(f'The package "{repo_url}" does not exist or is private') + + def _verify_version_exists(self, repo_url: str, version_name: str) -> None: + """ + Verify that the branch or tag exists in the repository. + + Args: + repo_url (str): The URL of the repository. + version_name (str): The branch or tag name to verify. + + Raises: + PackageDoesNotExist: If neither the branch nor the tag exists. + """ + branch_result = subprocess.run( + ['git', 'ls-remote', '--heads', repo_url, version_name], + capture_output=True, text=True, check=False + ) + tag_result = subprocess.run( + ['git', 'ls-remote', '--tags', repo_url, version_name], + capture_output=True, text=True, check=False + ) + + if not branch_result.stdout and not tag_result.stdout: + raise PackageDoesNotExist(f'Neither branch nor tag "{version_name}" exists on "{repo_url}"') diff --git a/tutordistro/distro/share/domain/cloud_package.py b/tutordistro/distro/share/domain/cloud_package.py index a7ae4f8..a7215e3 100644 --- a/tutordistro/distro/share/domain/cloud_package.py +++ b/tutordistro/distro/share/domain/cloud_package.py @@ -11,7 +11,6 @@ from urllib.parse import urlparse from tutordistro.distro.share.domain.package import Package -from tutordistro.distro.share.domain.package_does_not_exist import PackageDoesNotExist class CloudPackage: @@ -50,36 +49,29 @@ def is_valid_requirement(url) -> bool: @staticmethod def __parse_url(url) -> CloudPackage: - version: str = "" + version: str = '' pattern = r"git\+(https?://\S+?)(?:#|$)" - result = re.search(pattern, url) - url = result.group(1).replace('@', '/tree/').replace('.git', '') + found_package_url = re.search(pattern, url).group(1) + github_url = found_package_url.replace('@', '/tree/').replace('.git', '') - parsed_url = urlparse(url) + parsed_url = urlparse(github_url) + split_path = parsed_url.path.split('/') protocol = parsed_url.scheme domain = parsed_url.netloc - path = parsed_url.path - partes_path = path.split('/') - name = partes_path[2] - - if len(partes_path) > 5: - raise PackageDoesNotExist(f"The package {url} or branch doesn't exist or is private") - - if '/tree/' in url: - version = partes_path[-1] - if len(partes_path) > 3 and '/tree/' not in url: - raise PackageDoesNotExist(f"The package {url} or branch doesn't exist or is private") + org_path = split_path[1] + package_name = split_path[2] - path = partes_path[1] + if '/tree/' in github_url: + version = github_url.split('/tree/')[-1] # This is the branch name or tag return CloudPackage( domain=domain, - name=name, + name=package_name, version=version, protocol=protocol, - path=path + path=org_path ) @staticmethod diff --git a/tutordistro/distro/themes/infraestructure/theme_git_repository.py b/tutordistro/distro/themes/infraestructure/theme_git_repository.py index 24678b4..a06c9d2 100644 --- a/tutordistro/distro/themes/infraestructure/theme_git_repository.py +++ b/tutordistro/distro/themes/infraestructure/theme_git_repository.py @@ -32,6 +32,7 @@ def clone(self, theme_settings: type(ThemeSettings)): Args: theme_settings (ThemeSettings): Theme settings. """ + repo = None if "https" == theme_settings.settings["protocol"]: repo = ( f"https://{theme_settings.settings['domain']}/"