diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2021-07-31 10:16:38 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2021-07-31 10:17:44 +0300 |
commit | 9181b3a021acc7585cb6f9f20da6accfce0015e0 (patch) | |
tree | 0fcb4d5a99be02ac7a934817491f50d0d32d757d /cgitize | |
parent | add some unit tests (diff) | |
download | cgitize-9181b3a021acc7585cb6f9f20da6accfce0015e0.tar.gz cgitize-9181b3a021acc7585cb6f9f20da6accfce0015e0.zip |
merge my_repos.py to the config
The config is also in the TOML format now. It's a bit messy for the
moment, but I'll fix it.
Diffstat (limited to 'cgitize')
-rw-r--r-- | cgitize/cgit.py | 10 | ||||
-rw-r--r-- | cgitize/config.py | 116 | ||||
-rw-r--r-- | cgitize/main.py | 9 | ||||
-rw-r--r-- | cgitize/repo.py | 76 |
4 files changed, 120 insertions, 91 deletions
diff --git a/cgitize/cgit.py b/cgitize/cgit.py index fe715d5..53a726a 100644 --- a/cgitize/cgit.py +++ b/cgitize/cgit.py @@ -18,7 +18,7 @@ class CGitServer: def get_clone_url(self, repo): if self.clone_url is None: return None - return self.clone_url.format(repo_id=repo.repo_id) + return self.clone_url.format(name=repo.name) class CGitRCWriter: @@ -96,7 +96,7 @@ class CGitRepositories: return abs_path def get_repo_dir(self, repo): - return os.path.join(self.dir, repo.repo_id) + return os.path.join(self.dir, repo.name) def update(self, repo): success = self._mirror_or_update(repo) @@ -130,7 +130,7 @@ class CGitRepositories: # Jeez, there's a proper local repository in the target # directory already with a different upstream; something's # wrong, fix it manually. - logging.warning("Existing repository '%s' doesn't match the specified clone URL: %s", repo.repo_id, repo.clone_url) + logging.warning("Existing repository '%s' doesn't match the specified clone URL: %s", repo.name, repo.clone_url) if self.force: # Unless --force was specified, in which case we overwrite # the repository. @@ -142,7 +142,7 @@ class CGitRepositories: return self._update_existing(repo) def _mirror(self, repo): - logging.info("Mirroring repository '%s' from: %s", repo.repo_id, repo.clone_url) + logging.info("Mirroring repository '%s' from: %s", repo.name, repo.clone_url) repo_dir = self.get_repo_dir(repo) if os.path.isdir(repo_dir): try: @@ -159,7 +159,7 @@ class CGitRepositories: return Git.check('remote', 'set-url', 'origin', repo.clone_url) def _update_existing(self, repo): - logging.info("Updating repository '%s'", repo.repo_id) + logging.info("Updating repository '%s'", repo.name) repo_dir = self.get_repo_dir(repo) with chdir(repo_dir): with Git.setup_auth(repo): diff --git a/cgitize/config.py b/cgitize/config.py index a630d7a..8171860 100644 --- a/cgitize/config.py +++ b/cgitize/config.py @@ -9,73 +9,99 @@ import logging import os.path import sys +from cgitize.repo import Repo, GitHub as GitHubRepo, Bitbucket as BitbucketRepo from cgitize.utils import chdir +import tomli -class Config: - DEFAULT_PATH = '/etc/cgitize/cgitize.conf' - DEFAULT_OUTPUT_DIR = '/var/tmp/cgitize/output' - DEFAULT_MY_REPOS_PATH = '/etc/cgitize/my_repos.py' - @staticmethod - def read(path): - return Config(path) +class Section: + def __init__(self, impl): + self.impl = impl - def __init__(self, path): - self.path = os.path.abspath(path) - self.impl = configparser.ConfigParser() - self.impl.read(path) + def _get_config_value(self, key, required=True, default=None): + if required and default is None: + if not key in self.impl: + raise RuntimeError(f'configuration value is missing: {key}') + return self.impl.get(key, default) + + def _get_config_path(self, *args, **kwargs): + return os.path.abspath(self._get_config_value(*args, **kwargs)) - def _resolve_relative(self, path): - if os.path.isabs(path): - return path - with chdir(os.path.dirname(self.path)): - path = os.path.abspath(path) - return path + +class Main(Section): + DEFAULT_OUTPUT_DIR = '/var/tmp/cgitize/output' @property def output(self): - path = self.impl.get('DEFAULT', 'output', fallback=Config.DEFAULT_OUTPUT_DIR) - return self._resolve_relative(path) + return self._get_config_path('output', default=Main.DEFAULT_OUTPUT_DIR) @property def clone_url(self): - return self.impl.get('DEFAULT', 'clone_url', fallback=None) + return self._get_config_value('clone_url', required=False) @property def default_owner(self): - return self.impl.get('DEFAULT', 'owner', fallback=None) + return self._get_config_value('owner', required=False) @property def via_ssh(self): - return self.impl.getboolean('DEFAULT', 'ssh', fallback=True) + return self._get_config_value('ssh', default=True) - @property - def github_username(self): - return self.impl.get('GITHUB', 'username', fallback=None) - @property - def github_access_token(self): - return self.impl.get('GITHUB', 'access_token', fallback=None) +class GitHub(Section): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.repositories = Repositories(self.impl.get('repositories', {}), GitHubRepo) @property - def bitbucket_username(self): - return self.impl.get('BITBUCKET', 'username', fallback=None) + def access_token(self): + return self._get_config_value('access_token', required=False) - @property - def bitbucket_app_password(self): - return self.impl.get('BITBUCKET', 'app_password', fallback=None) + def enum_repositories(self): + return self.repositories.enum_repositories() + + +class Bitbucket(Section): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.repositories = Repositories(self.impl.get('repositories', {}), BitbucketRepo) @property - def my_repos(self): - path = self.impl.get('DEFAULT', 'my_repos', fallback=Config.DEFAULT_MY_REPOS_PATH) - return self._resolve_relative(path) - - def import_my_repos(self): - sys.path.append(os.path.dirname(self.my_repos)) - if not os.path.exists(self.my_repos): - logging.error("Couldn't find my_repos.py at: %s", self.my_repos) - return None - module_name = os.path.splitext(os.path.basename(self.my_repos))[0] - module = importlib.import_module(module_name) - return module.MY_REPOS + def app_password(self): + return self._get_config_value('app_password', required=False) + + def enum_repositories(self): + return self.repositories.enum_repositories() + + +class Repositories(Section): + def __init__(self, impl, repo_cls=Repo): + super().__init__(impl) + self.repo_cls = repo_cls + + def enum_repositories(self): + for k, v in self.impl.items(): + yield self.repo_cls.from_config(v) + + +class Config: + DEFAULT_PATH = '/etc/cgitize/cgitize.toml' + + @staticmethod + def read(path): + return Config(path) + + def __init__(self, path): + self.path = os.path.abspath(path) + with open(self.path, 'rb') as f: + self.impl = tomli.load(f) + self.main = Main(self.impl) + self.repositories = Repositories(self.impl.get('repositories', {})) + self.github = GitHub(self.impl.get('github', {})) + self.bitbucket = Bitbucket(self.impl.get('bitbucket', {})) + + def enum_repositories(self): + yield from self.repositories.enum_repositories() + yield from self.github.enum_repositories() + yield from self.bitbucket.enum_repositories() diff --git a/cgitize/main.py b/cgitize/main.py index 0c8f029..6b4705f 100644 --- a/cgitize/main.py +++ b/cgitize/main.py @@ -33,13 +33,10 @@ def main(args=None): args = parse_args() with setup_logging(args.verbose): config = Config.read(args.config) - my_repos = config.import_my_repos() - if my_repos is None: - return 1 - cgit_server = CGitServer(config.clone_url) - output = CGitRepositories(config.output, cgit_server, force=args.force) + cgit_server = CGitServer(config.main.clone_url) + output = CGitRepositories(config.main.output, cgit_server, force=args.force) success = True - for repo in my_repos: + for repo in config.enum_repositories(): if args.repos is None or repo.repo_id in args.repos: repo.fill_defaults(config) repo.validate() diff --git a/cgitize/repo.py b/cgitize/repo.py index 8ecbcf5..01b7430 100644 --- a/cgitize/repo.py +++ b/cgitize/repo.py @@ -9,14 +9,17 @@ from urllib.parse import urlsplit, urlunsplit class Repo: - @staticmethod - def extract_repo_name(repo_id): - return os.path.basename(repo_id) - - def __init__(self, repo_id, clone_url=None, owner=None, desc=None, + @classmethod + def from_config(cls, cfg): + if 'id' not in cfg: + raise ValueError('every repository must have its id defined') + return cls(cfg['id'], clone_url=cfg.get('clone_url'), + owner=cfg.get('owner'), desc=cfg.get('desc'), + homepage=cfg.get('homepage')) + + def __init__(self, name, clone_url=None, owner=None, desc=None, homepage=None): - self._repo_id = repo_id - self._repo_name = self.extract_repo_name(repo_id) + self._name = name self._clone_url = clone_url self._owner = owner self._desc = desc @@ -24,19 +27,15 @@ class Repo: def fill_defaults(self, config): if self._owner is None: - self._owner = config.default_owner + self._owner = config.main.default_owner def validate(self): if self.clone_url is None: raise RuntimeError('upstream repository URL must be specified') @property - def repo_id(self): - return self._repo_id - - @property - def repo_name(self): - return self._repo_name + def name(self): + return self._name @property def clone_url(self): @@ -54,7 +53,7 @@ class Repo: return self.homepage if self.clone_url: return self.clone_url - return self.repo_name + return self.name @property def homepage(self): @@ -66,21 +65,32 @@ class Repo: class HostedRepo(Repo, abc.ABC): + @classmethod + def from_config(cls, cfg): + if 'id' not in cfg: + raise ValueError('every repository must have its id defined') + return cls(cfg['id'], owner=cfg.get('owner'), desc=cfg.get('desc'), + homepage=cfg.get('homepage')) + + @staticmethod + def split_repo_id(repo_id): + components = repo_id.split('/') + if len(components) != 2: + raise ValueError(f'repository ID must be in the USER/NAME format: {repo_id}') + user, name = components + return user, name + def __init__(self, repo_id, owner=None, desc=None, homepage=None, - user=None, via_ssh=True): - super().__init__(repo_id, clone_url=None, owner=owner, desc=desc, + via_ssh=True): + user, name = self.split_repo_id(repo_id) + super().__init__(name, clone_url=None, owner=owner, desc=desc, homepage=homepage) self._user = user self._via_ssh = via_ssh def fill_defaults(self, config): super().fill_defaults(config) - self._via_ssh = config.via_ssh - - def validate(self): - super().validate() - if self.user is None: - raise RuntimeError(f'neither explicit or default {self.provider_name} username was specified') + self._via_ssh = config.main.via_ssh @property def user(self): @@ -127,9 +137,7 @@ class GitHub(HostedRepo): def fill_defaults(self, config): super().fill_defaults(config) - if self._user is None: - self._user = config.github_username - self._access_token = config.github_access_token + self._access_token = config.github.access_token @property def provider_name(self): @@ -137,7 +145,7 @@ class GitHub(HostedRepo): @property def homepage(self): - return f'https://github.com/{self.user}/{self.repo_name}' + return f'https://github.com/{self.user}/{self.name}' @property def url_auth(self): @@ -147,11 +155,11 @@ class GitHub(HostedRepo): @property def clone_url_ssh(self): - return f'ssh://git@github.com/{self.user}/{self.repo_name}.git' + return f'ssh://git@github.com/{self.user}/{self.name}.git' @property def clone_url_https(self): - return f'https://github.com/{self.user}/{self.repo_name}.git' + return f'https://github.com/{self.user}/{self.name}.git' class Bitbucket(HostedRepo): @@ -161,9 +169,7 @@ class Bitbucket(HostedRepo): def fill_defaults(self, config): super().fill_defaults(config) - if self._user is None: - self._user = config.bitbucket_username - self._app_password = config.bitbucket_app_password + self._app_password = config.bitbucket.app_password @property def provider_name(self): @@ -171,7 +177,7 @@ class Bitbucket(HostedRepo): @property def homepage(self): - return f'https://bitbucket.org/{self.user}/{self.repo_name.lower()}' + return f'https://bitbucket.org/{self.user}/{self.name.lower()}' @property def url_auth(self): @@ -181,8 +187,8 @@ class Bitbucket(HostedRepo): @property def clone_url_ssh(self): - return f'ssh://git@bitbucket.org/{self.user}/{self.repo_name}.git' + return f'ssh://git@bitbucket.org/{self.user}/{self.name}.git' @property def clone_url_https(self): - return f'https://bitbucket.org/{self.user}/{self.repo_name}.git' + return f'https://bitbucket.org/{self.user}/{self.name}.git' |