Source code for snakedeploy.providers

from abc import abstractmethod, ABC
from shutil import copytree
import shutil
from snakedeploy.exceptions import UserError
import subprocess as sp
import os


[docs] def get_provider(source_url): for provider in PROVIDERS: if provider.matches(source_url): return provider(source_url) raise UserError("No matching provider for source url %s" % source_url)
[docs] class Provider(ABC): def __init__(self, source_url): if not ( source_url.startswith("https://") or source_url.startswith("file:") or os.path.exists(source_url) ): raise UserError( "Repository source URLs must be given as https:// or file://, or exist." ) # TODO replace with removesuffix once Python 3.9 becomes the minimal version of snakedeploy if source_url.endswith(".git"): source_url = source_url[:-4] self.source_url = source_url
[docs] @classmethod @abstractmethod def matches(cls, source_url: str): ...
[docs] @abstractmethod def clone(self, path: str): ...
[docs] @abstractmethod def checkout(self, path: str, ref: str): ...
[docs] @abstractmethod def get_raw_file(self, path: str, tag: str): ...
[docs] def get_repo_name(self): return self.source_url.split("/")[-1]
[docs] class Local(Provider):
[docs] @classmethod def matches(cls, source_url: str): return os.path.exists(source_url)
[docs] def clone(self, tmpdir: str): """ A local "clone" means moving files. """ if os.path.exists(tmpdir): try: shutil.rmtree(tmpdir) except OSError as e: raise UserError(f"Failed to remove existing directory {tmpdir}: {e}") copytree(self.source_url, tmpdir)
[docs] def checkout(self, path: str, ref: str): # Local repositories don't need to check out anything pass
[docs] def get_raw_file(self, path: str, tag: str): if tag: print( "Warning: tag is not supported for a local repository - check out the branch you need." ) return f"{self.source_url}/{path}"
[docs] def get_source_file_declaration(self, path: str, tag: str, branch: str): relative_path = path.replace(self.source_url, "").strip(os.sep) return f'"{relative_path}"'
[docs] class Github(Provider):
[docs] @classmethod def matches(cls, source_url: str): return cls.__name__.lower() in source_url
@property def name(self): return self.__class__.__name__.lower()
[docs] def clone(self, path: str): """ Clone the known source URL to a temporary directory """ try: sp.run(["git", "clone", self.source_url, "."], cwd=path, check=True) except sp.CalledProcessError as e: raise UserError("Failed to clone repository {}:\n{}", self.source_url, e)
[docs] def checkout(self, path: str, ref: str): try: sp.run(["git", "checkout", ref], cwd=path, check=True) except sp.CalledProcessError as e: raise UserError("Failed to checkout ref {}:\n{}", ref, e)
[docs] def get_raw_file(self, path: str, tag: str): return f"{self.source_url}/raw/{tag}/{path}"
[docs] def get_source_file_declaration(self, path: str, tag: str, branch: str): owner_repo = "/".join(self.source_url.split("/")[-2:]) if not (tag or branch): raise UserError("Either tag or branch has to be specified for deployment.") ref_arg = f'tag="{tag}"' if tag is not None else f'branch="{branch}"' return f'{self.name}("{owner_repo}", path="{path}", {ref_arg})'
[docs] class Gitlab(Github):
[docs] def get_raw_file(self, path: str, tag: str): return f"{self.source_url}/-/raw/{tag}/{path}"
PROVIDERS = [Github, Gitlab, Local]