From d1d7ada62e6147db7e5bef537a816aea6410532f Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Mon, 8 Dec 2025 16:46:36 +0100 Subject: [PATCH] Remove copier and provide workaround for 'infrahubctl repository init' --- changelog/+470be9b4.removed.md | 1 + .../infrahubctl/infrahubctl-repository.mdx | 11 +-- infrahub_sdk/ctl/repository.py | 50 +---------- pyproject.toml | 2 - tests/unit/ctl/test_repository_app.py | 73 +--------------- uv.lock | 87 ------------------- 6 files changed, 9 insertions(+), 215 deletions(-) create mode 100644 changelog/+470be9b4.removed.md diff --git a/changelog/+470be9b4.removed.md b/changelog/+470be9b4.removed.md new file mode 100644 index 00000000..26a75c74 --- /dev/null +++ b/changelog/+470be9b4.removed.md @@ -0,0 +1 @@ +Removed: Removed copier as a dependency, this impacts the `infrahub repository init` command and contains new instructions for how to initialize a repository from the template. diff --git a/docs/docs/infrahubctl/infrahubctl-repository.mdx b/docs/docs/infrahubctl/infrahubctl-repository.mdx index eed5af5a..519fe0b7 100644 --- a/docs/docs/infrahubctl/infrahubctl-repository.mdx +++ b/docs/docs/infrahubctl/infrahubctl-repository.mdx @@ -55,20 +55,11 @@ Initialize a new Infrahub repository. **Usage**: ```console -$ infrahubctl repository init [OPTIONS] DIRECTORY +$ infrahubctl repository init [OPTIONS] ``` -**Arguments**: - -* `DIRECTORY`: Directory path for the new project. [required] - **Options**: -* `--template TEXT`: Template to use for the new repository. Can be a local path or a git repository URL. [default: https://github.com/opsmill/infrahub-template.git] -* `--data PATH`: Path to YAML file containing answers to CLI prompt. -* `--vcs-ref TEXT`: VCS reference to use for the template. Defaults to HEAD. [default: HEAD] -* `--trust / --no-trust`: Trust the template repository. If set, the template will be cloned without verification. [default: no-trust] -* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml] * `--help`: Show this message and exit. ## `infrahubctl repository list` diff --git a/infrahub_sdk/ctl/repository.py b/infrahub_sdk/ctl/repository.py index 4ce141b8..ec03136a 100644 --- a/infrahub_sdk/ctl/repository.py +++ b/infrahub_sdk/ctl/repository.py @@ -1,11 +1,9 @@ from __future__ import annotations -import asyncio from pathlib import Path import typer import yaml -from copier import run_copy from pydantic import ValidationError from rich.console import Console from rich.table import Table @@ -207,49 +205,9 @@ async def list( @app.command() -async def init( - directory: Path = typer.Argument(help="Directory path for the new project."), - template: str = typer.Option( - default="https://github.com/opsmill/infrahub-template.git", - help="Template to use for the new repository. Can be a local path or a git repository URL.", - ), - data: Path | None = typer.Option(default=None, help="Path to YAML file containing answers to CLI prompt."), - vcs_ref: str | None = typer.Option( - default="HEAD", - help="VCS reference to use for the template. Defaults to HEAD.", - ), - trust: bool | None = typer.Option( - default=False, - help="Trust the template repository. If set, the template will be cloned without verification.", - ), - _: str = CONFIG_PARAM, -) -> None: +async def init() -> None: """Initialize a new Infrahub repository.""" - config_data = None - if data: - try: - with Path.open(data, encoding="utf-8") as file: - config_data = yaml.safe_load(file) - typer.echo(f"Loaded config: {config_data}") - except Exception as exc: - typer.echo(f"Error loading YAML file: {exc}", err=True) - raise typer.Exit(code=1) - - # Allow template to be a local path or a URL - template_source = template or "" - if template and Path(template).exists(): - template_source = str(Path(template).resolve()) - - try: - await asyncio.to_thread( - run_copy, - template_source, - str(directory), - data=config_data, - vcs_ref=vcs_ref, - unsafe=trust, - ) - except Exception as e: - typer.echo(f"Error running copier: {e}", err=True) - raise typer.Exit(code=1) + console.print("The copier tool is not included in the Infrahub SDK CLI due to license restrictions,") + console.print("please run the following command to create a new Infrahub repository project:\n") + console.print("uv tool run --from 'copier' copier copy https://github.com/opsmill/infrahub-template ") diff --git a/pyproject.toml b/pyproject.toml index 52a3a5b9..c8481cff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ ctl = [ "rich>=12,<14", "typer>=0.12.5", "click==8.1.*", - "copier>=9.8.0", "ariadne-codegen==0.15.3", ] @@ -63,7 +62,6 @@ all = [ "rich>=12,<14", "typer>=0.12.5", "click==8.1.*", - "copier>=9.8.0", "ariadne-codegen==0.15.3", ] diff --git a/tests/unit/ctl/test_repository_app.py b/tests/unit/ctl/test_repository_app.py index 5f23bb60..45f567dd 100644 --- a/tests/unit/ctl/test_repository_app.py +++ b/tests/unit/ctl/test_repository_app.py @@ -1,11 +1,8 @@ """Integration tests for infrahubctl commands.""" -import tempfile -from pathlib import Path from unittest import mock import pytest -import yaml from typer.testing import CliRunner from infrahub_sdk.client import InfrahubClient @@ -327,70 +324,6 @@ def test_repo_list(self, mock_repositories_list) -> None: def test_repo_init(self) -> None: """Test the repository init command.""" - with ( - tempfile.TemporaryDirectory() as temp_dst, - tempfile.NamedTemporaryFile(mode="w", suffix=".yml", delete=False, encoding="utf-8") as temp_yaml, - ): - dst = Path(temp_dst) - yaml_path = Path(temp_yaml.name) - commit = "v0.0.1" - - answers = { - "generators": True, - "menus": True, - "project_name": "test", - "queries": True, - "scripts": True, - "tests": True, - "transforms": True, - "package_mode": False, - "_commit": commit, - } - - yaml.safe_dump(answers, temp_yaml) - temp_yaml.close() - runner.invoke(app, ["repository", "init", str(dst), "--data", str(yaml_path), "--vcs-ref", commit]) - coppied_answers = yaml.safe_load((dst / ".copier-answers.yml").read_text()) - coppied_answers.pop("_src_path") - - assert coppied_answers == answers - assert (dst / "generators").is_dir() - assert (dst / "queries").is_dir() - assert (dst / "scripts").is_dir() - assert (dst / "pyproject.toml").is_file() - - def test_repo_init_local_template(self) -> None: - """Test the repository init command with a local template.""" - with ( - tempfile.TemporaryDirectory() as temp_src, - tempfile.TemporaryDirectory() as temp_dst, - tempfile.NamedTemporaryFile(mode="w", suffix=".yml", delete=False, encoding="utf-8") as temp_yaml, - ): - src = Path(temp_src) - dst = Path(temp_dst) - - # Create a simple copier template - (src / "copier.yml").write_text("project_name:\n type: str") - template_dir = src / "{{project_name}}" - template_dir.mkdir() - (template_dir / "file.txt.jinja").write_text("Hello {{ project_name }}") - - # Create answers file - yaml_path = Path(temp_yaml.name) - answers = {"project_name": "local-test"} - yaml.safe_dump(answers, temp_yaml) - temp_yaml.close() - - # Run the command - result = runner.invoke( - app, ["repository", "init", str(dst), "--template", str(src), "--data", str(yaml_path)] - ) - - assert result.exit_code == 0, result.stdout - - # Check the output - project_dir = dst / "local-test" - assert project_dir.is_dir() - output_file = project_dir / "file.txt" - assert output_file.is_file() - assert output_file.read_text() == "Hello local-test" + output = runner.invoke(app, ["repository", "init"]) + raw = strip_color(output.stdout) + assert "uv tool run --from 'copier' copier copy https://github.com/opsmill/infrahub-template" in raw diff --git a/uv.lock b/uv.lock index 8bb95995..1074d2fd 100644 --- a/uv.lock +++ b/uv.lock @@ -311,31 +311,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1b/b1/5745d7523d8ce53b87779f46ef6cf5c5c342997939c2fe967e607b944e43/coolname-2.2.0-py2.py3-none-any.whl", hash = "sha256:4d1563186cfaf71b394d5df4c744f8c41303b6846413645e31d31915cdeb13e8", size = 37849, upload-time = "2023-01-09T14:50:39.897Z" }, ] -[[package]] -name = "copier" -version = "9.10.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, - { name = "dunamai" }, - { name = "funcy" }, - { name = "jinja2" }, - { name = "jinja2-ansible-filters" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "plumbum" }, - { name = "pydantic" }, - { name = "pygments" }, - { name = "pyyaml" }, - { name = "questionary" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/c6/1f61fe2b011347c0b9d7637d02ef3dc1c2874640a4fd5fb059ea8b5493f2/copier-9.10.3.tar.gz", hash = "sha256:6e965d8f719678ee3bc5e611ef0d1b182d6b01a3d5385a5f1ba43aaade51caf7", size = 598697, upload-time = "2025-10-17T18:26:04.217Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/f2/58ee35dbd55d0ffa13197365ec4725c36da2f7ec72eaa1dd154bfbd2676e/copier-9.10.3-py3-none-any.whl", hash = "sha256:7165239566f68e9e36c148f71e0552bf1e4911eef96bfa7cf33e6a0dbfed3c96", size = 56186, upload-time = "2025-10-17T18:26:02.856Z" }, -] - [[package]] name = "coverage" version = "7.11.0" @@ -496,18 +471,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/30/d5c337fcf96e2b159b61053fd5b374b80629d28267f471483a3da5577ce3/dulwich-0.24.7-py3-none-any.whl", hash = "sha256:c1d6e35d7c41982d4ba375ce8ba9db783f4b4ca1a00c62f3121eb881f5c03c53", size = 545605, upload-time = "2025-10-23T11:01:33.566Z" }, ] -[[package]] -name = "dunamai" -version = "1.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/2f/194d9a34c4d831c6563d2d990720850f0baef9ab60cb4ad8ae0eff6acd34/dunamai-1.25.0.tar.gz", hash = "sha256:a7f8360ea286d3dbaf0b6a1473f9253280ac93d619836ad4514facb70c0719d1", size = 46155, upload-time = "2025-07-04T19:25:56.082Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/41/04e2a649058b0713b00d6c9bd22da35618bb157289e05d068e51fddf8d7e/dunamai-1.25.0-py3-none-any.whl", hash = "sha256:7f9dc687dd3256e613b6cc978d9daabfd2bb5deb8adc541fc135ee423ffa98ab", size = 27022, upload-time = "2025-07-04T19:25:54.863Z" }, -] - [[package]] name = "exceptiongroup" version = "1.3.0" @@ -571,15 +534,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, ] -[[package]] -name = "funcy" -version = "2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/b8/c6081521ff70afdff55cd9512b2220bbf4fa88804dae51d1b57b4b58ef32/funcy-2.0.tar.gz", hash = "sha256:3963315d59d41c6f30c04bc910e10ab50a3ac4a225868bfa96feed133df075cb", size = 537931, upload-time = "2023-03-28T06:22:46.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl", hash = "sha256:53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0", size = 30891, upload-time = "2023-03-28T06:22:42.576Z" }, -] - [[package]] name = "graphql-core" version = "3.2.6" @@ -742,7 +696,6 @@ dependencies = [ all = [ { name = "ariadne-codegen" }, { name = "click" }, - { name = "copier" }, { name = "jinja2" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -755,7 +708,6 @@ all = [ ctl = [ { name = "ariadne-codegen" }, { name = "click" }, - { name = "copier" }, { name = "jinja2" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -816,8 +768,6 @@ requires-dist = [ { name = "ariadne-codegen", marker = "extra == 'ctl'", specifier = "==0.15.3" }, { name = "click", marker = "extra == 'all'", specifier = "==8.1.*" }, { name = "click", marker = "extra == 'ctl'", specifier = "==8.1.*" }, - { name = "copier", marker = "extra == 'all'", specifier = ">=9.8.0" }, - { name = "copier", marker = "extra == 'ctl'", specifier = ">=9.8.0" }, { name = "dulwich", specifier = ">=0.21.4" }, { name = "graphql-core", specifier = ">=3.1,<3.3" }, { name = "httpx", specifier = ">=0.20" }, @@ -1021,19 +971,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] -[[package]] -name = "jinja2-ansible-filters" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/27/fa186af4b246eb869ffca8ffa42d92b05abaec08c99329e74d88b2c46ec7/jinja2-ansible-filters-1.3.2.tar.gz", hash = "sha256:07c10cf44d7073f4f01102ca12d9a2dc31b41d47e4c61ed92ef6a6d2669b356b", size = 16945, upload-time = "2022-06-30T14:08:50.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl", hash = "sha256:e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34", size = 18975, upload-time = "2022-06-30T14:08:49.571Z" }, -] - [[package]] name = "jsonpatch" version = "1.33" @@ -1558,18 +1495,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "plumbum" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f0/5d/49ba324ad4ae5b1a4caefafbce7a1648540129344481f2ed4ef6bb68d451/plumbum-1.9.0.tar.gz", hash = "sha256:e640062b72642c3873bd5bdc3effed75ba4d3c70ef6b6a7b907357a84d909219", size = 319083, upload-time = "2024-10-05T05:59:27.059Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/9d/d03542c93bb3d448406731b80f39c3d5601282f778328c22c77d270f4ed4/plumbum-1.9.0-py3-none-any.whl", hash = "sha256:9fd0d3b0e8d86e4b581af36edf3f3bbe9d1ae15b45b8caab28de1bcb27aaa7f5", size = 127970, upload-time = "2024-10-05T05:59:25.102Z" }, -] - [[package]] name = "pprintpp" version = "0.4.0" @@ -2114,18 +2039,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, ] -[[package]] -name = "questionary" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "prompt-toolkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/45/eafb0bba0f9988f6a2520f9ca2df2c82ddfa8d67c95d6625452e97b204a5/questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d", size = 25845, upload-time = "2025-08-28T19:00:20.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, -] - [[package]] name = "referencing" version = "0.37.0"