Source code for git_toggle

#!/usr/bin/env python3
#
#  __init__.py
"""
Toggle Git remotes between https and ssh.
"""
#
#  Copyright © 2020-2021 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.
#

# stdlib
import re
from typing import Optional, Union

# 3rd party
import attr
import dulwich.repo
import southwark.repo
from apeye_core import URL
from domdf_python_tools.typing import PathLike
from typing_extensions import Literal

__author__: str = "Dominic Davis-Foster"
__copyright__: str = "2020 Dominic Davis-Foster"
__license__: str = "MIT License"
__version__: str = "0.1.1"
__email__: str = "dominic@davis-foster.co.uk"

__all__ = ["Remote", "Toggler"]


[docs]class Toggler(southwark.repo.Repo): """ Toggle Git remotes between https and ssh. :param repo: The repository to toggle remotes for. """ def __init__(self, repo: Union[dulwich.repo.Repo, PathLike], *args, **kwargs): if isinstance(repo, dulwich.repo.Repo): super().__init__(repo.path) else: super().__init__(repo)
[docs] def get_current_remote(self, name: str = "origin") -> str: """ Return the current remote. :param name: If given, try to retrieve the remote with that name. If no such remote exists returns the ``origin`` remote. If no remote can be found an empty string will be returned. """ remotes = self.list_remotes() if name in remotes: return remotes[name] elif "origin" in remotes: return remotes["origin"] else: return ''
[docs] def set_current_remote(self, url: Union[str, URL, "Remote"], name: str = "origin") -> None: """ Set the current remote. :param url: The URL to set for the remote. :param name: The name of the remote to set. """ if isinstance(url, Remote): url = url.as_url() config = self.get_config() config.set(("remote", name), "url", str(url).encode("UTF-8")) config.write_to_path()
[docs]@attr.s class Remote: """ Represents a remote repository. """ #: The style of remote. style: Literal["https", "ssh"] = attr.ib(converter=str, validator=attr.validators.in_(("https", "ssh"))) #: The domain of the remote. domain: str = attr.ib(converter=str) #: The repository the remote points to. repo: str = attr.ib(converter=str) #: The account on the remote server which owns the repository. username: str = attr.ib(converter=str)
[docs] def as_url(self) -> URL: """ Returns the :class:`apeye.url.URL` representation of the :class;`~.Remote`. :return: """ if self.style == "https": return URL(f"https://{self.domain}/{self.username}/{self.repo}.git") elif self.style == "ssh": return URL(f"git@{self.domain}:{self.username}/{self.repo}.git")
[docs] @classmethod def from_url(cls, url: Union[str, URL]) -> "Remote": """ Construct a :class:`~.Remote` from a url. :param url: """ url = URL(url) if re.match(r"^\s*http(s)?://", str(url)): return cls( "https", domain=url.fqdn, repo=url.path.stem, username=str(url.path.parent)[1:], ) elif re.match(r"^\s*git@", str(url)): domain = url.fqdn return cls( "ssh", domain=domain, repo=url.path.stem, username=str(url.netloc)[(len(domain) + 5):], ) else: raise ValueError(f"Unknown remote type for {url}.")
[docs] def set_username(self, username: Optional[str]): """ If ``username`` is not :py:obj:`None`, set :attr:`~.Remote.username` to that value. :param username: :return: The new value of :attr:`~.Remote.username` """ if username: self.username = username return self.username
[docs] def set_repo(self, repo: Optional[str]): """ If ``repo`` is not :py:obj:`None`, set :attr:`~.Remote.repo` to that value. :param repo: :return: The new value of :attr:`~.Remote.repo` """ if repo: self.repo = repo return self.repo