Skip to content
Snippets Groups Projects
Commit 5909705e authored by nimrod's avatar nimrod
Browse files

Git namespace backup.

Script to backup a directory under ~/Repositories to my GitLab instance.
parent ac4d857a
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3
# pylint: disable=invalid-name
"""Backup a Git repositories namespace to my GitLab instace.
Which means creating a namespace in GitLab, creating a project for every
repository in the namespace, adding a remote to the repository and pushing
everything to that project.
In this context, a namespace is a directory under ~/Repositories.
"""
import argparse
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
def namespace_path(namespace):
"""Returns the full path to the namespace.
Raises an exception if the path doesn't exits or isn't a directory.
"""
path = pathlib.Path(os.path.expanduser("~/Repositories/" + namespace))
if not path.exists():
raise argparse.ArgumentTypeError("Path {} does not exit.".format(path))
if not path.is_dir():
raise argparse.ArgumentTypeError(
"Path {} is not a directory.".format(path)
)
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 get_group(lab, name):
"""Returns a GitLab group object.
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:
print(
"Using existing group id: {}, name: {}, path: {}.".format(
i.id, i.name, i.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:
print(
"Using existing project id: {}, name: {}, path: {}.".format(
i.id, i.name, i.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)
with sh.pushd(repo):
if "shore.co.il" in git.remote().splitlines():
print(
"Setting the remote URL in {}.".format(repo.name),
file=sys.stderr,
)
git.remote(
"set-url", "shore.co.il", project.ssh_url_to_repo
)
else:
print(
"Adding remote in {}.".format(repo.name),
file=sys.stderr,
)
git.remote("add", "shore.co.il", project.ssh_url_to_repo)
print(
"Pushing to {}.".format(project.ssh_url_to_repo),
file=sys.stderr,
)
git.push("--all", "shore.co.il")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment