From 49d3ab67ddd276a44349c1780e9dd14be610b340 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 25 Jun 2024 06:30:08 -0700 Subject: [PATCH 1/5] Add RH auth extension recommendation --- src/ansible_creator/constants.py | 1 + .../ansible_project/.vscode/extensions.json | 3 --- .../.devcontainer/devcontainer.json.j2 | 2 +- .../.devcontainer/docker/devcontainer.json.j2 | 2 +- .../.devcontainer/podman/devcontainer.json.j2 | 2 +- .../common/vscode/.vscode/extensions.json.j2 | 3 +++ .../new_collection/.vscode/extensions.json | 3 --- src/ansible_creator/subcommands/init.py | 21 ++++++++----------- src/ansible_creator/templar.py | 3 +++ .../testcol/.devcontainer/devcontainer.json | 2 +- .../.devcontainer/docker/devcontainer.json | 2 +- .../.devcontainer/podman/devcontainer.json | 2 +- .../testorg/testcol/.vscode/extensions.json | 2 +- .../.devcontainer/devcontainer.json | 2 +- .../.devcontainer/docker/devcontainer.json | 2 +- .../.devcontainer/podman/devcontainer.json | 2 +- .../ansible_project/.vscode/extensions.json | 2 +- 17 files changed, 27 insertions(+), 29 deletions(-) delete mode 100644 src/ansible_creator/resources/ansible_project/.vscode/extensions.json create mode 100644 src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 delete mode 100644 src/ansible_creator/resources/new_collection/.vscode/extensions.json diff --git a/src/ansible_creator/constants.py b/src/ansible_creator/constants.py index bca45fcb..781796a2 100644 --- a/src/ansible_creator/constants.py +++ b/src/ansible_creator/constants.py @@ -3,6 +3,7 @@ GLOBAL_TEMPLATE_VARS = { "DEV_CONTAINER_IMAGE": "ghcr.io/ansible/community-ansible-dev-tools:latest", "DEV_FILE_IMAGE": "ghcr.io/ansible/ansible-workspace-env-reference:latest", + "RECOMMENDED_EXTENSIONS": ["redhat.ansible", "redhat.vscode-redhat-account"], } MIN_COLLECTION_NAME_LEN = 2 diff --git a/src/ansible_creator/resources/ansible_project/.vscode/extensions.json b/src/ansible_creator/resources/ansible_project/.vscode/extensions.json deleted file mode 100644 index 51360a49..00000000 --- a/src/ansible_creator/resources/ansible_project/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["redhat.ansible"] -} diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 index 96689ffe..b0e6adcf 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": {{ RECOMMENDED_EXTENSIONS | json }} } } } diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 index 2f078393..0c361ce9 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": {{ RECOMMENDED_EXTENSIONS | json }} } } } diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 index ed8ee27d..26794347 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 @@ -20,7 +20,7 @@ ], "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": {{ RECOMMENDED_EXTENSIONS | json }} } } } diff --git a/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 b/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 new file mode 100644 index 00000000..5e129928 --- /dev/null +++ b/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 @@ -0,0 +1,3 @@ +{ + "recommendations": {{ RECOMMENDED_EXTENSIONS | json }} +} diff --git a/src/ansible_creator/resources/new_collection/.vscode/extensions.json b/src/ansible_creator/resources/new_collection/.vscode/extensions.json deleted file mode 100644 index 51360a49..00000000 --- a/src/ansible_creator/resources/new_collection/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["redhat.ansible"] -} diff --git a/src/ansible_creator/subcommands/init.py b/src/ansible_creator/subcommands/init.py index a44f6c77..3f69ca63 100644 --- a/src/ansible_creator/subcommands/init.py +++ b/src/ansible_creator/subcommands/init.py @@ -89,6 +89,13 @@ def run(self: Init) -> None: # noqa: C901 self.output.debug(msg=f"creating new directory at {self._init_path}") self._init_path.mkdir(parents=True) + common_resources = [ + "common.devcontainer", + "common.devfile", + "common.gitignore", + "common.vscode", + ] + if self._project == "collection": if not isinstance(self._collection_name, str): msg = "Collection name is required when scaffolding a collection." @@ -96,12 +103,7 @@ def run(self: Init) -> None: # noqa: C901 # copy new_collection container to destination, templating files when found self.output.debug(msg="started copying collection skeleton to destination") copier = Copier( - resources=[ - "new_collection", - "common.devcontainer", - "common.devfile", - "common.gitignore", - ], + resources=["new_collection", *common_resources], resource_id="new_collection", dest=self._init_path, output=self.output, @@ -133,12 +135,7 @@ def run(self: Init) -> None: # noqa: C901 ) raise CreatorError(msg) copier = Copier( - resources=[ - "ansible_project", - "common.devcontainer", - "common.devfile", - "common.gitignore", - ], + resources=["ansible_project", *common_resources], resource_id="ansible_project", dest=self._init_path, output=self.output, diff --git a/src/ansible_creator/templar.py b/src/ansible_creator/templar.py index ea5c33f3..2c44b3ca 100644 --- a/src/ansible_creator/templar.py +++ b/src/ansible_creator/templar.py @@ -2,6 +2,8 @@ from __future__ import annotations +import json + from typing import TYPE_CHECKING @@ -37,6 +39,7 @@ def __init__(self: Templar) -> None: undefined=StrictUndefined, keep_trailing_newline=True, ) + self.env.filters["json"] = json.dumps def render_from_content(self: Templar, template: str, data: dict[str, Any]) -> str: """Render a template with provided data. diff --git a/tests/fixtures/collection/testorg/testcol/.devcontainer/devcontainer.json b/tests/fixtures/collection/testorg/testcol/.devcontainer/devcontainer.json index e14c0bb8..4443c9e0 100644 --- a/tests/fixtures/collection/testorg/testcol/.devcontainer/devcontainer.json +++ b/tests/fixtures/collection/testorg/testcol/.devcontainer/devcontainer.json @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/collection/testorg/testcol/.devcontainer/docker/devcontainer.json b/tests/fixtures/collection/testorg/testcol/.devcontainer/docker/devcontainer.json index 18b5ef34..48d612ee 100644 --- a/tests/fixtures/collection/testorg/testcol/.devcontainer/docker/devcontainer.json +++ b/tests/fixtures/collection/testorg/testcol/.devcontainer/docker/devcontainer.json @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/collection/testorg/testcol/.devcontainer/podman/devcontainer.json b/tests/fixtures/collection/testorg/testcol/.devcontainer/podman/devcontainer.json index 30327b46..a11a3dfc 100644 --- a/tests/fixtures/collection/testorg/testcol/.devcontainer/podman/devcontainer.json +++ b/tests/fixtures/collection/testorg/testcol/.devcontainer/podman/devcontainer.json @@ -20,7 +20,7 @@ ], "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/collection/testorg/testcol/.vscode/extensions.json b/tests/fixtures/collection/testorg/testcol/.vscode/extensions.json index 51360a49..c1b89785 100644 --- a/tests/fixtures/collection/testorg/testcol/.vscode/extensions.json +++ b/tests/fixtures/collection/testorg/testcol/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["redhat.ansible"] + "recommendations": ["redhat.ansible", "redhat.vscode-redhat-account"] } diff --git a/tests/fixtures/project/ansible_project/.devcontainer/devcontainer.json b/tests/fixtures/project/ansible_project/.devcontainer/devcontainer.json index e14c0bb8..4443c9e0 100644 --- a/tests/fixtures/project/ansible_project/.devcontainer/devcontainer.json +++ b/tests/fixtures/project/ansible_project/.devcontainer/devcontainer.json @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/project/ansible_project/.devcontainer/docker/devcontainer.json b/tests/fixtures/project/ansible_project/.devcontainer/docker/devcontainer.json index 18b5ef34..48d612ee 100644 --- a/tests/fixtures/project/ansible_project/.devcontainer/docker/devcontainer.json +++ b/tests/fixtures/project/ansible_project/.devcontainer/docker/devcontainer.json @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/project/ansible_project/.devcontainer/podman/devcontainer.json b/tests/fixtures/project/ansible_project/.devcontainer/podman/devcontainer.json index 30327b46..a11a3dfc 100644 --- a/tests/fixtures/project/ansible_project/.devcontainer/podman/devcontainer.json +++ b/tests/fixtures/project/ansible_project/.devcontainer/podman/devcontainer.json @@ -20,7 +20,7 @@ ], "customizations": { "vscode": { - "extensions": ["redhat.ansible"] + "extensions": ["redhat.ansible", "redhat.vscode-redhat-account"] } } } diff --git a/tests/fixtures/project/ansible_project/.vscode/extensions.json b/tests/fixtures/project/ansible_project/.vscode/extensions.json index 51360a49..c1b89785 100644 --- a/tests/fixtures/project/ansible_project/.vscode/extensions.json +++ b/tests/fixtures/project/ansible_project/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["redhat.ansible"] + "recommendations": ["redhat.ansible", "redhat.vscode-redhat-account"] } From 2370d19d6857c506b37aaeffb99bca5497615d48 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 25 Jun 2024 06:36:56 -0700 Subject: [PATCH 2/5] Add RH auth extension recommendation --- tests/units/test_templar.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/units/test_templar.py diff --git a/tests/units/test_templar.py b/tests/units/test_templar.py new file mode 100644 index 00000000..0a6994c8 --- /dev/null +++ b/tests/units/test_templar.py @@ -0,0 +1,27 @@ +"""Tests for templar.""" + +from ansible_creator.templar import Templar + + +def test_templar() -> None: + """Test templar.""" + templar = Templar() + data = {"key": "value"} + template = "{{ key }}" + assert templar.render_from_content(template, data) == "value" + + +def test_templar_json_simple() -> None: + """Test templar json with a simple structure.""" + templar = Templar() + data = {"key": "value"} + template = "{{ key | json }}" + assert templar.render_from_content(template, data) == '"value"' + + +def test_templar_json_complex() -> None: + """Test templar json with a complex structure.""" + templar = Templar() + data = {"key": {"sub_key": ["value", "value2"]}} + template = "{{ key | json }}" + assert templar.render_from_content(template, data) == '{"sub_key": ["value", "value2"]}' From 284b286310c44714e4067fb18326219eee74da35 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 25 Jun 2024 07:44:21 -0700 Subject: [PATCH 3/5] Add test for runtime type check --- src/ansible_creator/utils.py | 13 +++++++++++-- tests/units/test_utils.py | 27 ++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index 1258af5d..351d7e8e 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -15,10 +15,13 @@ if TYPE_CHECKING: + from collections.abc import Sequence + from ansible_creator.compat import Traversable from ansible_creator.output import Output from ansible_creator.templar import Templar + PATH_REPLACERS = { "project_org": "scm_org", "project_repo": "scm_project", @@ -84,7 +87,7 @@ class Copier: index: int = 0 resource_root: str = "ansible_creator.resources" templar: Templar | None = None - template_data: dict[str, str] = field(default_factory=dict) + template_data: dict[str, Sequence[str]] = field(default_factory=dict) @property def resource(self: Copier) -> str: @@ -94,13 +97,16 @@ def resource(self: Copier) -> str: def _recursive_copy( # noqa: C901, PLR0912 self: Copier, root: Traversable, - template_data: dict[str, str], + template_data: dict[str, Sequence[str]], ) -> None: """Recursively traverses a resource container and copies content to destination. Args: root: A traversable object representing root of the container to copy. template_data: A dictionary containing current data to render templates with. + + Raises: + TypeError: If template_data for PATH_REPLACERS value is not a string. """ self.output.debug(msg=f"current root set to {root}") @@ -119,6 +125,9 @@ def _recursive_copy( # noqa: C901, PLR0912 if key in str(dest_path) and template_data: str_dest_path = str(dest_path) repl_val = template_data.get(val, "") + if not isinstance(repl_val, str): + msg = "template_data for PATH_REPLACERS value must be a string" + raise TypeError(msg) dest_path = Path(str_dest_path.replace(key, repl_val)) if obj.is_dir(): diff --git a/tests/units/test_utils.py b/tests/units/test_utils.py index 600e31c4..970bf0df 100644 --- a/tests/units/test_utils.py +++ b/tests/units/test_utils.py @@ -2,7 +2,11 @@ from pathlib import Path -from ansible_creator.utils import expand_path +import pytest + +from ansible_creator.output import Output +from ansible_creator.templar import Templar +from ansible_creator.utils import Copier, expand_path def test_expand_path() -> None: @@ -14,3 +18,24 @@ def test_expand_path() -> None: assert expand_path("foo") == Path.cwd() / "foo" assert expand_path("$HOME") == home assert expand_path("~/$HOME") == Path(f"{home}/{Path.home()}") + + +def test_copier(output: Output, tmp_path: Path) -> None: + """Test Copier raises type error for path replacers. + + Args: + output: Output object. + tmp_path: Temporary directory. + """ + templar = Templar() + template_data = {"scm_org": True, "scm_project": False} + copier = Copier( + resources=["ansible_project"], + resource_id="ansible_project", + template_data=template_data, # type: ignore[arg-type] + dest=tmp_path, + output=output, + templar=templar, + ) + with pytest.raises(TypeError, match="must be a string"): + copier.copy_containers() From cf74851928e7bf251263015a22d550ebe47481f7 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 25 Jun 2024 09:07:42 -0700 Subject: [PATCH 4/5] template data is a dataclass --- .../.devcontainer/devcontainer.json.j2 | 4 +-- .../.devcontainer/docker/devcontainer.json.j2 | 4 +-- .../.devcontainer/podman/devcontainer.json.j2 | 4 +-- .../resources/common/devfile/devfile.yaml.j2 | 2 +- .../common/vscode/.vscode/extensions.json.j2 | 2 +- src/ansible_creator/subcommands/init.py | 25 +++++++------ src/ansible_creator/templar.py | 8 +++-- src/ansible_creator/types.py | 35 +++++++++++++++++++ src/ansible_creator/utils.py | 32 +++++++---------- tests/units/test_templar.py | 19 +++++----- tests/units/test_utils.py | 27 +------------- 11 files changed, 86 insertions(+), 76 deletions(-) create mode 100644 src/ansible_creator/types.py diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 index b0e6adcf..bf58332b 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/devcontainer.json.j2 @@ -1,6 +1,6 @@ { "name": "ansible-dev-container-codespaces", - "image": "{{ DEV_CONTAINER_IMAGE }}", + "image": "{{ dev_container_image }}", "containerUser": "podman", "runArgs": [ "--security-opt", @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": {{ RECOMMENDED_EXTENSIONS | json }} + "extensions": {{ recommended_extensions | json }} } } } diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 index 0c361ce9..46696406 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/docker/devcontainer.json.j2 @@ -1,6 +1,6 @@ { "name": "ansible-dev-container-docker", - "image": "{{ DEV_CONTAINER_IMAGE }}", + "image": "{{ dev_container_image }}", "containerUser": "podman", "runArgs": [ "--security-opt", @@ -18,7 +18,7 @@ "updateRemoteUserUID": true, "customizations": { "vscode": { - "extensions": {{ RECOMMENDED_EXTENSIONS | json }} + "extensions": {{ recommended_extensions | json }} } } } diff --git a/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 b/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 index 26794347..3812ca8e 100644 --- a/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 +++ b/src/ansible_creator/resources/common/devcontainer/.devcontainer/podman/devcontainer.json.j2 @@ -1,6 +1,6 @@ { "name": "ansible-dev-container-podman", - "image": "{{ DEV_CONTAINER_IMAGE }}", + "image": "{{ dev_container_image }}", "containerUser": "root", "runArgs": [ "--cap-add=SYS_ADMIN", @@ -20,7 +20,7 @@ ], "customizations": { "vscode": { - "extensions": {{ RECOMMENDED_EXTENSIONS | json }} + "extensions": {{ recommended_extensions | json }} } } } diff --git a/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 b/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 index 323b2039..aca20ba8 100644 --- a/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 +++ b/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 @@ -4,7 +4,7 @@ metadata: components: - name: tooling-container container: - image: {{ DEV_FILE_IMAGE }} + image: {{ dev_file_image }} memoryRequest: 256M memoryLimit: 6Gi cpuRequest: 250m diff --git a/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 b/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 index 5e129928..e4e80856 100644 --- a/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 +++ b/src/ansible_creator/resources/common/vscode/.vscode/extensions.json.j2 @@ -1,3 +1,3 @@ { - "recommendations": {{ RECOMMENDED_EXTENSIONS | json }} + "recommendations": {{ recommended_extensions | json }} } diff --git a/src/ansible_creator/subcommands/init.py b/src/ansible_creator/subcommands/init.py index 3f69ca63..fac6c778 100644 --- a/src/ansible_creator/subcommands/init.py +++ b/src/ansible_creator/subcommands/init.py @@ -9,6 +9,7 @@ from ansible_creator.exceptions import CreatorError from ansible_creator.templar import Templar +from ansible_creator.types import TemplateData from ansible_creator.utils import Copier @@ -102,17 +103,18 @@ def run(self: Init) -> None: # noqa: C901 raise CreatorError(msg) # copy new_collection container to destination, templating files when found self.output.debug(msg="started copying collection skeleton to destination") + template_data = TemplateData( + namespace=self._namespace, + collection_name=self._collection_name, + creator_version=self._creator_version, + ) copier = Copier( resources=["new_collection", *common_resources], resource_id="new_collection", dest=self._init_path, output=self.output, templar=self._templar, - template_data={ - "namespace": self._namespace, - "collection_name": self._collection_name, - "creator_version": self._creator_version, - }, + template_data=template_data, ) copier.copy_containers() @@ -134,17 +136,20 @@ def run(self: Init) -> None: # noqa: C901 "scaffolding an ansible-project." ) raise CreatorError(msg) + + template_data = TemplateData( + creator_version=self._creator_version, + scm_org=self._scm_org, + scm_project=self._scm_project, + ) + copier = Copier( resources=["ansible_project", *common_resources], resource_id="ansible_project", dest=self._init_path, output=self.output, templar=self._templar, - template_data={ - "scm_org": self._scm_org, - "scm_project": self._scm_project, - "creator_version": self._creator_version, - }, + template_data=template_data, ) copier.copy_containers() diff --git a/src/ansible_creator/templar.py b/src/ansible_creator/templar.py index 2c44b3ca..76291f12 100644 --- a/src/ansible_creator/templar.py +++ b/src/ansible_creator/templar.py @@ -4,11 +4,13 @@ import json +from dataclasses import asdict from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any + from ansible_creator.types import TemplateData + try: from jinja2 import Environment, StrictUndefined @@ -41,7 +43,7 @@ def __init__(self: Templar) -> None: ) self.env.filters["json"] = json.dumps - def render_from_content(self: Templar, template: str, data: dict[str, Any]) -> str: + def render_from_content(self: Templar, template: str, data: TemplateData) -> str: """Render a template with provided data. Args: @@ -51,4 +53,4 @@ def render_from_content(self: Templar, template: str, data: dict[str, Any]) -> s Returns: Templated content. """ - return self.env.from_string(template).render(data) + return self.env.from_string(template).render(asdict(data)) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py new file mode 100644 index 00000000..d28f50c8 --- /dev/null +++ b/src/ansible_creator/types.py @@ -0,0 +1,35 @@ +"""A home for shared types.""" + +from collections.abc import Sequence +from dataclasses import dataclass, field + +from ansible_creator.constants import GLOBAL_TEMPLATE_VARS + + +@dataclass +class TemplateData: + """Dataclass representing the template data. + + Attributes: + additions: A dictionary containing additional data to add to the gitignore. + collection_name: The name of the collection. + creator_version: The version of the creator. + namespace: The namespace of the collection. + scm_org: The organization of the source control management. + scm_project: The project of the source control management. + dev_container_image: The devcontainer image. + dev_file_image: The devfile image. + recommended_extensions: A list of recommended VsCode extensions. + """ + + additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) + collection_name: str = "" + creator_version: str = "" + dev_container_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_CONTAINER_IMAGE"] + dev_file_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_FILE_IMAGE"] + recommended_extensions: Sequence[str] = field( + default_factory=lambda: GLOBAL_TEMPLATE_VARS["RECOMMENDED_EXTENSIONS"], + ) + namespace: str = "" + scm_org: str = "" + scm_project: str = "" diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index 351d7e8e..74d59544 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -2,24 +2,25 @@ from __future__ import annotations +import copy import os -from dataclasses import dataclass, field +from dataclasses import dataclass from importlib import resources as impl_resources from pathlib import Path from typing import TYPE_CHECKING import yaml -from ansible_creator.constants import GLOBAL_TEMPLATE_VARS, SKIP_DIRS, SKIP_FILES_TYPES +from ansible_creator.constants import SKIP_DIRS, SKIP_FILES_TYPES if TYPE_CHECKING: - from collections.abc import Sequence from ansible_creator.compat import Traversable from ansible_creator.output import Output from ansible_creator.templar import Templar + from ansible_creator.types import TemplateData PATH_REPLACERS = { @@ -72,22 +73,22 @@ class Copier: resource_id: The id of the resource to copy. dest: The destination path to copy resources to. output: An instance of the Output class. + template_data: A dictionary containing the original data to render templates with. allow_overwrite: A list of paths that should be overwritten at destination. index: Index of the current resource being copied. resource_root: Root path for the resources. templar: An instance of the Templar class. - template_data: A dictionary containing the original data to render templates with. """ resources: list[str] resource_id: str dest: Path output: Output + template_data: TemplateData allow_overwrite: list[str] | None = None index: int = 0 resource_root: str = "ansible_creator.resources" templar: Templar | None = None - template_data: dict[str, Sequence[str]] = field(default_factory=dict) @property def resource(self: Copier) -> str: @@ -97,16 +98,13 @@ def resource(self: Copier) -> str: def _recursive_copy( # noqa: C901, PLR0912 self: Copier, root: Traversable, - template_data: dict[str, Sequence[str]], + template_data: TemplateData, ) -> None: """Recursively traverses a resource container and copies content to destination. Args: root: A traversable object representing root of the container to copy. template_data: A dictionary containing current data to render templates with. - - Raises: - TypeError: If template_data for PATH_REPLACERS value is not a string. """ self.output.debug(msg=f"current root set to {root}") @@ -124,10 +122,7 @@ def _recursive_copy( # noqa: C901, PLR0912 for key, val in PATH_REPLACERS.items(): if key in str(dest_path) and template_data: str_dest_path = str(dest_path) - repl_val = template_data.get(val, "") - if not isinstance(repl_val, str): - msg = "template_data for PATH_REPLACERS value must be a string" - raise TypeError(msg) + repl_val = getattr(template_data, val) dest_path = Path(str_dest_path.replace(key, repl_val)) if obj.is_dir(): @@ -180,11 +175,8 @@ def _per_container(self: Copier) -> None: ) self.output.debug(msg=f"allow_overwrite set to {self.allow_overwrite}") - # Include the global template variables - self.template_data.update(GLOBAL_TEMPLATE_VARS) - - # Copy the template data to not pollute the original - template_data = self.template_data.copy() + # Cast the template data to not pollute the original + template_data = copy.deepcopy(self.template_data) # Collect and template any resource specific variables meta_file = impl_resources.files(f"{self.resource_root}.{self.resource}") / "__meta__.yml" @@ -207,9 +199,9 @@ def _per_container(self: Copier) -> None: data=template_data, ) deserialized = yaml.safe_load(templated) - template_data.update({key: deserialized}) + setattr(template_data, key, deserialized) else: - template_data.update({key: value["value"]}) + setattr(template_data, key, value["value"]) self._recursive_copy( root=impl_resources.files(f"{self.resource_root}.{self.resource}"), diff --git a/tests/units/test_templar.py b/tests/units/test_templar.py index 0a6994c8..25f33842 100644 --- a/tests/units/test_templar.py +++ b/tests/units/test_templar.py @@ -1,27 +1,28 @@ """Tests for templar.""" from ansible_creator.templar import Templar +from ansible_creator.types import TemplateData def test_templar() -> None: """Test templar.""" templar = Templar() - data = {"key": "value"} - template = "{{ key }}" - assert templar.render_from_content(template, data) == "value" + data = TemplateData(collection_name="test") + template = "{{ collection_name }}" + assert templar.render_from_content(template, data) == "test" def test_templar_json_simple() -> None: """Test templar json with a simple structure.""" templar = Templar() - data = {"key": "value"} - template = "{{ key | json }}" - assert templar.render_from_content(template, data) == '"value"' + data = TemplateData(recommended_extensions=["value"]) + template = "{{ recommended_extensions | json }}" + assert templar.render_from_content(template, data) == '["value"]' def test_templar_json_complex() -> None: """Test templar json with a complex structure.""" templar = Templar() - data = {"key": {"sub_key": ["value", "value2"]}} - template = "{{ key | json }}" - assert templar.render_from_content(template, data) == '{"sub_key": ["value", "value2"]}' + data = TemplateData(additions={"key": {"key": {"key": True}}}) + template = "{{ additions | json }}" + assert templar.render_from_content(template, data) == '{"key": {"key": {"key": true}}}' diff --git a/tests/units/test_utils.py b/tests/units/test_utils.py index 970bf0df..600e31c4 100644 --- a/tests/units/test_utils.py +++ b/tests/units/test_utils.py @@ -2,11 +2,7 @@ from pathlib import Path -import pytest - -from ansible_creator.output import Output -from ansible_creator.templar import Templar -from ansible_creator.utils import Copier, expand_path +from ansible_creator.utils import expand_path def test_expand_path() -> None: @@ -18,24 +14,3 @@ def test_expand_path() -> None: assert expand_path("foo") == Path.cwd() / "foo" assert expand_path("$HOME") == home assert expand_path("~/$HOME") == Path(f"{home}/{Path.home()}") - - -def test_copier(output: Output, tmp_path: Path) -> None: - """Test Copier raises type error for path replacers. - - Args: - output: Output object. - tmp_path: Temporary directory. - """ - templar = Templar() - template_data = {"scm_org": True, "scm_project": False} - copier = Copier( - resources=["ansible_project"], - resource_id="ansible_project", - template_data=template_data, # type: ignore[arg-type] - dest=tmp_path, - output=output, - templar=templar, - ) - with pytest.raises(TypeError, match="must be a string"): - copier.copy_containers() From 60ef7df9d05a5371452d2c06b2ba1491dfd28134 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 25 Jun 2024 09:15:18 -0700 Subject: [PATCH 5/5] Update docstring --- src/ansible_creator/types.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index d28f50c8..553d8006 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -14,12 +14,12 @@ class TemplateData: additions: A dictionary containing additional data to add to the gitignore. collection_name: The name of the collection. creator_version: The version of the creator. - namespace: The namespace of the collection. - scm_org: The organization of the source control management. - scm_project: The project of the source control management. dev_container_image: The devcontainer image. dev_file_image: The devfile image. + namespace: The namespace of the collection. recommended_extensions: A list of recommended VsCode extensions. + scm_org: The organization of the source control management. + scm_project: The project of the source control management. """ additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) @@ -27,9 +27,9 @@ class TemplateData: creator_version: str = "" dev_container_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_CONTAINER_IMAGE"] dev_file_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_FILE_IMAGE"] + namespace: str = "" recommended_extensions: Sequence[str] = field( default_factory=lambda: GLOBAL_TEMPLATE_VARS["RECOMMENDED_EXTENSIONS"], ) - namespace: str = "" scm_org: str = "" scm_project: str = ""