diff --git a/vollerei/constants.py b/vollerei/constants.py index f5f4218..2495a7f 100644 --- a/vollerei/constants.py +++ b/vollerei/constants.py @@ -22,3 +22,5 @@ tools_cache_path = base_dirs.site_cache_path.joinpath("tools") tools_cache_path.mkdir(parents=True, exist_ok=True) launcher_cache_path = base_dirs.site_cache_path.joinpath("launcher") launcher_cache_path.mkdir(parents=True, exist_ok=True) +utils_cache_path = base_dirs.site_cache_path.joinpath("utils") +utils_cache_path.mkdir(parents=True, exist_ok=True) \ No newline at end of file diff --git a/vollerei/utils/git/__init__.py b/vollerei/utils/git/__init__.py index 60b34c1..a8f87b6 100644 --- a/vollerei/utils/git/__init__.py +++ b/vollerei/utils/git/__init__.py @@ -1,35 +1,99 @@ import subprocess +import requests +import json from pathlib import Path -from shutil import which -from vollerei.utils.git.exceptions import GitCloneError, GitNotInstalled +from zipfile import ZipFile +from io import BytesIO +from shutil import which, rmtree +from urllib.parse import urlparse +from vollerei.constants import utils_cache_path +from vollerei.utils.git.exceptions import GitCloneError class Git: """ - Quick wrapper around git binary + Quick wrapper around git binary (or simulate git if git is not installed) + + Simulate git because Windows users may not have git installed. """ def __init__(self) -> None: - pass + self._cache = utils_cache_path.joinpath("git") + self._cache.mkdir(parents=True, exist_ok=True) @staticmethod - def check_git(): + def is_installed(): """ Check for git installation, if not found raise GitNotInstalled """ if not which("git"): - raise GitNotInstalled("git is not installed") + return False + return True + + def _is_gitea(self, netloc: str) -> bool: + """ + Check if the url is a Gitea server + """ + rsp = requests.get(f"https://{netloc}/api/v1/meta") + try: + data: dict = rsp.json() + except json.JSONDecodeError: + return False + if not data.get("version"): + return False + return True + + def _clone(self, url: str, path: str = None) -> None: + """ + "Clone" a git repository without git + """ + if Path(url).suffix == ".git": + url = url[:-4] + url_info = urlparse(url) + netloc = url_info.netloc + if path is None: + path = Path.cwd().joinpath(Path(url).stem) + if self._is_gitea(netloc): + # Hardcoding the branch to master, because well :D + file = BytesIO() + rsp = requests.get( + f"https://{netloc}/api/v1/repos/{url_info.path}/archive/master.zip", + stream=True, + ) + rsp.raise_for_status() + with open(file, "wb") as f: + for chunk in rsp.iter_content(chunk_size=32768): + f.write(chunk) + zip_file = ZipFile(file) + zip_file.extractall(path) + with Path(path).joinpath(".git/PLEASE_INSTALL_GIT").open("w") as f: + f.write(json.dumps({"type": "gitea", "netloc": netloc, "path": url_info.path})) + else: + raise NotImplementedError def pull_or_clone(self, url: str, path: str = None) -> None: - self.check_git() """ Pulls or clones a git repository If the repository already exists and the url matches, it'll be pulled. """ + if not self.is_installed(): + # Git is not installed, we need to simulate it + self._clone(url, path) + return if path is None: - path = Path.cwd().joinpath(Path(url).stem) + if Path(url).suffix == ".git": + path = Path.cwd().joinpath(Path(url).stem) + else: + path = Path.cwd().joinpath(Path(url).name) + path_as_path = Path(path) + if path_as_path.joinpath(".git/PLEASE_INSTALL_GIT").exists(): + # This is a fake .git directory we created. + # We need to clone the repository + rmtree(path) try: + if not path_as_path.exists(): + raise subprocess.CalledProcessError origin_url = subprocess.check_output( ["git", "config", "--get", "remote.origin.url"], cwd=path ).decode() @@ -37,6 +101,8 @@ class Git: raise subprocess.CalledProcessError subprocess.check_call(["git", "pull"], cwd=path) except subprocess.CalledProcessError: + if path_as_path.exists(): + rmtree(path) try: subprocess.check_call(["git", "clone", url, path]) except subprocess.CalledProcessError as e: