import logging import os.path import sys from os import makedirs from src.config import Config, read_toml_config from src.git import git_clone, git_pull, http_git_env, ssh_git_env from src.gitea_api import GiteaApi from src.log_fields import ( OPERATION, PATH, REPOSITORY, REPOSITORY_COUNT, REPOSITORY_URL, ) from src.logging_config import setup_logging from src.models import GiteaRepository from src.repository_name import get_repository_name, is_valid_repository_names BASE_PATH = "out" FORMAT = "{owner}/{name}" logger = logging.getLogger(__name__) def get_fetch_params(config: Config, repo: GiteaRepository): if config.fetch.ssh_key_path: return repo.ssh_url, ssh_git_env(config.fetch.ssh_key_path) return repo.clone_url, http_git_env( user=config.fetch.user, token=config.fetch.token, ) def process_repo(config: Config, repo: GiteaRepository): path = get_repository_name(name_format=config.repository_format, r=repo) out_path = os.path.join(config.out_dir, path) repository_url, git_env = get_fetch_params(config=config, repo=repo) log_context = { REPOSITORY: path, PATH: out_path, REPOSITORY_URL: repository_url, } makedirs(out_path, exist_ok=True) if os.path.exists(os.path.join(out_path, ".git")): logger.info( "repository pull started", extra=log_context | {OPERATION: "pull"}, ) git_pull(out_path, env=git_env) return logger.info( "repository clone started", extra=log_context | {OPERATION: "clone"}, ) git_clone(repository_url=repository_url, repository=out_path, env=git_env) def main(): if len(sys.argv) < 2: logging.basicConfig( level=logging.ERROR, format="%(levelname)s %(message)s", ) logger.error("Usage: python gitea-mirror.py CONFIG_PATH") sys.exit(1) try: config = read_toml_config(sys.argv[1]) except RuntimeError as err: logging.basicConfig( level=logging.ERROR, format="%(levelname)s %(message)s", ) logger.error("invalid config: %s", err) sys.exit(1) setup_logging(config.logging) api = GiteaApi( endpoint=config.endpoint, api_token=config.api_token, ) repos = api.get_repositories() logger.info( "repositories ready", extra={OPERATION: "mirror", REPOSITORY_COUNT: len(repos)}, ) if not is_valid_repository_names( name_format=config.repository_format, repos=repos, ): logger.error( "repository name format creates duplicates", extra={OPERATION: "validate_repository_names"}, ) sys.exit(1) for repo in repos: process_repo(config=config, repo=repo) if __name__ == "__main__": main()