From ca6662145a3d8d4831eb1c6bc16a26cd7660e43b Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Sun, 22 Jan 2023 16:29:08 +0100 Subject: [PATCH] feat: add option to load templates from remote git sources --- ansibledoctor/config.py | 52 +++++++++++++++++++---------- ansibledoctor/doc_generator.py | 36 ++++++++++---------- docs/content/usage/configuration.md | 4 +-- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/ansibledoctor/config.py b/ansibledoctor/config.py index 54ab504..30ea4dc 100644 --- a/ansibledoctor/config.py +++ b/ansibledoctor/config.py @@ -27,6 +27,10 @@ class Config(): - provides cli parameters """ + TEMPLATE_SRC = os.path.join( + os.path.dirname(os.path.realpath(__file__)), *["templates", "readme"] + ) + SETTINGS = { "config_file": { "default": "", @@ -67,15 +71,9 @@ class Config(): "file": True, "type": environs.Env().str }, - "template_dir": { - "default": os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates"), - "env": "TEMPLATE_DIR", - "file": True, - "type": environs.Env().str - }, - "template": { - "default": "readme", - "env": "TEMPLATE", + "template_src": { + "default": f"local>{TEMPLATE_SRC}", + "env": "template_src", "file": True, "type": environs.Env().str }, @@ -163,6 +161,7 @@ class Config(): self.config = None self._set_config() self.is_role = self._set_is_role() or False + self.template = self._set_template() def _get_args(self, args): cleaned = dict(filter(lambda item: item[1] is not None, args.items())) @@ -257,7 +256,7 @@ class Config(): if self._validate(args): anyconfig.merge(defaults, args, ac_merge=anyconfig.MS_DICTS) - fix_files = ["output_dir", "template_dir", "custom_header"] + fix_files = ["output_dir", "custom_header"] for file in fix_files: if defaults[file] and defaults[file] != "": defaults[file] = self._normalize_path(defaults[file]) @@ -320,15 +319,32 @@ class Config(): annotations.append(k) return annotations - def get_template(self): - """ - Get the base dir for the template to use. + def _set_template(self): + allowed_provider = ["local", "git"] - :return: str abs path - """ - template_dir = self.config.get("template_dir") - template = self.config.get("template") - return os.path.realpath(os.path.join(template_dir, template)) + try: + provider, path = self.config.get("template_src").split(">", 1) + except ValueError as e: + raise ansibledoctor.exception.ConfigError( + "Malformed option 'template_src'", str(e) + ) from e + + provider = provider.strip().lower() + path = path.strip() + + if provider not in allowed_provider: + raise ansibledoctor.exception.ConfigError( + f"Unknonw template provider '{provider}'", + f"the template provider has to be one of [{', '.join(allowed_provider)}]" + ) + + if provider == "local": + path = self._normalize_path(os.path.realpath(path)) + + if provider == "git": + pass + + return provider, path class SingleConfig(Config, metaclass=Singleton): diff --git a/ansibledoctor/doc_generator.py b/ansibledoctor/doc_generator.py index 9997c19..0f06dad 100644 --- a/ansibledoctor/doc_generator.py +++ b/ansibledoctor/doc_generator.py @@ -2,7 +2,6 @@ """Prepare output and write compiled jinja2 templates.""" import glob -import ntpath import os import re from functools import reduce @@ -36,19 +35,18 @@ class Generator: :return: None """ - template_dir = self.config.get_template() - if os.path.isdir(template_dir): - self.logger.info(f"Using template dir: {template_dir}") + provider, template_src = self.config.template + if os.path.isdir(template_src): + self.logger.info(f"Using template dir: {template_src}") else: - self.log.sysexit_with_message(f"Can not open template dir {template_dir}") + self.log.sysexit_with_message(f"Can not open template dir {template_src}") - for file in glob.iglob(template_dir + "/**/*." + self.extension, recursive=True): - relative_file = file[len(template_dir) + 1:] - if ntpath.basename(file)[:1] != "_": - self.logger.debug(f"Found template file: {relative_file}") - self.template_files.append(relative_file) + for workfile in glob.iglob(f"{template_src}/**/*.{self.extension}", recursive=True): + if not os.path.basename(workfile).startswith("_"): + self.logger.debug(f"Found template file: {os.path.basename(workfile)}") + self.template_files.append(workfile) else: - self.logger.debug(f"Ignoring template file: {relative_file}") + self.logger.debug(f"Ignoring template file: {os.path.basename(workfile)}") def _create_dir(self, directory): if not self.config.config["dry_run"] and not os.path.isdir(directory): @@ -94,25 +92,25 @@ class Generator: self.logger.debug(str(e)) self.log.sysexit_with_message("Aborted...") - for file in self.template_files: + for workfile in self.template_files: doc_file = os.path.join( self.config.config.get("output_dir"), - os.path.splitext(file)[0] + os.path.splitext(os.path.basename(workfile))[0] ) - source_file = self.config.get_template() + "/" + file - self.logger.debug(f"Writing doc output to: {doc_file} from: {source_file}") + self.logger.debug(f"Writing doc output to: {doc_file} from: {workfile}") # make sure the directory exists self._create_dir(os.path.dirname(doc_file)) - if os.path.exists(source_file) and os.path.isfile(source_file): - with open(source_file) as template: + if os.path.exists(workfile) and os.path.isfile(workfile): + with open(workfile) as template: data = template.read() if data is not None: try: + provider, template_src = self.config.template jenv = Environment( # nosec - loader=FileSystemLoader(self.config.get_template()), + loader=FileSystemLoader(template_src), lstrip_blocks=True, trim_blocks=True, autoescape=jinja2.select_autoescape() @@ -133,7 +131,7 @@ class Generator: ) as e: self.log.sysexit_with_message( "Jinja2 templating error while loading file: '{}'\n{}".format( - file, str(e) + workfile, str(e) ) ) except UnicodeEncodeError as e: diff --git a/docs/content/usage/configuration.md b/docs/content/usage/configuration.md index 9f74536..fd098dc 100644 --- a/docs/content/usage/configuration.md +++ b/docs/content/usage/configuration.md @@ -37,7 +37,7 @@ logging: # Path to write rendered template file. Default is the current working directory. output_dir: # Default is built-in templates directory. -template_dir: +template_src: template: readme # Don't ask to overwrite if output file exists. @@ -93,7 +93,7 @@ ANSIBLE_DOCTOR_DRY_RUN=false ANSIBLE_DOCTOR_LOG_LEVEL=warning ANSIBLE_DOCTOR_LOG_JSON=false ANSIBLE_DOCTOR_OUTPUT_DIR= -ANSIBLE_DOCTOR_TEMPLATE_DIR= +ANSIBLE_DOCTOR_template_src= ANSIBLE_DOCTOR_TEMPLATE=readme ANSIBLE_DOCTOR_FORCE_OVERWRITE=false ANSIBLE_DOCTOR_CUSTOM_HEADER=