supervaizer 0.9.8__py3-none-any.whl → 0.10.1__py3-none-any.whl
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.
- supervaizer/__init__.py +11 -2
- supervaizer/__version__.py +1 -1
- supervaizer/account.py +4 -0
- supervaizer/account_service.py +7 -1
- supervaizer/admin/routes.py +24 -8
- supervaizer/admin/templates/agents.html +74 -0
- supervaizer/admin/templates/agents_grid.html +5 -3
- supervaizer/admin/templates/navigation.html +11 -1
- supervaizer/admin/templates/supervaize_instructions.html +212 -0
- supervaizer/agent.py +28 -6
- supervaizer/case.py +46 -14
- supervaizer/cli.py +247 -7
- supervaizer/common.py +45 -4
- supervaizer/deploy/__init__.py +16 -0
- supervaizer/deploy/cli.py +296 -0
- supervaizer/deploy/commands/__init__.py +9 -0
- supervaizer/deploy/commands/clean.py +294 -0
- supervaizer/deploy/commands/down.py +119 -0
- supervaizer/deploy/commands/local.py +460 -0
- supervaizer/deploy/commands/plan.py +167 -0
- supervaizer/deploy/commands/status.py +169 -0
- supervaizer/deploy/commands/up.py +281 -0
- supervaizer/deploy/docker.py +378 -0
- supervaizer/deploy/driver_factory.py +42 -0
- supervaizer/deploy/drivers/__init__.py +39 -0
- supervaizer/deploy/drivers/aws_app_runner.py +607 -0
- supervaizer/deploy/drivers/base.py +196 -0
- supervaizer/deploy/drivers/cloud_run.py +570 -0
- supervaizer/deploy/drivers/do_app_platform.py +504 -0
- supervaizer/deploy/health.py +404 -0
- supervaizer/deploy/state.py +210 -0
- supervaizer/deploy/templates/Dockerfile.template +44 -0
- supervaizer/deploy/templates/debug_env.py +69 -0
- supervaizer/deploy/templates/docker-compose.yml.template +37 -0
- supervaizer/deploy/templates/dockerignore.template +66 -0
- supervaizer/deploy/templates/entrypoint.sh +20 -0
- supervaizer/deploy/utils.py +52 -0
- supervaizer/examples/controller_template.py +1 -1
- supervaizer/job.py +18 -5
- supervaizer/job_service.py +6 -5
- supervaizer/parameter.py +13 -1
- supervaizer/protocol/__init__.py +2 -2
- supervaizer/protocol/a2a/routes.py +1 -1
- supervaizer/routes.py +141 -17
- supervaizer/server.py +5 -11
- supervaizer/utils/__init__.py +16 -0
- supervaizer/utils/version_check.py +56 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/METADATA +105 -34
- supervaizer-0.10.1.dist-info/RECORD +76 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/WHEEL +1 -1
- supervaizer/protocol/acp/__init__.py +0 -21
- supervaizer/protocol/acp/model.py +0 -198
- supervaizer/protocol/acp/routes.py +0 -74
- supervaizer-0.9.8.dist-info/RECORD +0 -52
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/entry_points.txt +0 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
4
|
+
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
|
5
|
+
# https://mozilla.org/MPL/2.0/.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Deployment CLI Commands
|
|
9
|
+
|
|
10
|
+
This module contains the main CLI commands for the deploy subcommand.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from supervaizer.deploy.commands.plan import plan_deployment
|
|
18
|
+
from supervaizer.deploy.commands.up import deploy_up
|
|
19
|
+
from supervaizer.deploy.commands.down import deploy_down
|
|
20
|
+
from supervaizer.deploy.commands.status import deploy_status
|
|
21
|
+
from supervaizer.deploy.commands.local import local_docker
|
|
22
|
+
from supervaizer.deploy.commands.clean import (
|
|
23
|
+
clean_deployment,
|
|
24
|
+
clean_docker_artifacts,
|
|
25
|
+
clean_state_only,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
console = Console()
|
|
29
|
+
|
|
30
|
+
# Create the deploy subcommand
|
|
31
|
+
deploy_app = typer.Typer(
|
|
32
|
+
name="deploy",
|
|
33
|
+
help="Deploy Supervaizer agents to cloud platforms. Python dependencies must be managed in pyproject.toml file.",
|
|
34
|
+
no_args_is_help=True,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Common parameters
|
|
38
|
+
platform_option = typer.Option(
|
|
39
|
+
None,
|
|
40
|
+
"--platform",
|
|
41
|
+
"-p",
|
|
42
|
+
help="Target platform (cloud-run|aws-app-runner|do-app-platform)",
|
|
43
|
+
)
|
|
44
|
+
name_option = typer.Option(
|
|
45
|
+
...,
|
|
46
|
+
"--name",
|
|
47
|
+
"-n",
|
|
48
|
+
help="Service name. Required for local command.",
|
|
49
|
+
prompt="Service name (e.g. my-service)",
|
|
50
|
+
)
|
|
51
|
+
env_option = typer.Option("dev", "--env", "-e", help="Environment (dev|staging|prod)")
|
|
52
|
+
region_option = typer.Option(None, "--region", "-r", help="Provider region")
|
|
53
|
+
project_id_option = typer.Option(
|
|
54
|
+
None, "--project-id", help="GCP project / AWS account / DO project"
|
|
55
|
+
)
|
|
56
|
+
verbose_option = typer.Option(False, "--verbose", "-v", help="Show detailed output")
|
|
57
|
+
|
|
58
|
+
# Additional parameters for specific commands
|
|
59
|
+
image_option = typer.Option(None, "--image", help="Container image (registry/repo:tag)")
|
|
60
|
+
port_option = typer.Option(8000, "--port", help="Application port")
|
|
61
|
+
generate_api_key_option = typer.Option(
|
|
62
|
+
False, "--generate-api-key", help="Generate secure API key"
|
|
63
|
+
)
|
|
64
|
+
generate_rsa_option = typer.Option(
|
|
65
|
+
False, "--generate-rsa", help="Generate RSA private key"
|
|
66
|
+
)
|
|
67
|
+
yes_option = typer.Option(False, "--yes", "-y", help="Non-interactive mode")
|
|
68
|
+
no_rollback_option = typer.Option(False, "--no-rollback", help="Keep failed revision")
|
|
69
|
+
timeout_option = typer.Option(300, "--timeout", help="Deployment timeout in seconds")
|
|
70
|
+
docker_files_only_option = typer.Option(
|
|
71
|
+
False, "--docker-files-only", help="Only generate Docker files without running them"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
controller_file_option = typer.Option(
|
|
75
|
+
"supervaizer_control.py",
|
|
76
|
+
"--controller-file",
|
|
77
|
+
help="Controller file name (default: supervaizer_control.py)",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Clean command options
|
|
81
|
+
force_option = typer.Option(False, "--force", "-f", help="Skip confirmation prompts")
|
|
82
|
+
verbose_option_clean = typer.Option(
|
|
83
|
+
False, "--verbose", "-v", help="Show detailed output"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _check_pyproject_toml() -> Path:
|
|
88
|
+
"""Check if pyproject.toml exists in current directory or parent directories."""
|
|
89
|
+
current_dir = Path.cwd()
|
|
90
|
+
|
|
91
|
+
# Check current directory first
|
|
92
|
+
pyproject_path = current_dir / "pyproject.toml"
|
|
93
|
+
if pyproject_path.exists():
|
|
94
|
+
return current_dir
|
|
95
|
+
|
|
96
|
+
# Check parent directories up to 3 levels
|
|
97
|
+
for _ in range(3):
|
|
98
|
+
current_dir = current_dir.parent
|
|
99
|
+
pyproject_path = current_dir / "pyproject.toml"
|
|
100
|
+
if pyproject_path.exists():
|
|
101
|
+
return current_dir
|
|
102
|
+
|
|
103
|
+
# If not found, show help and exit
|
|
104
|
+
_show_pyproject_toml_help()
|
|
105
|
+
raise typer.Exit(1)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _show_pyproject_toml_help() -> None:
|
|
109
|
+
"""Show help message when pyproject.toml is not found."""
|
|
110
|
+
console.print("[bold red]Error:[/] pyproject.toml file not found")
|
|
111
|
+
console.print(
|
|
112
|
+
"The supervaizer deploy command must be run from a directory containing pyproject.toml"
|
|
113
|
+
)
|
|
114
|
+
console.print("or from a subdirectory of such a directory.")
|
|
115
|
+
console.print("\n[bold]Current directory:[/] " + str(Path.cwd()))
|
|
116
|
+
console.print("\n[bold]Please ensure:[/]")
|
|
117
|
+
console.print(" • You are in the correct project directory")
|
|
118
|
+
console.print(" • The pyproject.toml file exists in the project root")
|
|
119
|
+
console.print(" • Python dependencies are properly defined in pyproject.toml")
|
|
120
|
+
console.print("\n[bold]Available deploy commands:[/]")
|
|
121
|
+
console.print(
|
|
122
|
+
" • [bold]supervaizer deploy local[/] - Test deployment locally using Docker Compose"
|
|
123
|
+
)
|
|
124
|
+
console.print(" • [bold]supervaizer deploy up[/] - Deploy or update the service")
|
|
125
|
+
console.print(
|
|
126
|
+
" • [bold]supervaizer deploy down[/] - Destroy the service and cleanup resources"
|
|
127
|
+
)
|
|
128
|
+
console.print(
|
|
129
|
+
" • [bold]supervaizer deploy status[/] - Show deployment status and health information"
|
|
130
|
+
)
|
|
131
|
+
console.print(
|
|
132
|
+
" • [bold]supervaizer deploy plan[/] - Plan deployment changes without applying them"
|
|
133
|
+
)
|
|
134
|
+
console.print(
|
|
135
|
+
" • [bold]supervaizer deploy clean[/] - Clean up deployment artifacts and generated files"
|
|
136
|
+
)
|
|
137
|
+
console.print(
|
|
138
|
+
"\nUse [bold]supervaizer deploy <command> --help[/] for more information about each command."
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@deploy_app.callback()
|
|
143
|
+
def deploy_callback() -> None:
|
|
144
|
+
"""Deploy Supervaizer agents to cloud platforms."""
|
|
145
|
+
# This callback is called when no subcommand is provided
|
|
146
|
+
# The no_args_is_help=True will automatically show help
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _check_platform_required(platform: str, command_name: str) -> None:
|
|
151
|
+
"""Check if platform is provided and show helpful error if not."""
|
|
152
|
+
if platform is None:
|
|
153
|
+
console.print("[bold red]Error:[/] --platform is required")
|
|
154
|
+
console.print(
|
|
155
|
+
f"Use [bold]supervaizer deploy {command_name} --help[/] for more information"
|
|
156
|
+
)
|
|
157
|
+
raise typer.Exit(1)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@deploy_app.command(no_args_is_help=True)
|
|
161
|
+
def plan(
|
|
162
|
+
platform: str = platform_option,
|
|
163
|
+
name: str = name_option,
|
|
164
|
+
env: str = env_option,
|
|
165
|
+
region: str = region_option,
|
|
166
|
+
project_id: str = project_id_option,
|
|
167
|
+
verbose: bool = verbose_option,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Plan deployment changes without applying them."""
|
|
170
|
+
_check_platform_required(platform, "plan")
|
|
171
|
+
source_dir = _check_pyproject_toml()
|
|
172
|
+
plan_deployment(platform, name, env, region, project_id, verbose, source_dir)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@deploy_app.command(no_args_is_help=True)
|
|
176
|
+
def up(
|
|
177
|
+
platform: str = platform_option,
|
|
178
|
+
name: str = name_option,
|
|
179
|
+
env: str = env_option,
|
|
180
|
+
region: str = region_option,
|
|
181
|
+
project_id: str = project_id_option,
|
|
182
|
+
image: str = image_option,
|
|
183
|
+
port: int = port_option,
|
|
184
|
+
generate_api_key: bool = generate_api_key_option,
|
|
185
|
+
generate_rsa: bool = generate_rsa_option,
|
|
186
|
+
yes: bool = yes_option,
|
|
187
|
+
no_rollback: bool = no_rollback_option,
|
|
188
|
+
timeout: int = timeout_option,
|
|
189
|
+
verbose: bool = verbose_option,
|
|
190
|
+
) -> None:
|
|
191
|
+
"""Deploy or update the service."""
|
|
192
|
+
_check_platform_required(platform, "up")
|
|
193
|
+
source_dir = _check_pyproject_toml()
|
|
194
|
+
deploy_up(
|
|
195
|
+
platform,
|
|
196
|
+
name,
|
|
197
|
+
env,
|
|
198
|
+
region,
|
|
199
|
+
project_id,
|
|
200
|
+
image,
|
|
201
|
+
port,
|
|
202
|
+
generate_api_key,
|
|
203
|
+
generate_rsa,
|
|
204
|
+
yes,
|
|
205
|
+
no_rollback,
|
|
206
|
+
timeout,
|
|
207
|
+
verbose,
|
|
208
|
+
source_dir,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@deploy_app.command(no_args_is_help=True)
|
|
213
|
+
def down(
|
|
214
|
+
platform: str = platform_option,
|
|
215
|
+
name: str = name_option,
|
|
216
|
+
env: str = env_option,
|
|
217
|
+
region: str = region_option,
|
|
218
|
+
project_id: str = project_id_option,
|
|
219
|
+
yes: bool = yes_option,
|
|
220
|
+
verbose: bool = verbose_option,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""Destroy the service and cleanup resources."""
|
|
223
|
+
_check_platform_required(platform, "down")
|
|
224
|
+
source_dir = _check_pyproject_toml()
|
|
225
|
+
deploy_down(platform, name, env, region, project_id, yes, verbose, source_dir)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@deploy_app.command(no_args_is_help=True)
|
|
229
|
+
def status(
|
|
230
|
+
platform: str = platform_option,
|
|
231
|
+
name: str = name_option,
|
|
232
|
+
env: str = env_option,
|
|
233
|
+
region: str = region_option,
|
|
234
|
+
project_id: str = project_id_option,
|
|
235
|
+
verbose: bool = verbose_option,
|
|
236
|
+
) -> None:
|
|
237
|
+
"""Show deployment status and health information."""
|
|
238
|
+
_check_platform_required(platform, "status")
|
|
239
|
+
source_dir = _check_pyproject_toml()
|
|
240
|
+
deploy_status(platform, name, env, region, project_id, verbose, source_dir)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@deploy_app.command()
|
|
244
|
+
def local(
|
|
245
|
+
name: str = name_option,
|
|
246
|
+
env: str = env_option,
|
|
247
|
+
port: int = port_option,
|
|
248
|
+
generate_api_key: bool = generate_api_key_option,
|
|
249
|
+
generate_rsa: bool = generate_rsa_option,
|
|
250
|
+
timeout: int = timeout_option,
|
|
251
|
+
verbose: bool = verbose_option,
|
|
252
|
+
docker_files_only: bool = docker_files_only_option,
|
|
253
|
+
controller_file: str = controller_file_option,
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Test deployment locally using Docker Compose. Requires --name."""
|
|
256
|
+
source_dir = _check_pyproject_toml()
|
|
257
|
+
local_docker(
|
|
258
|
+
name,
|
|
259
|
+
env,
|
|
260
|
+
port,
|
|
261
|
+
generate_api_key,
|
|
262
|
+
generate_rsa,
|
|
263
|
+
timeout,
|
|
264
|
+
verbose,
|
|
265
|
+
docker_files_only,
|
|
266
|
+
str(source_dir),
|
|
267
|
+
controller_file,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@deploy_app.command()
|
|
272
|
+
def clean(
|
|
273
|
+
force: bool = force_option,
|
|
274
|
+
verbose: bool = verbose_option_clean,
|
|
275
|
+
docker_only: bool = typer.Option(
|
|
276
|
+
False, "--docker-only", help="Clean only Docker artifacts"
|
|
277
|
+
),
|
|
278
|
+
state_only: bool = typer.Option(
|
|
279
|
+
False, "--state-only", help="Clean only deployment state"
|
|
280
|
+
),
|
|
281
|
+
) -> None:
|
|
282
|
+
"""Clean up deployment artifacts and generated files."""
|
|
283
|
+
_check_pyproject_toml()
|
|
284
|
+
|
|
285
|
+
if docker_only and state_only:
|
|
286
|
+
console.print(
|
|
287
|
+
"[bold red]Error:[/] Cannot use both --docker-only and --state-only"
|
|
288
|
+
)
|
|
289
|
+
raise typer.Exit(1)
|
|
290
|
+
|
|
291
|
+
if docker_only:
|
|
292
|
+
clean_docker_artifacts(force=force, verbose=verbose)
|
|
293
|
+
elif state_only:
|
|
294
|
+
clean_state_only(force=force, verbose=verbose)
|
|
295
|
+
else:
|
|
296
|
+
clean_deployment(force=force, verbose=verbose)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
4
|
+
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
|
5
|
+
# https://mozilla.org/MPL/2.0/.
|
|
6
|
+
|
|
7
|
+
from . import plan, up, down, status
|
|
8
|
+
|
|
9
|
+
__all__ = ["plan", "up", "down", "status"]
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
4
|
+
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
|
5
|
+
# https://mozilla.org/MPL/2.0/.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Clean Command
|
|
9
|
+
|
|
10
|
+
This module provides functionality to clean up deployment artifacts and generated files.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import shutil
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.prompt import Confirm
|
|
19
|
+
|
|
20
|
+
from supervaizer.common import log
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def clean_deployment(
|
|
26
|
+
deployment_dir: Optional[Path] = None,
|
|
27
|
+
force: bool = False,
|
|
28
|
+
verbose: bool = False,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Clean up deployment artifacts and generated files.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
deployment_dir: Path to deployment directory (default: .deployment)
|
|
35
|
+
force: Skip confirmation prompt
|
|
36
|
+
verbose: Show detailed output
|
|
37
|
+
"""
|
|
38
|
+
if deployment_dir is None:
|
|
39
|
+
deployment_dir = Path(".deployment")
|
|
40
|
+
|
|
41
|
+
# Check if deployment directory exists
|
|
42
|
+
if not deployment_dir.exists():
|
|
43
|
+
console.print(f"[yellow]No deployment directory found at {deployment_dir}[/]")
|
|
44
|
+
console.print("Nothing to clean up.")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
# Show what will be deleted
|
|
48
|
+
console.print(f"[bold blue]Cleaning deployment directory: {deployment_dir}[/]")
|
|
49
|
+
|
|
50
|
+
if verbose:
|
|
51
|
+
console.print("\n[bold]Contents to be deleted:[/]")
|
|
52
|
+
try:
|
|
53
|
+
for item in deployment_dir.rglob("*"):
|
|
54
|
+
if item.is_file():
|
|
55
|
+
console.print(f" 📄 {item.relative_to(deployment_dir)}")
|
|
56
|
+
elif item.is_dir():
|
|
57
|
+
console.print(f" 📁 {item.relative_to(deployment_dir)}/")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
log.debug(f"Error listing directory contents: {e}")
|
|
60
|
+
|
|
61
|
+
# Calculate total size
|
|
62
|
+
total_size = 0
|
|
63
|
+
file_count = 0
|
|
64
|
+
try:
|
|
65
|
+
for item in deployment_dir.rglob("*"):
|
|
66
|
+
if item.is_file():
|
|
67
|
+
total_size += item.stat().st_size
|
|
68
|
+
file_count += 1
|
|
69
|
+
except Exception as e:
|
|
70
|
+
log.debug(f"Error calculating directory size: {e}")
|
|
71
|
+
|
|
72
|
+
if file_count > 0:
|
|
73
|
+
size_mb = total_size / (1024 * 1024)
|
|
74
|
+
console.print(f"\n[bold]Summary:[/] {file_count} files, {size_mb:.2f} MB")
|
|
75
|
+
|
|
76
|
+
# Confirmation prompt (unless forced)
|
|
77
|
+
if not force:
|
|
78
|
+
console.print(
|
|
79
|
+
"\n[yellow]This will permanently delete all deployment artifacts.[/]"
|
|
80
|
+
)
|
|
81
|
+
console.print("This includes:")
|
|
82
|
+
console.print(" • Generated Docker files")
|
|
83
|
+
console.print(" • Deployment state")
|
|
84
|
+
console.print(" • Configuration files")
|
|
85
|
+
console.print(" • Logs and debug information")
|
|
86
|
+
|
|
87
|
+
if not Confirm.ask("\n[bold red]Are you sure you want to continue?[/]"):
|
|
88
|
+
console.print("[yellow]Cleanup cancelled.[/]")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Perform cleanup
|
|
92
|
+
try:
|
|
93
|
+
console.print(f"\n[bold]Deleting {deployment_dir}...[/]")
|
|
94
|
+
shutil.rmtree(deployment_dir)
|
|
95
|
+
console.print("[green]✓[/] Deployment directory cleaned successfully")
|
|
96
|
+
|
|
97
|
+
# Show what was cleaned
|
|
98
|
+
console.print("\n[bold green]Cleanup completed![/]")
|
|
99
|
+
console.print(f"Removed: {deployment_dir}")
|
|
100
|
+
if file_count > 0:
|
|
101
|
+
console.print(f"Files deleted: {file_count}")
|
|
102
|
+
console.print(f"Space freed: {size_mb:.2f} MB")
|
|
103
|
+
|
|
104
|
+
# Show next steps
|
|
105
|
+
console.print("\n[bold]Next steps:[/]")
|
|
106
|
+
console.print(
|
|
107
|
+
" • Run [bold]supervaizer deploy local[/] to regenerate files for local testing"
|
|
108
|
+
)
|
|
109
|
+
console.print(
|
|
110
|
+
" • Run [bold]supervaizer deploy up[/] to deploy to cloud platforms"
|
|
111
|
+
)
|
|
112
|
+
console.print(
|
|
113
|
+
" • Run [bold]supervaizer deploy plan[/] to plan new deployments"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
except PermissionError as e:
|
|
117
|
+
console.print(
|
|
118
|
+
f"[bold red]Error:[/] Permission denied while deleting {deployment_dir}"
|
|
119
|
+
)
|
|
120
|
+
console.print(
|
|
121
|
+
"Make sure no processes are using files in the deployment directory."
|
|
122
|
+
)
|
|
123
|
+
console.print(f"Details: {e}")
|
|
124
|
+
raise RuntimeError(f"Failed to clean deployment directory: {e}") from e
|
|
125
|
+
except Exception as e:
|
|
126
|
+
console.print("[bold red]Error:[/] Failed to clean deployment directory")
|
|
127
|
+
console.print(f"Details: {e}")
|
|
128
|
+
raise RuntimeError(f"Failed to clean deployment directory: {e}") from e
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def clean_docker_artifacts(
|
|
132
|
+
force: bool = False,
|
|
133
|
+
verbose: bool = False,
|
|
134
|
+
) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Clean up Docker-related artifacts only.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
force: Skip confirmation prompt
|
|
140
|
+
verbose: Show detailed output
|
|
141
|
+
"""
|
|
142
|
+
deployment_dir = Path(".deployment")
|
|
143
|
+
|
|
144
|
+
if not deployment_dir.exists():
|
|
145
|
+
console.print("[yellow]No deployment directory found[/]")
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
# List of Docker-related files to clean
|
|
149
|
+
docker_files = [
|
|
150
|
+
"Dockerfile",
|
|
151
|
+
".dockerignore",
|
|
152
|
+
"docker-compose.yml",
|
|
153
|
+
"docker-compose.yaml",
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
docker_dirs = [
|
|
157
|
+
"logs",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
files_to_delete = []
|
|
161
|
+
dirs_to_delete = []
|
|
162
|
+
|
|
163
|
+
# Find files to delete
|
|
164
|
+
for file_name in docker_files:
|
|
165
|
+
file_path = deployment_dir / file_name
|
|
166
|
+
if file_path.exists():
|
|
167
|
+
files_to_delete.append(file_path)
|
|
168
|
+
|
|
169
|
+
# Find directories to delete
|
|
170
|
+
for dir_name in docker_dirs:
|
|
171
|
+
dir_path = deployment_dir / dir_name
|
|
172
|
+
if dir_path.exists() and dir_path.is_dir():
|
|
173
|
+
dirs_to_delete.append(dir_path)
|
|
174
|
+
|
|
175
|
+
if not files_to_delete and not dirs_to_delete:
|
|
176
|
+
console.print("[yellow]No Docker artifacts found to clean[/]")
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
# Show what will be deleted
|
|
180
|
+
console.print("[bold blue]Cleaning Docker artifacts[/]")
|
|
181
|
+
|
|
182
|
+
if verbose:
|
|
183
|
+
console.print("\n[bold]Files to be deleted:[/]")
|
|
184
|
+
for file_path in files_to_delete:
|
|
185
|
+
console.print(f" 📄 {file_path.relative_to(deployment_dir)}")
|
|
186
|
+
|
|
187
|
+
console.print("\n[bold]Directories to be deleted:[/]")
|
|
188
|
+
for dir_path in dirs_to_delete:
|
|
189
|
+
console.print(f" 📁 {dir_path.relative_to(deployment_dir)}/")
|
|
190
|
+
|
|
191
|
+
# Confirmation prompt (unless forced)
|
|
192
|
+
if not force:
|
|
193
|
+
console.print("\n[yellow]This will delete Docker-related files only.[/]")
|
|
194
|
+
console.print("Deployment state and configuration will be preserved.")
|
|
195
|
+
|
|
196
|
+
if not Confirm.ask("\n[bold red]Continue with Docker cleanup?[/]"):
|
|
197
|
+
console.print("[yellow]Cleanup cancelled.[/]")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
# Perform cleanup
|
|
201
|
+
deleted_count = 0
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
# Delete files
|
|
205
|
+
for file_path in files_to_delete:
|
|
206
|
+
if verbose:
|
|
207
|
+
console.print(f"Deleting {file_path.relative_to(deployment_dir)}...")
|
|
208
|
+
file_path.unlink()
|
|
209
|
+
deleted_count += 1
|
|
210
|
+
|
|
211
|
+
# Delete directories
|
|
212
|
+
for dir_path in dirs_to_delete:
|
|
213
|
+
if verbose:
|
|
214
|
+
console.print(f"Deleting {dir_path.relative_to(deployment_dir)}/...")
|
|
215
|
+
shutil.rmtree(dir_path)
|
|
216
|
+
deleted_count += 1
|
|
217
|
+
|
|
218
|
+
console.print("[green]✓[/] Docker artifacts cleaned successfully")
|
|
219
|
+
console.print(f"Deleted {deleted_count} items")
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
console.print("[bold red]Error:[/] Failed to clean Docker artifacts")
|
|
223
|
+
console.print(f"Details: {e}")
|
|
224
|
+
raise RuntimeError(f"Failed to clean Docker artifacts: {e}") from e
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def clean_state_only(
|
|
228
|
+
force: bool = False,
|
|
229
|
+
verbose: bool = False,
|
|
230
|
+
) -> None:
|
|
231
|
+
"""
|
|
232
|
+
Clean up deployment state only, preserving generated files.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
force: Skip confirmation prompt
|
|
236
|
+
verbose: Show detailed output
|
|
237
|
+
"""
|
|
238
|
+
deployment_dir = Path(".deployment")
|
|
239
|
+
|
|
240
|
+
if not deployment_dir.exists():
|
|
241
|
+
console.print("[yellow]No deployment directory found[/]")
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
# State-related files to clean
|
|
245
|
+
state_files = [
|
|
246
|
+
"state.json",
|
|
247
|
+
"config.yaml",
|
|
248
|
+
"config.yml",
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
files_to_delete = []
|
|
252
|
+
|
|
253
|
+
# Find state files to delete
|
|
254
|
+
for file_name in state_files:
|
|
255
|
+
file_path = deployment_dir / file_name
|
|
256
|
+
if file_path.exists():
|
|
257
|
+
files_to_delete.append(file_path)
|
|
258
|
+
|
|
259
|
+
if not files_to_delete:
|
|
260
|
+
console.print("[yellow]No state files found to clean[/]")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
# Show what will be deleted
|
|
264
|
+
console.print("[bold blue]Cleaning deployment state[/]")
|
|
265
|
+
|
|
266
|
+
if verbose:
|
|
267
|
+
console.print("\n[bold]State files to be deleted:[/]")
|
|
268
|
+
for file_path in files_to_delete:
|
|
269
|
+
console.print(f" 📄 {file_path.relative_to(deployment_dir)}")
|
|
270
|
+
|
|
271
|
+
# Confirmation prompt (unless forced)
|
|
272
|
+
if not force:
|
|
273
|
+
console.print("\n[yellow]This will delete deployment state only.[/]")
|
|
274
|
+
console.print("Generated Docker files will be preserved.")
|
|
275
|
+
console.print("You will need to redeploy to recreate the state.")
|
|
276
|
+
|
|
277
|
+
if not Confirm.ask("\n[bold red]Continue with state cleanup?[/]"):
|
|
278
|
+
console.print("[yellow]Cleanup cancelled.[/]")
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
# Perform cleanup
|
|
282
|
+
try:
|
|
283
|
+
for file_path in files_to_delete:
|
|
284
|
+
if verbose:
|
|
285
|
+
console.print(f"Deleting {file_path.relative_to(deployment_dir)}...")
|
|
286
|
+
file_path.unlink()
|
|
287
|
+
|
|
288
|
+
console.print("[green]✓[/] Deployment state cleaned successfully")
|
|
289
|
+
console.print(f"Deleted {len(files_to_delete)} state files")
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
console.print("[bold red]Error:[/] Failed to clean deployment state")
|
|
293
|
+
console.print(f"Details: {e}")
|
|
294
|
+
raise RuntimeError(f"Failed to clean deployment state: {e}") from e
|