From 04f0a216585b93b73f24afd45056b108872057b6 Mon Sep 17 00:00:00 2001 From: NilashishC Date: Tue, 30 Jan 2024 19:24:30 +0530 Subject: [PATCH 1/4] Add --templates-path option Signed-off-by: NilashishC --- src/ansible_creator/cli.py | 7 +++++ src/ansible_creator/config.py | 4 +++ src/ansible_creator/subcommands/init.py | 35 +++++++++++++++++++++++++ src/ansible_creator/utils.py | 18 ++++++++++--- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/ansible_creator/cli.py b/src/ansible_creator/cli.py index 84d27ab6..18714f70 100644 --- a/src/ansible_creator/cli.py +++ b/src/ansible_creator/cli.py @@ -153,6 +153,13 @@ def parse_args(self: Cli) -> argparse.Namespace: "current working directory.", ) + init_command_parser.add_argument( + "--templates-path", + help="Path to a directory containing custom template for collection skeleton." + " When provided, this will be used to scaffold the collection instead of the" + " in-built template.", + ) + init_command_parser.add_argument( "--force", default=False, diff --git a/src/ansible_creator/config.py b/src/ansible_creator/config.py index 59ddb6c3..2a4e3417 100644 --- a/src/ansible_creator/config.py +++ b/src/ansible_creator/config.py @@ -19,6 +19,7 @@ class Config: no_ansi: bool subcommand: str verbose: int + templates_path: str collection: str = "" force: bool = False @@ -36,4 +37,7 @@ def __post_init__(self: Config) -> None: object.__setattr__(self, "namespace", fqcn[0]) object.__setattr__(self, "collection_name", fqcn[-1]) + if self.templates_path: + object.__setattr__(self, "template", expand_path(self.init_path)) + object.__setattr__(self, "init_path", expand_path(self.init_path)) diff --git a/src/ansible_creator/subcommands/init.py b/src/ansible_creator/subcommands/init.py index d6c03997..584184ea 100644 --- a/src/ansible_creator/subcommands/init.py +++ b/src/ansible_creator/subcommands/init.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +import re import shutil from typing import TYPE_CHECKING @@ -36,6 +37,7 @@ def __init__( self._force = config.force self._creator_version = config.creator_version self._templar = Templar() + self._templates_path = config.templates_path self.output: Output = output def run(self: Init) -> None: @@ -47,6 +49,38 @@ def run(self: Init) -> None: self.output.debug(msg=f"final collection path set to {col_path}") + # validate custom templates + if self._templates_path: + full_templates_path = os.path.join(self._templates_path, "new_collection") + + # verify that templates path exists and has "new_collection" dir + if not os.path.exists(full_templates_path): + msg = f"could not find templates for new collection at {self._templates_path}." + raise CreatorError(msg) + + # ensure that "new_collection" dir is not empty + if len(os.listdir(full_templates_path)) == 0: + msg = ( + "please ensure that a directory named `new_collection` exists" + f" at {self._templates_path} and is not empty." + ) + raise CreatorError(msg) + + # ensure that a template for galaxy file exists in "new_collection" dir + if ( + len( + list( + filter( + re.compile("(?:galaxy.)(?:yml|yaml)(?:.j2)?").match, + os.listdir(full_templates_path), + ), + ), + ) + == 0 + ): + msg = f"template for Ansible galaxy file not found in {full_templates_path!s}." + raise CreatorError(msg) + # check if init_path already exists if os.path.exists(col_path): if os.path.isfile(col_path): @@ -86,6 +120,7 @@ def run(self: Init) -> None: source="new_collection", dest=col_path, templar=self._templar, + templates_path=self._templates_path, template_data={ "namespace": self._namespace, "collection_name": self._collection_name, diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index 30029fc2..ebcc9a22 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -6,6 +6,7 @@ import sys from dataclasses import dataclass +from pathlib import Path from typing import TYPE_CHECKING from ansible_creator.exceptions import CreatorError @@ -84,6 +85,7 @@ def copy_container( # noqa: PLR0913 dest: str, output: Output, templar: Templar, + templates_path: str, template_data: dict[str, str], allow_overwrite: list[str] | None = None, ) -> None: @@ -100,10 +102,13 @@ def copy_container( # noqa: PLR0913 output.debug(msg=f"starting recursive copy with source container '{source}'") output.debug(msg=f"allow_overwrite set to {allow_overwrite}") - def _recursive_copy(root: abc.Traversable) -> None: + if templates_path: + output.debug(msg=f"custom templates path set to {templates_path}") + + def _recursive_copy(root: Path | abc.Traversable) -> None: """Recursively traverses a resource container and copies content to destination. - :param root: A traversable object representing root of the container to copy. + :param root: A traversable or Path object representing root of the container to copy. """ output.debug(msg=f"current root set to {root}") @@ -143,4 +148,11 @@ def _recursive_copy(root: abc.Traversable) -> None: with open(dest_file, "w", encoding="utf-8") as df_handle: df_handle.write(content) - _recursive_copy(root=resources.files(f"ansible_creator.resources.{source}")) + if templates_path: + # use custom templates path + root = Path(templates_path) / source + else: + # use built-in templates + root = resources.files(f"ansible_creator.resources.{source}") + + _recursive_copy(root) From 5004630597894ec9a6a68903f5bc9ed03dd0243f Mon Sep 17 00:00:00 2001 From: NilashishC Date: Tue, 30 Jan 2024 19:29:55 +0530 Subject: [PATCH 2/4] fix tests Signed-off-by: NilashishC --- tests/units/test_basic.py | 3 +++ tests/units/test_init.py | 1 + 2 files changed, 4 insertions(+) diff --git a/tests/units/test_basic.py b/tests/units/test_basic.py index d98d11a1..2eb5beee 100644 --- a/tests/units/test_basic.py +++ b/tests/units/test_basic.py @@ -32,6 +32,7 @@ def test_expand_path() -> None: "collection": "testorg.testcol", "init_path": "./", "force": False, + "templates_path": None, }, ], [ @@ -59,6 +60,7 @@ def test_expand_path() -> None: "collection": "testorg.testcol", "init_path": "/home/ansible", "force": True, + "templates_path": None, }, ], ], @@ -82,6 +84,7 @@ def test_configuration_class() -> None: "verbose": 2, "collection": "testorg.testcol", "init_path": "$HOME", + "templates_path": None, } app_config = Config(**cli_args) assert app_config.namespace == "testorg" diff --git a/tests/units/test_init.py b/tests/units/test_init.py index c5589b5e..bffc73c8 100644 --- a/tests/units/test_init.py +++ b/tests/units/test_init.py @@ -34,6 +34,7 @@ def cli_args(tmp_path) -> dict: "verbose": 0, "collection": "testorg.testcol", "init_path": tmp_path, + "templates_path": None, } From 9244502039a8d1f44b6116af21bbb5d71a06bceb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:26:48 +0000 Subject: [PATCH 3/4] chore: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/ansible_creator/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index 4a4c3039..945d2b12 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -5,8 +5,8 @@ import os from dataclasses import dataclass -from pathlib import Path from importlib import resources +from pathlib import Path from typing import TYPE_CHECKING from ansible_creator.exceptions import CreatorError From 9388ef53452231d2ef38e72c5c2e8a0dd601a7f5 Mon Sep 17 00:00:00 2001 From: NilashishC Date: Tue, 6 Feb 2024 18:42:24 +0530 Subject: [PATCH 4/4] Add a compat file Signed-off-by: NilashishC --- src/ansible_creator/compat.py | 18 ++++++++++++++++++ src/ansible_creator/utils.py | 16 ++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 src/ansible_creator/compat.py diff --git a/src/ansible_creator/compat.py b/src/ansible_creator/compat.py new file mode 100644 index 00000000..7e51eabe --- /dev/null +++ b/src/ansible_creator/compat.py @@ -0,0 +1,18 @@ +"""Compat library for ansible-creator. + +This contains compatibility definitions for older python +When we need to import a module differently depending on python versions, we do it +here. +""" + +# ruff: noqa: F401 + +# pylint: disable=unused-import + +import sys + + +if sys.version_info >= (3, 11): + from importlib.resources.abc import Traversable +else: + from importlib.abc import Traversable diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index 945d2b12..60290595 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -13,8 +13,7 @@ if TYPE_CHECKING: - from importlib import abc - + from ansible_creator.compat import Traversable from ansible_creator.output import Output from ansible_creator.templar import Templar @@ -84,8 +83,8 @@ def copy_container( # noqa: PLR0913 dest: str, output: Output, templar: Templar, - templates_path: str, template_data: dict[str, str], + templates_path: str, allow_overwrite: list[str] | None = None, ) -> None: """Copy files and directories from a possibly nested source to a destination. @@ -94,6 +93,7 @@ def copy_container( # noqa: PLR0913 :param dest: Absolute destination path. :param templar: An object of template class. :param template_data: A dictionary containing data to render templates with. + :param templates_path: A string representing path to custom templates :param allow_overwrite: A list of paths that should be overwritten at destination. :raises CreatorError: if allow_overwrite is not a list. @@ -104,7 +104,7 @@ def copy_container( # noqa: PLR0913 if templates_path: output.debug(msg=f"custom templates path set to {templates_path}") - def _recursive_copy(root: Path | abc.Traversable) -> None: + def _recursive_copy(root: Path | Traversable) -> None: """Recursively traverses a resource container and copies content to destination. :param root: A traversable or Path object representing root of the container to copy. @@ -149,9 +149,9 @@ def _recursive_copy(root: Path | abc.Traversable) -> None: if templates_path: # use custom templates path - root = Path(templates_path) / source + tp_root = Path(templates_path) / source + _recursive_copy(tp_root) else: # use built-in templates - root = resources.files(f"ansible_creator.resources.{source}") - - _recursive_copy(root) + ib_root = resources.files(f"ansible_creator.resources.{source}") + _recursive_copy(ib_root)