Repository: splunk/addonfactory-ucc-generator (language: python)

Issue:
Refactor: remove cookiecutter and replace it with jinja2 templates
### Description

`cookiecutter` library was used to develop `ucc-gen init` feature quite fast, but it can be replaced by `jinja2` library and we will reduce the number of dependencies we have in the project.


Discussion / hints:

Fix patch:
diff --git a/poetry.lock b/poetry.lock
index 233714b39..9c566b96c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -12,22 +12,6 @@ files = [
     {file = "addonfactory_splunk_conf_parser_lib-0.3.4.tar.gz", hash = "sha256:e550105c8c85a6f553688e5b1f4f55ae7ca758a1a428cbce1e0b9b73b9f4e35d"},
 ]
 
-[[package]]
-name = "arrow"
-version = "1.2.3"
-description = "Better dates & times for Python"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"},
-    {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.7.0"
-typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
-
 [[package]]
 name = "attrs"
 version = "23.1.0"
@@ -65,21 +49,6 @@ files = [
 [package.dependencies]
 pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
 
-[[package]]
-name = "binaryornot"
-version = "0.4.4"
-description = "Ultra-lightweight pure Python package to check if a file is binary or text."
-category = "main"
-optional = false
-python-versions = "*"
-files = [
-    {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"},
-    {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"},
-]
-
-[package.dependencies]
-chardet = ">=3.0.2"
-
 [[package]]
 name = "certifi"
 version = "2023.7.22"
@@ -92,18 +61,6 @@ files = [
     {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
 ]
 
-[[package]]
-name = "chardet"
-version = "5.2.0"
-description = "Universal encoding detector for Python 3"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
-    {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
-]
-
 [[package]]
 name = "charset-normalizer"
 version = "3.2.0"
@@ -193,7 +150,7 @@ files = [
 name = "click"
 version = "8.1.7"
 description = "Composable command line interface toolkit"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -209,7 +166,7 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
 name = "colorama"
 version = "0.4.6"
 description = "Cross-platform colored terminal text."
-category = "main"
+category = "dev"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 files = [
@@ -217,28 +174,6 @@ files = [
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 ]
 
-[[package]]
-name = "cookiecutter"
-version = "2.3.0"
-description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "cookiecutter-2.3.0-py3-none-any.whl", hash = "sha256:7e87944757c6e9f8729cf89a4139b6a35ab4d6dcbc6ae3e7d6360d44ad3ad383"},
-    {file = "cookiecutter-2.3.0.tar.gz", hash = "sha256:942a794981747f6d7f439d6e49d39dc91a9a641283614160c93c474c72c29621"},
-]
-
-[package.dependencies]
-arrow = "*"
-binaryornot = ">=0.4.4"
-click = ">=7.0,<9.0.0"
-Jinja2 = ">=2.7,<4.0.0"
-python-slugify = ">=4.0.0"
-pyyaml = ">=5.3.1"
-requests = ">=2.23.0"
-rich = "*"
-
 [[package]]
 name = "covdefaults"
 version = "2.3.0"
@@ -739,32 +674,6 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.0)", "mkdocs-nature (>=0.4)"]
 testing = ["coverage", "pyyaml"]
 
-[[package]]
-name = "markdown-it-py"
-version = "2.2.0"
-description = "Python port of markdown-it. Markdown parsing, done right!"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"},
-    {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"},
-]
-
-[package.dependencies]
-mdurl = ">=0.1,<1.0"
-typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
-
-[package.extras]
-benchmarking = ["psutil", "pytest", "pytest-benchmark"]
-code-style = ["pre-commit (>=3.0,<4.0)"]
-compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
-linkify = ["linkify-it-py (>=1,<3)"]
-plugins = ["mdit-py-plugins"]
-profiling = ["gprof2dot"]
-rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
-testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
-
 [[package]]
 name = "markupsafe"
 version = "2.1.3"
@@ -835,18 +744,6 @@ files = [
     {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
 ]
 
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-description = "Markdown URL utilities"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
-    {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
 [[package]]
 name = "mergedeep"
 version = "1.3.4"
@@ -1064,7 +961,7 @@ files = [
 name = "pygments"
 version = "2.16.1"
 description = "Pygments is a syntax highlighting package written in Python."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1336,7 +1233,7 @@ testing = ["filelock"]
 name = "python-dateutil"
 version = "2.8.2"
 description = "Extensions to the standard Python datetime module"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 files = [
@@ -1362,24 +1259,6 @@ files = [
 [package.extras]
 cli = ["click (>=5.0)"]
 
-[[package]]
-name = "python-slugify"
-version = "8.0.1"
-description = "A Python slugify application that also handles Unicode"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"},
-    {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"},
-]
-
-[package.dependencies]
-text-unidecode = ">=1.3"
-
-[package.extras]
-unidecode = ["Unidecode (>=1.1.1)"]
-
 [[package]]
 name = "pytz"
 version = "2023.3.post1"
@@ -1587,26 +1466,6 @@ urllib3 = ">=1.21.1,<3"
 socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 
-[[package]]
-name = "rich"
-version = "13.5.3"
-description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-category = "main"
-optional = false
-python-versions = ">=3.7.0"
-files = [
-    {file = "rich-13.5.3-py3-none-any.whl", hash = "sha256:9257b468badc3d347e146a4faa268ff229039d4c2d176ab0cffb4c4fbc73d5d9"},
-    {file = "rich-13.5.3.tar.gz", hash = "sha256:87b43e0543149efa1253f485cd845bb7ee54df16c9617b8a893650ab84b4acb6"},
-]
-
-[package.dependencies]
-markdown-it-py = ">=2.2.0"
-pygments = ">=2.13.0,<3.0.0"
-typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
-
-[package.extras]
-jupyter = ["ipywidgets (>=7.5.1,<9)"]
-
 [[package]]
 name = "selenium"
 version = "3.141.0"
@@ -1626,7 +1485,7 @@ urllib3 = "*"
 name = "six"
 version = "1.16.0"
 description = "Python 2 and 3 compatibility utilities"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 files = [
@@ -1662,18 +1521,6 @@ defusedxml = ">=0.7.1,<0.8.0"
 httplib2 = ">=0.20.0"
 splunk-sdk = ">=1.6.20"
 
-[[package]]
-name = "text-unidecode"
-version = "1.3"
-description = "The most basic Text::Unidecode port"
-category = "main"
-optional = false
-python-versions = "*"
-files = [
-    {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
-    {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
-]
-
 [[package]]
 name = "tomli"
 version = "2.0.1"
@@ -1823,4 +1670,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.7"
-content-hash = "8fb72e194efac8d9301a8b23749b554a93770416f81f5803a0127d711dc79332"
+content-hash = "f188c93d24e354caea43bc51bb889e552db5404381fe1a630fb2cbc61257bce9"
diff --git a/pyproject.toml b/pyproject.toml
index 1ef5a0da3..89a4d3267 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,7 +41,6 @@ addonfactory-splunk-conf-parser-lib = "^0.3.3"
 dunamai = "^1.9.0"
 jsonschema = "^4.4.0"
 PyYAML = "^6.0"
-cookiecutter = "^2.1.1"
 openapi3 = "^1.7.0"
 defusedxml = "^0.7.1"
 requests = "^2.31.0"
diff --git a/splunk_add_on_ucc_framework/commands/init.py b/splunk_add_on_ucc_framework/commands/init.py
index d9ff91c5e..c2a5b254c 100644
--- a/splunk_add_on_ucc_framework/commands/init.py
+++ b/splunk_add_on_ucc_framework/commands/init.py
@@ -19,7 +19,9 @@
 import re
 from typing import Optional
 
-from cookiecutter import exceptions, main
+import shutil
+
+from splunk_add_on_ucc_framework import utils
 
 logger = logging.getLogger("ucc_gen")
 
@@ -53,6 +55,97 @@ def _is_valid_input_name(input_name: str) -> bool:
     return _is_valid(ADDON_INPUT_NAME_RE, input_name)
 
 
+def _generate_addon(
+    addon_name: str,
+    addon_display_name: str,
+    addon_input_name: str,
+    addon_version: str,
+    addon_rest_root: Optional[str] = None,
+    overwrite: bool = False,
+) -> str:
+    generated_addon_path = os.path.join(
+        os.getcwd(),
+        addon_name,
+    )
+    if not overwrite and os.path.exists(generated_addon_path):
+        logger.error(
+            "The location is already taken, use `--overwrite` "
+            "option to overwrite the content of existing folder."
+        )
+        sys.exit(1)
+    if overwrite:
+        try:
+            shutil.rmtree(generated_addon_path)
+        except FileNotFoundError:
+            pass
+    os.makedirs(generated_addon_path)
+    readme_path = os.path.join(generated_addon_path, "README.md")
+    with open(readme_path, "w") as _f:
+        _f.write(f"# {addon_name}\n")
+    global_config_path = os.path.join(generated_addon_path, "globalConfig.json")
+    global_config_rendered_content = (
+        utils.get_j2_env()
+        .get_template("globalConfig.json.init-template")
+        .render(
+            addon_name=addon_name,
+            addon_rest_root=addon_rest_root,
+            addon_version=addon_version,
+            addon_display_name=addon_display_name,
+            addon_input_name=addon_input_name,
+        )
+    )
+    with open(global_config_path, "w") as _f:
+        _f.write(global_config_rendered_content)
+    package_path = os.path.join(generated_addon_path, "package")
+    os.makedirs(package_path)
+    package_license_path = os.path.join(package_path, "LICENSE.txt")
+    with open(package_license_path, "w") as _f:
+        pass
+    package_readme_path = os.path.join(package_path, "README.txt")
+    with open(package_readme_path, "w") as _f:
+        pass
+    package_app_manifest_path = os.path.join(package_path, "app.manifest")
+    app_manifest_rendered_content = (
+        utils.get_j2_env()
+        .get_template("app.manifest.init-template")
+        .render(
+            addon_name=addon_name,
+            addon_version=addon_version,
+            addon_display_name=addon_display_name,
+        )
+    )
+    with open(package_app_manifest_path, "w") as _f:
+        _f.write(app_manifest_rendered_content)
+    package_bin_path = os.path.join(package_path, "bin")
+    os.makedirs(package_bin_path)
+    package_bin_input_path = os.path.join(package_bin_path, f"{addon_input_name}.py")
+    input_rendered_content = (
+        utils.get_j2_env()
+        .get_template("input.name.init-template")
+        .render(
+            addon_name=addon_name,
+            addon_rest_root=addon_rest_root,
+            addon_input_name=addon_input_name,
+            addon_version=addon_version,
+            addon_display_name=addon_display_name,
+        )
+    )
+    with open(package_bin_input_path, "w") as _f:
+        _f.write(input_rendered_content + "\n")
+    package_lib_path = os.path.join(package_path, "lib")
+    os.makedirs(package_lib_path)
+    package_lib_requirements_path = os.path.join(package_lib_path, "requirements.txt")
+    with open(package_lib_requirements_path, "w") as _f:
+        _f.writelines(
+            [
+                "splunktaucclib\n",
+                "splunk-sdk\n",
+                "solnlib\n",
+            ]
+        )
+    return generated_addon_path
+
+
 def init(
     addon_name: str,
     addon_display_name: str,
@@ -61,62 +154,48 @@ def init(
     addon_rest_root: Optional[str] = None,
     overwrite: bool = False,
 ) -> str:
-    try:
-        if not _is_valid_addon_name(addon_name):
+    if not _is_valid_addon_name(addon_name):
+        logger.error(
+            f"Add-on name provided is not valid, it should follow '{ADDON_NAME_RE_STR}' regex."
+        )
+        sys.exit(1)
+
+    if addon_rest_root is None:
+        if _is_valid_rest_root_name(addon_name):
+            logger.info(
+                "Parameter `--addon-rest-root` is not provided, using `--addon-name` as a REST root."
+            )
+            addon_rest_root = addon_name
+        else:
             logger.error(
-                f"Add-on name provided is not valid, it should follow '{ADDON_NAME_RE_STR}' regex."
+                f"Can not use add-on name provided as an add-on REST root. "
+                f"It should follow '{ADDON_REST_ROOT_RE_STR}' regex. "
+                f"Please provide `--addon-rest-root` to specify add-on REST root."
             )
             sys.exit(1)
-
-        if addon_rest_root is None:
-            if _is_valid_rest_root_name(addon_name):
-                logger.info(
-                    "Parameter `--addon-rest-root` is not provided, using `--addon-name` as a REST root."
-                )
-                addon_rest_root = addon_name
-            else:
-                logger.error(
-                    f"Can not use add-on name provided as an add-on REST root. "
-                    f"It should follow '{ADDON_REST_ROOT_RE_STR}' regex. "
-                    f"Please provide `--addon-rest-root` to specify add-on REST root."
-                )
-                sys.exit(1)
-        else:
-            if not _is_valid_rest_root_name(addon_rest_root):
-                logger.error(
-                    f"Add-on REST root provided is not valid, it should follow '{ADDON_REST_ROOT_RE_STR}' regex."
-                )
-                sys.exit(1)
-        if not _is_valid_input_name(addon_input_name):
+    else:
+        if not _is_valid_rest_root_name(addon_rest_root):
             logger.error(
-                f"Add-on input name provided is not valid, "
-                f"it should follow '{ADDON_INPUT_NAME_RE_STR}' regex and be less than 50 characters."
+                f"Add-on REST root provided is not valid, it should follow '{ADDON_REST_ROOT_RE_STR}' regex."
             )
             sys.exit(1)
-        generated_addon_path = main.cookiecutter(
-            template=os.path.join(
-                os.path.dirname(__file__),
-                "init_template",
-            ),
-            overwrite_if_exists=overwrite,
-            no_input=True,
-            extra_context={
-                "addon_name": addon_name,
-                "addon_rest_root": addon_rest_root,
-                "addon_display_name": addon_display_name,
-                "addon_input_name": addon_input_name,
-                "addon_version": addon_version,
-            },
-        )
-        logger.info(f"Generated add-on is located here {generated_addon_path}")
-        logger.info(
-            "LICENSE.txt and README.txt are empty, "
-            "you may need to modify the content of those files. "
-        )
-        return generated_addon_path
-    except exceptions.OutputDirExistsException:
+    if not _is_valid_input_name(addon_input_name):
         logger.error(
-            "The location is already taken, use `--overwrite` "
-            "option to overwrite the content of existing folder."
+            f"Add-on input name provided is not valid, "
+            f"it should follow '{ADDON_INPUT_NAME_RE_STR}' regex and be less than 50 characters."
         )
         sys.exit(1)
+    generated_addon_path = _generate_addon(
+        addon_name,
+        addon_display_name,
+        addon_input_name,
+        addon_version,
+        addon_rest_root,
+        overwrite,
+    )
+    logger.info(f"Generated add-on is located here {generated_addon_path}")
+    logger.info(
+        "LICENSE.txt and README.txt are empty, "
+        "you may need to modify the content of those files. "
+    )
+    return generated_addon_path
diff --git a/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json b/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json
deleted file mode 100644
index eafd37031..000000000
--- a/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "addon_name": "",
-    "addon_rest_root": "",
-    "addon_display_name": "",
-    "addon_input_name": "",
-    "addon_version": ""
-}
\ No newline at end of file
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md
deleted file mode 100644
index 8726f0c39..000000000
--- a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-# {{cookiecutter.addon_name}}
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/LICENSE.txt b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/LICENSE.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/README.txt b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/README.txt
deleted file mode 100644
index e69de29bb..000000000
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt
deleted file mode 100644
index 99f4b184d..000000000
--- a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-splunktaucclib
-splunk-sdk
-solnlib
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest b/splunk_add_on_ucc_framework/templates/app.manifest.init-template
similarity index 84%
rename from splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest
rename to splunk_add_on_ucc_framework/templates/app.manifest.init-template
index 4e8435cca..16a839b2c 100644
--- a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest
+++ b/splunk_add_on_ucc_framework/templates/app.manifest.init-template
@@ -1,11 +1,11 @@
 {
     "schemaVersion": "2.0.0",
     "info": {
-        "title": "{{cookiecutter.addon_display_name}}",
+        "title": "{{addon_display_name}}",
         "id": {
             "group": null,
-            "name": "{{cookiecutter.addon_name}}",
-            "version": "{{cookiecutter.addon_version}}"
+            "name": "{{addon_name}}",
+            "version": "{{addon_version}}"
         },
         "author": [
             {
@@ -15,7 +15,7 @@
             }
         ],
         "releaseDate": null,
-        "description": "{{cookiecutter.addon_display_name}}",
+        "description": "{{addon_display_name}}",
         "classification": {
             "intendedAudience": "IT Professionals",
             "categories": [
@@ -54,4 +54,4 @@
         "_search_heads",
         "_indexers"
     ]
-}
\ No newline at end of file
+}
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json b/splunk_add_on_ucc_framework/templates/globalConfig.json.init-template
similarity index 95%
rename from splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json
rename to splunk_add_on_ucc_framework/templates/globalConfig.json.init-template
index bbe673fb3..2a569f77f 100644
--- a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json
+++ b/splunk_add_on_ucc_framework/templates/globalConfig.json.init-template
@@ -93,7 +93,7 @@
         "inputs": {
             "services": [
                 {
-                    "name": "{{cookiecutter.addon_input_name}}",
+                    "name": "{{addon_input_name}}",
                     "entity": [
                         {
                             "type": "text",
@@ -141,7 +141,7 @@
                             "required": true
                         }
                     ],
-                    "title": "{{cookiecutter.addon_input_name}}"
+                    "title": "{{addon_input_name}}"
                 }
             ],
             "title": "Inputs",
@@ -210,10 +210,10 @@
         }
     },
     "meta": {
-        "name": "{{cookiecutter.addon_name}}",
-        "restRoot": "{{cookiecutter.addon_rest_root}}",
-        "version": "{{cookiecutter.addon_version}}",
-        "displayName": "{{cookiecutter.addon_display_name}}",
+        "name": "{{addon_name}}",
+        "restRoot": "{{addon_rest_root}}",
+        "version": "{{addon_version}}",
+        "displayName": "{{addon_display_name}}",
         "schemaVersion": "0.0.3"
     }
-}
\ No newline at end of file
+}
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py b/splunk_add_on_ucc_framework/templates/input.name.init-template
similarity index 88%
rename from splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py
rename to splunk_add_on_ucc_framework/templates/input.name.init-template
index 8b0c7cf72..9b6311cc9 100644
--- a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py
+++ b/splunk_add_on_ucc_framework/templates/input.name.init-template
@@ -7,7 +7,7 @@
 from solnlib import conf_manager, log
 from splunklib import modularinput as smi
 
-ADDON_NAME = "{{cookiecutter.addon_name}}"
+ADDON_NAME = "{{addon_name}}"
 
 
 def logger_for_input(input_name: str) -> logging.Logger:
@@ -18,9 +18,9 @@ def get_account_api_key(session_key: str, account_name: str):
     cfm = conf_manager.ConfManager(
         session_key,
         ADDON_NAME,
-        realm=f"__REST_CREDENTIAL__#{ADDON_NAME}#configs/conf-{{cookiecutter.addon_rest_root}}_account",
+        realm=f"__REST_CREDENTIAL__#{ADDON_NAME}#configs/conf-{{addon_rest_root}}_account",
     )
-    account_conf_file = cfm.get_conf("{{cookiecutter.addon_name}}_account")
+    account_conf_file = cfm.get_conf("{{addon_name}}_account")
     return account_conf_file.get(account_name).get("api_key")
 
 
@@ -42,8 +42,8 @@ def __init__(self):
         super().__init__()
 
     def get_scheme(self):
-        scheme = smi.Scheme("{{cookiecutter.addon_input_name}}")
-        scheme.description = "{{cookiecutter.addon_input_name}} input"
+        scheme = smi.Scheme("{{addon_input_name}}")
+        scheme.description = "{{addon_input_name}} input"
         scheme.use_external_validation = True
         scheme.streaming_mode_xml = True
         scheme.use_single_instance = False
@@ -60,7 +60,7 @@ def validate_input(self, definition: smi.ValidationDefinition):
     def stream_events(self, inputs: smi.InputDefinition, event_writer: smi.EventWriter):
         # inputs.inputs is a Python dictionary object like:
         # {
-        #   "{{cookiecutter.addon_input_name}}://<input_name>": {
+        #   "{{addon_input_name}}://<input_name>": {
         #     "account": "<account_name>",
         #     "disabled": "0",
         #     "host": "$decideOnStartup",
@@ -100,7 +100,7 @@ def stream_events(self, inputs: smi.InputDefinition, event_writer: smi.EventWrit
             except Exception as e:
                 logger.error(
                     f"Exception raised while ingesting data for "
-                    f"{{cookiecutter.addon_input_name}}: {e}. Traceback: "
+                    f"{{addon_input_name}}: {e}. Traceback: "
                     f"{traceback.format_exc()}"
                 )
 


Test patch:
diff --git a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json
index 9d9ef98e2..13a9df8e6 100644
--- a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json
+++ b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json
@@ -1131,7 +1131,7 @@
     "meta": {
         "name": "Splunk_TA_UCCExample",
         "restRoot": "splunk_ta_uccexample",
-        "version": "5.28.6R62b903e8",
+        "version": "5.29.0Rafd1fe38",
         "displayName": "Splunk UCC test Add-on",
         "schemaVersion": "0.0.3"
     }
diff --git a/tests/unit/commands/test_init.py b/tests/unit/commands/test_init.py
index f57cd57b7..7b9ae8c0f 100644
--- a/tests/unit/commands/test_init.py
+++ b/tests/unit/commands/test_init.py
@@ -1,11 +1,7 @@
-import os
 from unittest import mock
 
-import cookiecutter
 import pytest
 
-import tests.unit.helpers as helpers
-
 from splunk_add_on_ucc_framework.commands import init
 
 
@@ -44,9 +40,9 @@ def test__is_valid_input_name(input_name, expected):
     assert init._is_valid_input_name(input_name) is expected
 
 
-@mock.patch("cookiecutter.main.cookiecutter")
+@mock.patch("splunk_add_on_ucc_framework.commands.init._generate_addon")
 @pytest.mark.parametrize(
-    "init_kwargs,expected_extra_content",
+    "init_kwargs,expected_args_to_generate_addon",
     [
         (
             {
@@ -55,13 +51,14 @@ def test__is_valid_input_name(input_name, expected):
                 "addon_input_name": "input_name",
                 "addon_version": "0.0.1",
             },
-            {
-                "addon_name": "addon_name",
-                "addon_rest_root": "addon_name",
-                "addon_display_name": "Addon For Demo",
-                "addon_input_name": "input_name",
-                "addon_version": "0.0.1",
-            },
+            (
+                "addon_name",
+                "Addon For Demo",
+                "input_name",
+                "0.0.1",
+                "addon_name",
+                False,
+            ),
         ),
         (
             {
@@ -71,13 +68,14 @@ def test__is_valid_input_name(input_name, expected):
                 "addon_input_name": "input_name",
                 "addon_version": "0.0.1",
             },
-            {
-                "addon_name": "addon_name",
-                "addon_rest_root": "addon_name",
-                "addon_display_name": "Addon For Demo",
-                "addon_input_name": "input_name",
-                "addon_version": "0.0.1",
-            },
+            (
+                "addon_name",
+                "Addon For Demo",
+                "input_name",
+                "0.0.1",
+                "addon_name",
+                False,
+            ),
         ),
         (
             {
@@ -87,28 +85,21 @@ def test__is_valid_input_name(input_name, expected):
                 "addon_input_name": "input_name",
                 "addon_version": "0.0.1",
             },
-            {
-                "addon_name": "addon_name",
-                "addon_rest_root": "addon_rest_root",
-                "addon_display_name": "Addon For Demo",
-                "addon_input_name": "input_name",
-                "addon_version": "0.0.1",
-            },
+            (
+                "addon_name",
+                "Addon For Demo",
+                "input_name",
+                "0.0.1",
+                "addon_rest_root",
+                False,
+            ),
         ),
     ],
 )
-def test_init(mock_cookiecutter_main_cookiecutter, init_kwargs, expected_extra_content):
+def test_init(mock_generate_addon, init_kwargs, expected_args_to_generate_addon):
     init.init(**init_kwargs)
 
-    expected_path_to_template = os.path.join(
-        helpers.get_path_to_source_dir(), "commands", "init_template"
-    )
-    mock_cookiecutter_main_cookiecutter.assert_called_once_with(
-        template=expected_path_to_template,
-        overwrite_if_exists=False,
-        no_input=True,
-        extra_context=expected_extra_content,
-    )
+    mock_generate_addon.assert_called_once_with(*expected_args_to_generate_addon)
 
 
 @pytest.mark.parametrize(
@@ -155,11 +146,9 @@ def test_init_when_incorrect_parameters_then_sys_exit(init_kwargs):
         init.init(**init_kwargs)
 
 
-@mock.patch("cookiecutter.main.cookiecutter")
-def test_init_when_folder_already_exists(mock_cookiecutter_main_cookiecutter, caplog):
-    mock_cookiecutter_main_cookiecutter.side_effect = (
-        cookiecutter.exceptions.OutputDirExistsException
-    )
+@mock.patch("splunk_add_on_ucc_framework.commands.init._generate_addon")
+def test_init_when_folder_already_exists(mock_generate_addon, caplog):
+    mock_generate_addon.side_effect = SystemExit
 
     with pytest.raises(SystemExit):
         init.init(