diff --git a/Documents/bin/git-namespace-backup b/Documents/bin/git-namespace-backup index 61e33774b3f50cb78c9017cf8dbd0278a8fca4e6..4a3186f58679fce8a1f1d2cef87d801514ed3b4a 100755 --- a/Documents/bin/git-namespace-backup +++ b/Documents/bin/git-namespace-backup @@ -14,11 +14,14 @@ import pathlib import sys import os import os.path -import re -import gitlab # pylint: disable=import-error import sh # pylint: disable=import-error from sh.contrib import git # pylint: disable=import-error +sys.path.append(os.path.expanduser("~/Documents/bin")) + +import rcfiles.git # noqa: E402 pylint: disable=wrong-import-position +import rcfiles.gitlab # noqa: E402 pylint: disable=wrong-import-position + def namespace_path(namespace): """Returns the full path to the namespace. @@ -35,92 +38,55 @@ def namespace_path(namespace): return path -def name_to_path(name): - """Converts a name to valid path in GitLab.""" - return re.sub("[^a-zA-Z0-9_-]", "-", name).lower() +def list_repositories(namespace): + """Returns a list of paths under the namespace that are Git + repositories.""" + return [x for x in namespace.iterdir() if rcfiles.git.is_repo(x)] -def get_group(lab, name): - """Returns a GitLab group object. +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("namespace", type=namespace_path) + args = parser.parse_args() - If a group with that name already exists, return that. Otherwise create a - new group and return that instead. - """ - for i in lab.groups.list(): - if i.name == name: + with rcfiles.gitlab.connect() as conn: + group = rcfiles.gitlab.get_group(conn, args.namespace.name) + if group is None: + group = rcfiles.gitlab.create_group( + conn, args.namespace.name, visibility="internal" + ) print( - "Using existing group id: {}, name: {}, path: {}.".format( - i.id, i.name, i.path + "Created new group id: {}, name: {}, path: {}.".format( + group.id, group.name, group.path ), file=sys.stderr, ) - return i - new_group = lab.groups.create( - { - "name": name, - "path": name_to_path(name), - "visivility": "internal", - } - ) - print( - "Created new group id: {}, name: {}, path: {}.".format( - new_group.id, new_group.name, new_group.path - ), - file=sys.stderr, - ) - return group - - -def get_project(group, name): # pylint: disable=redefined-outer-name - """Returns a GitLab project. - - If a project with that name already exists, return that. Otherwise create a - new project and return that instead. - """ - for i in group.projects.list(): - if i.name == name: + else: print( - "Using existing project id: {}, name: {}, path: {}.".format( - i.id, i.name, i.path + "Using existing group id: {}, name: {}, path: {}.".format( + group.id, group.name, group.path ), file=sys.stderr, ) - return i - new_project = group.projects.gitlab.projects.create( - {"name": name, "namespace_id": group.id} - ) - print( - "Created new project id: {}, name: {}, path: {}.".format( - new_project.id, new_project.name, new_project.path - ), - file=sys.stderr, - ) - return project - - -def is_git_repo(path): - """Returns a boolean if the path is a Git repo.""" - return path.is_dir() and pathlib.Path(path, ".git").is_dir() - - -def list_repositories(namespace): - """Returns a list of paths under the namespace that are Git - repositories.""" - return [x for x in namespace.iterdir() if is_git_repo(x)] - - -TOKEN = os.environ["GITLAB_PRIVATE_TOKEN"] -URL = os.environ["GITLAB_BASE_URL"].removesuffix("api/v4") - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("namespace", type=namespace_path) - args = parser.parse_args() - - with gitlab.Gitlab(url=URL, private_token=TOKEN) as Lab: - group = get_group(Lab, args.namespace.name) for repo in list_repositories(args.namespace): - project = get_project(group, repo.name) + project = rcfiles.gitlab.get_project(conn, group.name, repo.name) + if project is None: + project = rcfiles.gitlab.create_project( + conn, repo.name, group.name + ) + print( + "Created new project id: {}, name: {}, path: {}.".format( + project.id, project.name, project.path + ), + file=sys.stderr, + ) + else: + print( + "Using existing project id: {}, name: {}, path: {}.".format( # noqa: E501 + project.id, project.name, project.path + ), + file=sys.stderr, + ) with sh.pushd(repo): if "shore.co.il" in git.remote().splitlines(): print( diff --git a/Documents/bin/rcfiles/git.py b/Documents/bin/rcfiles/git.py new file mode 100644 index 0000000000000000000000000000000000000000..ff92a94c84c7430e53cbaa83214b371976771f64 --- /dev/null +++ b/Documents/bin/rcfiles/git.py @@ -0,0 +1,20 @@ +"""Git repository related functions.""" + +import os.path +import pathlib + + +def is_repo(path): + """Returns a boolean if the path is a Git repo.""" + return os.path.isdir(path) and pathlib.Path(path, ".git").is_dir() + + +def in_repo(): + """Is the current working directory a git repo? + + Because we invoke the command as a Git command (git foo) it is run from + the root of the repository if inside a repository so there's no need to + traverse up the directory hierarchy to find if we're in a Git repository, + it's enough to just check if the .git directory exists where we are. + """ + return is_repo(".") diff --git a/Documents/bin/rcfiles/gitlab.py b/Documents/bin/rcfiles/gitlab.py new file mode 100644 index 0000000000000000000000000000000000000000..f7ba6d9ad59be49704665c863e423c5c8938dcb8 --- /dev/null +++ b/Documents/bin/rcfiles/gitlab.py @@ -0,0 +1,79 @@ +"""A bunch of convenience functions to deal with GitLab.""" + +import os +import re +import gitlab # pylint: disable=import-error + + +def name_to_path(name): + """Converts a name to valid path in GitLab.""" + return re.sub("[^a-zA-Z0-9_-]", "-", name).lower() + + +def get_url(): + """Return the GitLab URL.""" + return os.getenv( + "GITLAB_BASE_URL", "https://git.shore.co.il/" + ).removesuffix("api/v4") + + +def connect(): + """Return the GitLab object.""" + try: + token = os.environ["GITLAB_TOKEN"] + except KeyError: + raise Exception("GITLAB_TOKEN environment variable not set.") + url = get_url() + conn = gitlab.Gitlab(url=url, private_token=token) + conn.auth() + return conn + + +def get_group(conn, name): + """Return the GitLab group object with the given name.""" + for group in conn.groups.list(all=True): + if group.name == name: + return group + return None + + +def create_group(conn, name, visibility=None, description=None): + """Create a new GitLab group and return that object.""" + data = { + "name": name, + "path": name_to_path(name), + "visibility": "public" if visibility is None else visibility, + } + if description is not None: + data["description"] = description + return conn.groups.create(data) + + +def get_project(conn, group, name): + """Returns a GitLab project.""" + # pylint: disable=invalid-name + g = get_group(conn, group) + if g is None: + return None + for p in g.projects.list(all=True): + if p.name == name: + return p + return None + + +def create_project(conn, name, group=None, description=None, visibility=None): + """Create a new GitLab project and return that object.""" + # pylint: disable=invalid-name + data = { + "name": name, + } + if group is not None: + g = get_group(conn, group) + if g is None: + return None + data["namespace_id"] = g.id + if description is not None: + data["description"] = description + if visibility is not None: + data["visibility"] = visibility + return conn.projects.create(data)