opsmith-cli 0.4.0b0__tar.gz → 0.4.1b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/.gitignore +2 -1
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/PKG-INFO +2 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/deployment_strategies/base.py +11 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/deployment_strategies/monolithic.py +332 -54
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/main.py +91 -51
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/models.py +61 -3
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_deploy/aws/inventory.yml +3 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_deploy/aws/main.yml +5 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_deploy/gcp/main.yml +5 -0
- {opsmith_cli-0.4.0b0/opsmith/templates/fetch_remote_files → opsmith_cli-0.4.1b0/opsmith/templates/docker_compose_run}/aws/inventory.yml +3 -2
- {opsmith_cli-0.4.0b0/opsmith/templates/docker_compose_run → opsmith_cli-0.4.1b0/opsmith/templates/fetch_remote_files}/aws/inventory.yml +3 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/aws/main.tf +11 -1
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/aws/outputs.tf +5 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine_setup/aws/inventory.yml +3 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine_setup/aws/main.yml +5 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/types.py +6 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/pyproject.toml +2 -2
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/uv.lock +1533 -100
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/.flake8 +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/.github/workflows/python-publish.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/.pre-commit-config.yaml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/CONVENTIONS.md +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/LICENSE.txt +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/README.md +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/__init__.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/agent.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/cloud_providers/__init__.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/cloud_providers/aws.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/cloud_providers/base.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/cloud_providers/gcp.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/constants.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/deployment_strategies/__init__.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/exceptions.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/git_repo.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/infra_provisioners/__init__.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/infra_provisioners/ansible_provisioner.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/infra_provisioners/base_provisioner.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/infra_provisioners/terraform_provisioner.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/prompts.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/README.md +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/arduino-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/c-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/chatito-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/commonlisp-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/cpp-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/csharp-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/d-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/dart-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/elisp-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/elixir-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/elm-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/gleam-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/go-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/hcl-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/java-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/javascript-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/lua-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/php-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/pony-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/properties-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/python-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/ql-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/r-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/racket-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/ruby-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/rust-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/scala-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/solidity-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/swift-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/typescript-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/queries/tree-sitter-languages/udev-tags.scm +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/repo_map.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/service_detector.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/settings.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/cloud_storage_cleanup/aws/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/cloud_storage_cleanup/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/aws/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/aws/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/aws/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/gcp/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/gcp/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/container_registry/gcp/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_build_push/aws/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_build_push/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_deploy/gcp/inventory.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_run/aws/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_run/gcp/inventory.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_run/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/base.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/elasticsearch.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/kafka.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/mongodb.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/mysql.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/postgresql.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/rabbitmq.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/redis.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/services/backend_api.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/services/backend_worker.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/services/full_stack.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/traefik.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/docker_compose_snippets/weaviate.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/dockerfiles/python_backend_api +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/dockerfiles/python_backend_worker +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/fetch_remote_files/aws/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/fetch_remote_files/gcp/inventory.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/fetch_remote_files/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/aws/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/aws/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/aws/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/gcp/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/gcp/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_bucket_cert/gcp/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/aws/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/aws/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/aws/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/gcp/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/gcp/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_cdn/gcp/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_deploy/aws/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/frontend_deploy/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/aws/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/gcp/main.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/gcp/outputs.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine/gcp/variables.tf +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine_setup/gcp/inventory.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/templates/virtual_machine_setup/gcp/main.yml +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/tests/__init__.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/tests/test_service_detector.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/opsmith/utils.py +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/resources/deploy_demo.gif +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/resources/deploy_demo.svg +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/resources/setup_demo.gif +0 -0
- {opsmith_cli-0.4.0b0 → opsmith_cli-0.4.1b0}/resources/setup_demo.svg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opsmith-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1b0
|
|
4
4
|
Summary: Opsmith is an AI devops engineer in your terminal
|
|
5
5
|
License-Expression: GPL-3.0-only
|
|
6
6
|
License-File: LICENSE.txt
|
|
@@ -28,7 +28,7 @@ Requires-Dist: inquirer>=3.4.0
|
|
|
28
28
|
Requires-Dist: jinja2>=3.1.6
|
|
29
29
|
Requires-Dist: networkx>=3.4.2
|
|
30
30
|
Requires-Dist: pick>=2.4.0
|
|
31
|
-
Requires-Dist: pydantic-ai[logfire]==
|
|
31
|
+
Requires-Dist: pydantic-ai[logfire]==1.27.0
|
|
32
32
|
Requires-Dist: pydantic>=2.11.5
|
|
33
33
|
Requires-Dist: python-dotenv>=1.1.0
|
|
34
34
|
Requires-Dist: pyyaml>=6.0.2
|
|
@@ -101,9 +101,9 @@ class BaseDeploymentStrategy(abc.ABC):
|
|
|
101
101
|
|
|
102
102
|
def __init__(self, agent: Agent, src_dir: Path):
|
|
103
103
|
self.agent = agent
|
|
104
|
-
self.agent_deps = AgentDeps(src_dir=
|
|
104
|
+
self.agent_deps = AgentDeps(src_dir=src_dir)
|
|
105
105
|
self.src_dir = src_dir
|
|
106
|
-
self.deployments_path =
|
|
106
|
+
self.deployments_path = src_dir.joinpath(settings.deployments_dir)
|
|
107
107
|
self.templates_dir = Path(__file__).parent.parent / "templates"
|
|
108
108
|
|
|
109
109
|
def _get_env_state_path(self, environment_name: str) -> Path:
|
|
@@ -476,3 +476,12 @@ class BaseDeploymentStrategy(abc.ABC):
|
|
|
476
476
|
):
|
|
477
477
|
"""Runs a command on a specific service."""
|
|
478
478
|
raise NotImplementedError
|
|
479
|
+
|
|
480
|
+
@abc.abstractmethod
|
|
481
|
+
def update(
|
|
482
|
+
self,
|
|
483
|
+
deployment_config: DeploymentConfig,
|
|
484
|
+
environment: DeploymentEnvironment,
|
|
485
|
+
):
|
|
486
|
+
"""Updates service configuration for an existing deployment."""
|
|
487
|
+
raise NotImplementedError
|
|
@@ -237,6 +237,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
237
237
|
environment: DeploymentEnvironment,
|
|
238
238
|
images: Dict[str, str],
|
|
239
239
|
environment_state: MonolithicDeploymentState,
|
|
240
|
+
existing_env_content: Optional[str] = None,
|
|
240
241
|
):
|
|
241
242
|
base_compose_template = self.docker_compose_snippets_env.get_template("base.yml")
|
|
242
243
|
base_compose = base_compose_template.render(app_name=deployment_config.app_name_slug)
|
|
@@ -276,7 +277,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
276
277
|
|
|
277
278
|
infra_snippets_list = []
|
|
278
279
|
for infra in deployment_config.infra_deps:
|
|
279
|
-
template = self.docker_compose_snippets_env.get_template(f"{infra.provider}.yml")
|
|
280
|
+
template = self.docker_compose_snippets_env.get_template(f"{infra.provider.value}.yml")
|
|
280
281
|
|
|
281
282
|
content = template.render(
|
|
282
283
|
version=infra.version,
|
|
@@ -289,7 +290,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
289
290
|
|
|
290
291
|
services_info_yaml = yaml.dump(services_info)
|
|
291
292
|
|
|
292
|
-
confirmed_env_content = "N/A"
|
|
293
|
+
confirmed_env_content = existing_env_content or "N/A"
|
|
293
294
|
is_successful = False
|
|
294
295
|
docker_compose_content = None
|
|
295
296
|
messages = []
|
|
@@ -372,6 +373,112 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
372
373
|
f"with reason: \n {reason}."
|
|
373
374
|
)
|
|
374
375
|
|
|
376
|
+
@staticmethod
|
|
377
|
+
def _detect_configuration_changes(
|
|
378
|
+
current_config: DeploymentConfig,
|
|
379
|
+
deployed_state: MonolithicDeploymentState,
|
|
380
|
+
) -> Tuple[bool, Dict[str, List]]:
|
|
381
|
+
"""
|
|
382
|
+
Detects changes between current config and deployed state.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
(has_changes, change_details)
|
|
386
|
+
"""
|
|
387
|
+
changes = {
|
|
388
|
+
"services_added": [],
|
|
389
|
+
"services_removed": [],
|
|
390
|
+
"services_modified": [],
|
|
391
|
+
"infra_added": [],
|
|
392
|
+
"infra_removed": [],
|
|
393
|
+
"infra_modified": [],
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
# Get deployed snapshots
|
|
397
|
+
deployed_services = deployed_state.deployed_services or []
|
|
398
|
+
deployed_infra_deps = deployed_state.deployed_infra_deps or []
|
|
399
|
+
|
|
400
|
+
# Create lookup maps for deployed state
|
|
401
|
+
deployed_services_map = {s["name_slug"]: s for s in deployed_services}
|
|
402
|
+
deployed_infra_map = {i["provider"]: i for i in deployed_infra_deps}
|
|
403
|
+
|
|
404
|
+
# Create lookup maps for current config
|
|
405
|
+
current_services_map = {
|
|
406
|
+
s.name_slug: s.model_dump(mode="json") for s in current_config.services
|
|
407
|
+
}
|
|
408
|
+
current_infra_map = {
|
|
409
|
+
i.provider.value: i.model_dump(mode="json") for i in current_config.infra_deps
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# Detect service changes
|
|
413
|
+
for name_slug, current_service in current_services_map.items():
|
|
414
|
+
if name_slug not in deployed_services_map:
|
|
415
|
+
changes["services_added"].append(name_slug)
|
|
416
|
+
else:
|
|
417
|
+
deployed_service = deployed_services_map[name_slug]
|
|
418
|
+
# Check for modifications (port, service_type, env_vars)
|
|
419
|
+
if (
|
|
420
|
+
current_service.get("service_port") != deployed_service.get("service_port")
|
|
421
|
+
or current_service.get("service_type") != deployed_service.get("service_type")
|
|
422
|
+
or current_service.get("env_vars") != deployed_service.get("env_vars")
|
|
423
|
+
):
|
|
424
|
+
changes["services_modified"].append(name_slug)
|
|
425
|
+
|
|
426
|
+
for name_slug in deployed_services_map:
|
|
427
|
+
if name_slug not in current_services_map:
|
|
428
|
+
changes["services_removed"].append(name_slug)
|
|
429
|
+
|
|
430
|
+
# Detect infrastructure changes
|
|
431
|
+
for provider, current_infra in current_infra_map.items():
|
|
432
|
+
if provider not in deployed_infra_map:
|
|
433
|
+
changes["infra_added"].append(provider)
|
|
434
|
+
else:
|
|
435
|
+
deployed_infra = deployed_infra_map[provider]
|
|
436
|
+
# Check for version changes
|
|
437
|
+
if current_infra.get("version") != deployed_infra.get("version"):
|
|
438
|
+
changes["infra_modified"].append(provider)
|
|
439
|
+
|
|
440
|
+
for provider in deployed_infra_map:
|
|
441
|
+
if provider not in current_infra_map:
|
|
442
|
+
changes["infra_removed"].append(provider)
|
|
443
|
+
|
|
444
|
+
# Determine if there are any changes
|
|
445
|
+
has_changes = any(
|
|
446
|
+
changes["services_added"]
|
|
447
|
+
or changes["services_removed"]
|
|
448
|
+
or changes["services_modified"]
|
|
449
|
+
or changes["infra_added"]
|
|
450
|
+
or changes["infra_removed"]
|
|
451
|
+
or changes["infra_modified"]
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
return has_changes, changes
|
|
455
|
+
|
|
456
|
+
@staticmethod
|
|
457
|
+
def _get_frontend_services(deployment_config: DeploymentConfig) -> List[ServiceInfo]:
|
|
458
|
+
"""Returns list of frontend services."""
|
|
459
|
+
return [s for s in deployment_config.services if s.service_type == ServiceTypeEnum.FRONTEND]
|
|
460
|
+
|
|
461
|
+
@staticmethod
|
|
462
|
+
def _get_backend_services(deployment_config: DeploymentConfig) -> List[ServiceInfo]:
|
|
463
|
+
"""Returns list of non-frontend services."""
|
|
464
|
+
return [s for s in deployment_config.services if s.service_type != ServiceTypeEnum.FRONTEND]
|
|
465
|
+
|
|
466
|
+
@staticmethod
|
|
467
|
+
def _save_deployment_state(
|
|
468
|
+
env_state: MonolithicDeploymentState,
|
|
469
|
+
deployment_config: DeploymentConfig,
|
|
470
|
+
env_state_path: Path,
|
|
471
|
+
):
|
|
472
|
+
"""Saves deployment state with current config snapshots."""
|
|
473
|
+
env_state.deployed_services = [
|
|
474
|
+
s.model_dump(mode="json") for s in deployment_config.services
|
|
475
|
+
]
|
|
476
|
+
env_state.deployed_infra_deps = [
|
|
477
|
+
i.model_dump(mode="json") for i in deployment_config.infra_deps
|
|
478
|
+
]
|
|
479
|
+
env_state.save(env_state_path)
|
|
480
|
+
print(f"\n[bold green]Deployment state saved to {env_state_path}[/bold green]")
|
|
481
|
+
|
|
375
482
|
@staticmethod
|
|
376
483
|
def _prompt_for_build_env_vars(
|
|
377
484
|
service: ServiceInfo, existing_vars: Optional[dict] = None
|
|
@@ -605,6 +712,55 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
605
712
|
print("[bold red]DNS configuration not confirmed. Aborting deployment.[/bold red]")
|
|
606
713
|
raise MonolithicDeploymentError("User did not confirm DNS configuration.")
|
|
607
714
|
|
|
715
|
+
def _deploy_frontend_service(
|
|
716
|
+
self,
|
|
717
|
+
deployment_config: DeploymentConfig,
|
|
718
|
+
environment: DeploymentEnvironment,
|
|
719
|
+
domain_info: DomainInfo,
|
|
720
|
+
cloud_provider: BaseCloudProvider,
|
|
721
|
+
service: ServiceInfo,
|
|
722
|
+
env_state: MonolithicDeploymentState,
|
|
723
|
+
):
|
|
724
|
+
"""Deploys a frontend service to a new deployment environment."""
|
|
725
|
+
cdn_part1_outputs = self._create_frontend_bucket_cert(
|
|
726
|
+
deployment_config, environment, service, domain_info, cloud_provider
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
cdn_part2_outputs = self._create_frontend_cdn(
|
|
730
|
+
deployment_config,
|
|
731
|
+
environment,
|
|
732
|
+
service,
|
|
733
|
+
domain_info,
|
|
734
|
+
cloud_provider,
|
|
735
|
+
cdn_part1_outputs,
|
|
736
|
+
)
|
|
737
|
+
cdn_outputs = {**cdn_part1_outputs, **cdn_part2_outputs}
|
|
738
|
+
|
|
739
|
+
build_env_vars = self._prompt_for_build_env_vars(service)
|
|
740
|
+
cdn_state = FrontendCDNState(
|
|
741
|
+
service_name_slug=service.name_slug,
|
|
742
|
+
domain_name=domain_info.domain_name,
|
|
743
|
+
bucket_name=cdn_outputs.get("bucket_name"),
|
|
744
|
+
cdn_domain_name=cdn_outputs.get("cdn_domain_name"),
|
|
745
|
+
cdn_ip_address=cdn_outputs.get("cdn_ip_address"),
|
|
746
|
+
cdn_distribution_id=cdn_outputs.get("cdn_distribution_id"),
|
|
747
|
+
cdn_url_map=cdn_outputs.get("cdn_url_map"),
|
|
748
|
+
certificate_id=cdn_outputs.get("certificate_id"),
|
|
749
|
+
build_env_vars=build_env_vars,
|
|
750
|
+
)
|
|
751
|
+
env_state.frontend_cdn.append(cdn_state)
|
|
752
|
+
|
|
753
|
+
self._build_and_upload_frontend_assets(
|
|
754
|
+
service,
|
|
755
|
+
cloud_provider,
|
|
756
|
+
environment,
|
|
757
|
+
cdn_state,
|
|
758
|
+
)
|
|
759
|
+
print(
|
|
760
|
+
"\n[bold green]Your website is available at:"
|
|
761
|
+
f" https://{domain_info.domain_name}[/bold green]"
|
|
762
|
+
)
|
|
763
|
+
|
|
608
764
|
def deploy(
|
|
609
765
|
self,
|
|
610
766
|
deployment_config: DeploymentConfig,
|
|
@@ -624,12 +780,8 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
624
780
|
:type environment: DeploymentEnvironment
|
|
625
781
|
:return: None
|
|
626
782
|
"""
|
|
627
|
-
frontend_services =
|
|
628
|
-
|
|
629
|
-
]
|
|
630
|
-
other_services = [
|
|
631
|
-
s for s in deployment_config.services if s.service_type != ServiceTypeEnum.FRONTEND
|
|
632
|
-
]
|
|
783
|
+
frontend_services = self._get_frontend_services(deployment_config)
|
|
784
|
+
other_services = self._get_backend_services(deployment_config)
|
|
633
785
|
|
|
634
786
|
cloud_provider = environment.cloud_provider_instance
|
|
635
787
|
env_state_path = self._get_env_state_path(environment.name)
|
|
@@ -647,44 +799,8 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
647
799
|
" Skipping.[/bold red]"
|
|
648
800
|
)
|
|
649
801
|
continue
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
deployment_config, environment, service, domain_info, cloud_provider
|
|
653
|
-
)
|
|
654
|
-
|
|
655
|
-
cdn_part2_outputs = self._create_frontend_cdn(
|
|
656
|
-
deployment_config,
|
|
657
|
-
environment,
|
|
658
|
-
service,
|
|
659
|
-
domain_info,
|
|
660
|
-
cloud_provider,
|
|
661
|
-
cdn_part1_outputs,
|
|
662
|
-
)
|
|
663
|
-
cdn_outputs = {**cdn_part1_outputs, **cdn_part2_outputs}
|
|
664
|
-
|
|
665
|
-
build_env_vars = self._prompt_for_build_env_vars(service)
|
|
666
|
-
cdn_state = FrontendCDNState(
|
|
667
|
-
service_name_slug=service.name_slug,
|
|
668
|
-
domain_name=domain_info.domain_name,
|
|
669
|
-
bucket_name=cdn_outputs.get("bucket_name"),
|
|
670
|
-
cdn_domain_name=cdn_outputs.get("cdn_domain_name"),
|
|
671
|
-
cdn_ip_address=cdn_outputs.get("cdn_ip_address"),
|
|
672
|
-
cdn_distribution_id=cdn_outputs.get("cdn_distribution_id"),
|
|
673
|
-
cdn_url_map=cdn_outputs.get("cdn_url_map"),
|
|
674
|
-
certificate_id=cdn_outputs.get("certificate_id"),
|
|
675
|
-
build_env_vars=build_env_vars,
|
|
676
|
-
)
|
|
677
|
-
env_state.frontend_cdn.append(cdn_state)
|
|
678
|
-
|
|
679
|
-
self._build_and_upload_frontend_assets(
|
|
680
|
-
service,
|
|
681
|
-
cloud_provider,
|
|
682
|
-
environment,
|
|
683
|
-
cdn_state,
|
|
684
|
-
)
|
|
685
|
-
print(
|
|
686
|
-
"\n[bold green]Your website is available at:"
|
|
687
|
-
f" https://{domain_info.domain_name}[/bold green]"
|
|
802
|
+
self._deploy_frontend_service(
|
|
803
|
+
deployment_config, environment, domain_info, cloud_provider, service, env_state
|
|
688
804
|
)
|
|
689
805
|
|
|
690
806
|
if other_services:
|
|
@@ -738,8 +854,8 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
738
854
|
f" https://{domain.domain_name}[/bold green]"
|
|
739
855
|
)
|
|
740
856
|
|
|
741
|
-
|
|
742
|
-
|
|
857
|
+
# Save config snapshots for change detection
|
|
858
|
+
self._save_deployment_state(env_state, deployment_config, env_state_path)
|
|
743
859
|
|
|
744
860
|
def release(
|
|
745
861
|
self,
|
|
@@ -753,9 +869,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
753
869
|
state_updated = False
|
|
754
870
|
|
|
755
871
|
# Release frontend services
|
|
756
|
-
frontend_services =
|
|
757
|
-
s for s in deployment_config.services if s.service_type == ServiceTypeEnum.FRONTEND
|
|
758
|
-
]
|
|
872
|
+
frontend_services = self._get_frontend_services(deployment_config)
|
|
759
873
|
if frontend_services:
|
|
760
874
|
print("\n[bold blue]Releasing frontend services...[/bold blue]")
|
|
761
875
|
cdn_state_map = {cdn.service_name_slug: cdn for cdn in env_state.frontend_cdn}
|
|
@@ -788,9 +902,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
788
902
|
)
|
|
789
903
|
|
|
790
904
|
# Release other services
|
|
791
|
-
other_services =
|
|
792
|
-
s for s in deployment_config.services if s.service_type != ServiceTypeEnum.FRONTEND
|
|
793
|
-
]
|
|
905
|
+
other_services = self._get_backend_services(deployment_config)
|
|
794
906
|
|
|
795
907
|
if other_services:
|
|
796
908
|
if env_state.virtual_machine:
|
|
@@ -1025,3 +1137,169 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
|
|
|
1025
1137
|
"main.yml",
|
|
1026
1138
|
extra_vars=extra_vars,
|
|
1027
1139
|
)
|
|
1140
|
+
|
|
1141
|
+
def update(
|
|
1142
|
+
self,
|
|
1143
|
+
deployment_config: DeploymentConfig,
|
|
1144
|
+
environment: DeploymentEnvironment,
|
|
1145
|
+
):
|
|
1146
|
+
"""
|
|
1147
|
+
Updates service configuration for an existing deployment.
|
|
1148
|
+
|
|
1149
|
+
Handles:
|
|
1150
|
+
- Service additions/removals
|
|
1151
|
+
- Infrastructure dependency changes
|
|
1152
|
+
- Port/configuration changes
|
|
1153
|
+
"""
|
|
1154
|
+
print("\n[bold blue]Starting configuration update...[/bold blue]")
|
|
1155
|
+
|
|
1156
|
+
# Load existing state
|
|
1157
|
+
env_state_path = self._get_env_state_path(environment.name)
|
|
1158
|
+
if not env_state_path.exists():
|
|
1159
|
+
print(
|
|
1160
|
+
"[bold red]No deployment found for this environment. Please run 'deploy'"
|
|
1161
|
+
" first.[/bold red]"
|
|
1162
|
+
)
|
|
1163
|
+
raise MonolithicDeploymentError(
|
|
1164
|
+
f"No state file found at {env_state_path}. Run 'deploy' first."
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
env_state = MonolithicDeploymentState.load(env_state_path)
|
|
1168
|
+
|
|
1169
|
+
# Detect changes
|
|
1170
|
+
has_changes, changes = self._detect_configuration_changes(deployment_config, env_state)
|
|
1171
|
+
|
|
1172
|
+
if not has_changes:
|
|
1173
|
+
print("[bold green]No configuration changes detected. Nothing to update.[/bold green]")
|
|
1174
|
+
return
|
|
1175
|
+
|
|
1176
|
+
# Display detected changes
|
|
1177
|
+
print("\n[bold]Configuration changes detected:[/bold]")
|
|
1178
|
+
if changes["services_added"]:
|
|
1179
|
+
print(f" [green]Services added:[/green] {', '.join(changes['services_added'])}")
|
|
1180
|
+
if changes["services_removed"]:
|
|
1181
|
+
print(f" [red]Services removed:[/red] {', '.join(changes['services_removed'])}")
|
|
1182
|
+
if changes["services_modified"]:
|
|
1183
|
+
print(
|
|
1184
|
+
f" [yellow]Services modified:[/yellow] {', '.join(changes['services_modified'])}"
|
|
1185
|
+
)
|
|
1186
|
+
if changes["infra_added"]:
|
|
1187
|
+
print(f" [green]Infrastructure added:[/green] {', '.join(changes['infra_added'])}")
|
|
1188
|
+
if changes["infra_removed"]:
|
|
1189
|
+
print(f" [red]Infrastructure removed:[/red] {', '.join(changes['infra_removed'])}")
|
|
1190
|
+
if changes["infra_modified"]:
|
|
1191
|
+
print(
|
|
1192
|
+
" [yellow]Infrastructure modified:[/yellow]"
|
|
1193
|
+
f" {', '.join(changes['infra_modified'])}"
|
|
1194
|
+
)
|
|
1195
|
+
|
|
1196
|
+
# Warn about infrastructure changes
|
|
1197
|
+
infra_changes = (
|
|
1198
|
+
changes["infra_added"] or changes["infra_removed"] or changes["infra_modified"]
|
|
1199
|
+
)
|
|
1200
|
+
if infra_changes:
|
|
1201
|
+
print(
|
|
1202
|
+
"\n[bold yellow]WARNING: Infrastructure changes detected. Existing data in affected"
|
|
1203
|
+
" services may be lost.[/bold yellow]"
|
|
1204
|
+
)
|
|
1205
|
+
confirm_questions = [
|
|
1206
|
+
inquirer.Confirm(
|
|
1207
|
+
"continue",
|
|
1208
|
+
message="Do you want to continue with the update?",
|
|
1209
|
+
default=False,
|
|
1210
|
+
)
|
|
1211
|
+
]
|
|
1212
|
+
confirm_answers = inquirer.prompt(confirm_questions)
|
|
1213
|
+
if not confirm_answers or not confirm_answers.get("continue"):
|
|
1214
|
+
print("[bold yellow]Update cancelled by user.[/bold yellow]")
|
|
1215
|
+
return
|
|
1216
|
+
|
|
1217
|
+
cloud_provider = environment.cloud_provider_instance
|
|
1218
|
+
|
|
1219
|
+
# Handle frontend services
|
|
1220
|
+
frontend_services = self._get_frontend_services(deployment_config)
|
|
1221
|
+
if frontend_services:
|
|
1222
|
+
print("\n[bold blue]Updating frontend services...[/bold blue]")
|
|
1223
|
+
cdn_state_map = {cdn.service_name_slug: cdn for cdn in env_state.frontend_cdn}
|
|
1224
|
+
domains_map = {d.service_name_slug: d for d in environment.domains}
|
|
1225
|
+
|
|
1226
|
+
for service in frontend_services:
|
|
1227
|
+
cdn_state = cdn_state_map.get(service.name_slug)
|
|
1228
|
+
if not cdn_state:
|
|
1229
|
+
# New frontend service - create CDN
|
|
1230
|
+
domain_info = domains_map.get(service.name_slug)
|
|
1231
|
+
if not domain_info:
|
|
1232
|
+
# This should not happen if main.py did its job
|
|
1233
|
+
print(
|
|
1234
|
+
f"[bold yellow]No domain configured for '{service.name_slug}'. "
|
|
1235
|
+
"Skipping CDN creation.[/bold yellow]"
|
|
1236
|
+
)
|
|
1237
|
+
continue
|
|
1238
|
+
self._deploy_frontend_service(
|
|
1239
|
+
deployment_config,
|
|
1240
|
+
environment,
|
|
1241
|
+
domain_info,
|
|
1242
|
+
cloud_provider,
|
|
1243
|
+
service,
|
|
1244
|
+
env_state,
|
|
1245
|
+
)
|
|
1246
|
+
else:
|
|
1247
|
+
# Existing service - update build env vars and assets
|
|
1248
|
+
build_env_vars = self._prompt_for_build_env_vars(
|
|
1249
|
+
service, existing_vars=cdn_state.build_env_vars
|
|
1250
|
+
)
|
|
1251
|
+
if cdn_state.build_env_vars != build_env_vars:
|
|
1252
|
+
cdn_state.build_env_vars = build_env_vars
|
|
1253
|
+
|
|
1254
|
+
self._build_and_upload_frontend_assets(
|
|
1255
|
+
service, cloud_provider, environment, cdn_state
|
|
1256
|
+
)
|
|
1257
|
+
print(
|
|
1258
|
+
f"[bold green]Frontend service '{service.name_slug}' updated"
|
|
1259
|
+
" successfully.[/bold green]"
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
# Handle backend services
|
|
1263
|
+
other_services = [
|
|
1264
|
+
s for s in deployment_config.services if s.service_type != ServiceTypeEnum.FRONTEND
|
|
1265
|
+
]
|
|
1266
|
+
|
|
1267
|
+
if other_services:
|
|
1268
|
+
if not env_state.virtual_machine:
|
|
1269
|
+
print(
|
|
1270
|
+
"[bold yellow]No virtual machine provisioned for this environment. Cannot"
|
|
1271
|
+
" update backend services.[/bold yellow]"
|
|
1272
|
+
)
|
|
1273
|
+
return
|
|
1274
|
+
|
|
1275
|
+
print("\n[bold blue]Updating backend services...[/bold blue]")
|
|
1276
|
+
|
|
1277
|
+
# Rebuild and push images
|
|
1278
|
+
images = self._build_and_push_images(
|
|
1279
|
+
deployment_config, environment, env_state.registry_url
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
# Fetch existing .env file
|
|
1283
|
+
env_file_path = f"/home/{env_state.virtual_machine.user}/app/.env"
|
|
1284
|
+
fetched_files = self._fetch_remote_deployment_files(
|
|
1285
|
+
deployment_config,
|
|
1286
|
+
environment,
|
|
1287
|
+
env_state.virtual_machine,
|
|
1288
|
+
[env_file_path],
|
|
1289
|
+
)
|
|
1290
|
+
existing_env_content = fetched_files[0]
|
|
1291
|
+
|
|
1292
|
+
# Regenerate docker-compose with existing env as starting point
|
|
1293
|
+
print("\n[bold blue]Regenerating docker-compose configuration...[/bold blue]")
|
|
1294
|
+
self._generate_docker_compose(
|
|
1295
|
+
deployment_config,
|
|
1296
|
+
environment,
|
|
1297
|
+
images,
|
|
1298
|
+
env_state,
|
|
1299
|
+
existing_env_content=existing_env_content,
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
print("[bold green]Backend services updated successfully.[/bold green]")
|
|
1303
|
+
|
|
1304
|
+
# Update state with new config snapshots
|
|
1305
|
+
self._save_deployment_state(env_state, deployment_config, env_state_path)
|