git-batch/gitbatch/Cli.py

111 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Main program."""
import argparse
import os
from collections import defaultdict
from urllib.parse import urlparse
import git
from gitbatch import __version__
from gitbatch.Logging import SingleLog
from gitbatch.Utils import normalize_path
from gitbatch.Utils import to_bool
class GitBatch:
"""Handles git operations."""
def __init__(self):
self.log = SingleLog()
self.logger = self.log.logger
self.args = self._cli_args()
self.config = self._config()
self.run()
def _cli_args(self):
parser = argparse.ArgumentParser(
description=("Clone single branch from all repositories listed in a file")
)
parser.add_argument(
"--version", action="version", version="%(prog)s {}".format(__version__)
)
return parser.parse_args()
def _config(self):
config = defaultdict(dict)
input_file_raw = os.environ.get("GIT_BATCH_INPUT_FILE", ".batchfile")
config["input_file"] = normalize_path(input_file_raw)
config["ignore_existing"] = to_bool(os.environ.get("GIT_BATCH_IGNORE_EXISTING_REPO", True))
config["ignore_missing"] = to_bool(os.environ.get("GIT_BATCH_IGNORE_MISSING_REMOTE", True))
return config
def _repos_from_file(self, src):
repos = []
with open(src, "r") as f:
for num, line in enumerate(f, start=1):
repo = {}
line = line.strip()
if line and not line.startswith("#"):
try:
url, branch, dest = [x.strip() for x in line.split(";")]
except ValueError as e:
self.log.sysexit_with_message(
"Wrong numer of delimiters in line {line_num}: {exp}".format(
line_num=num, exp=e
)
)
if url:
url_parts = urlparse(url)
repo["url"] = url
repo["branch"] = branch or "master"
repo["name"] = os.path.basename(url_parts.path)
repo["dest"] = normalize_path(dest) or normalize_path(
"./{}".format(repo["name"])
)
repos.append(repo)
else:
self.log.sysexit_with_message(
"Repository Url is not set on line {line_num}".format(line_num=num)
)
return repos
def _repos_clone(self, repos, ignore_existing):
for repo in repos:
try:
options = ["--branch={}".format(repo["branch"]), "--single-branch"]
git.Repo.clone_from(repo["url"], repo["dest"], multi_options=options)
except git.exc.GitCommandError as e:
passed = False
err_raw = e.stderr.strip().splitlines()[:-1]
err = [x.split(":", 1)[1].strip() for x in err_raw]
if any(["already exists and is not an empty directory" in item for item in err]):
if self.config["ignore_existing"]:
passed = True
if any(["Could not find remote branch" in item for item in err]):
if self.config["ignore_missing"]:
passed = True
if not passed:
self.log.sysexit_with_message("Git error: " + "\n".join(err))
def run(self):
if os.path.isfile(self.config["input_file"]):
repos = self._repos_from_file(self.config["input_file"])
self._repos_clone(repos, self.config["ignore_existing"])
else:
self.log.sysexit_with_message(
"The given batch file at '{}' does not exist".format(
os.path.relpath(os.path.join("./", self.config["input_file"]))
)
)