supervaizer 0.10.5__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 +97 -0
- supervaizer/__version__.py +10 -0
- supervaizer/account.py +308 -0
- supervaizer/account_service.py +93 -0
- supervaizer/admin/routes.py +1293 -0
- supervaizer/admin/static/js/job-start-form.js +373 -0
- supervaizer/admin/templates/agent_detail.html +145 -0
- supervaizer/admin/templates/agents.html +249 -0
- supervaizer/admin/templates/agents_grid.html +82 -0
- supervaizer/admin/templates/base.html +233 -0
- supervaizer/admin/templates/case_detail.html +230 -0
- supervaizer/admin/templates/cases_list.html +182 -0
- supervaizer/admin/templates/cases_table.html +134 -0
- supervaizer/admin/templates/console.html +389 -0
- supervaizer/admin/templates/dashboard.html +153 -0
- supervaizer/admin/templates/job_detail.html +192 -0
- supervaizer/admin/templates/job_start_test.html +109 -0
- supervaizer/admin/templates/jobs_list.html +180 -0
- supervaizer/admin/templates/jobs_table.html +122 -0
- supervaizer/admin/templates/navigation.html +163 -0
- supervaizer/admin/templates/recent_activity.html +81 -0
- supervaizer/admin/templates/server.html +105 -0
- supervaizer/admin/templates/server_status_cards.html +121 -0
- supervaizer/admin/templates/supervaize_instructions.html +212 -0
- supervaizer/agent.py +956 -0
- supervaizer/case.py +432 -0
- supervaizer/cli.py +395 -0
- supervaizer/common.py +324 -0
- supervaizer/deploy/__init__.py +16 -0
- supervaizer/deploy/cli.py +305 -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 +377 -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/event.py +181 -0
- supervaizer/examples/controller_template.py +196 -0
- supervaizer/instructions.py +145 -0
- supervaizer/job.py +392 -0
- supervaizer/job_service.py +156 -0
- supervaizer/lifecycle.py +417 -0
- supervaizer/parameter.py +233 -0
- supervaizer/protocol/__init__.py +11 -0
- supervaizer/protocol/a2a/__init__.py +21 -0
- supervaizer/protocol/a2a/model.py +227 -0
- supervaizer/protocol/a2a/routes.py +99 -0
- supervaizer/py.typed +1 -0
- supervaizer/routes.py +917 -0
- supervaizer/server.py +553 -0
- supervaizer/server_utils.py +54 -0
- supervaizer/storage.py +462 -0
- supervaizer/telemetry.py +81 -0
- supervaizer/utils/__init__.py +16 -0
- supervaizer/utils/version_check.py +56 -0
- supervaizer-0.10.5.dist-info/METADATA +317 -0
- supervaizer-0.10.5.dist-info/RECORD +76 -0
- supervaizer-0.10.5.dist-info/WHEEL +4 -0
- supervaizer-0.10.5.dist-info/entry_points.txt +2 -0
- supervaizer-0.10.5.dist-info/licenses/LICENSE.md +346 -0
|
@@ -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
|
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
Down Command
|
|
9
|
+
|
|
10
|
+
Destroy the service and cleanup resources.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.prompt import Confirm
|
|
18
|
+
|
|
19
|
+
from supervaizer.common import log
|
|
20
|
+
from supervaizer.deploy.driver_factory import create_driver, get_supported_platforms
|
|
21
|
+
from supervaizer.deploy.state import StateManager
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def deploy_down(
|
|
27
|
+
platform: str,
|
|
28
|
+
name: Optional[str] = None,
|
|
29
|
+
env: str = "dev",
|
|
30
|
+
region: Optional[str] = None,
|
|
31
|
+
project_id: Optional[str] = None,
|
|
32
|
+
yes: bool = False,
|
|
33
|
+
verbose: bool = False,
|
|
34
|
+
source_dir: Optional[Path] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Destroy the service and cleanup resources."""
|
|
37
|
+
# Validate platform
|
|
38
|
+
if platform not in get_supported_platforms():
|
|
39
|
+
console.print(f"[bold red]Error:[/] Unsupported platform: {platform}")
|
|
40
|
+
console.print(f"Supported platforms: {', '.join(get_supported_platforms())}")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# Set defaults
|
|
44
|
+
if not name:
|
|
45
|
+
name = (source_dir or Path.cwd()).name
|
|
46
|
+
if not region:
|
|
47
|
+
region = _get_default_region(platform)
|
|
48
|
+
|
|
49
|
+
console.print(f"[bold red]Destroying service on {platform}[/bold red]")
|
|
50
|
+
console.print(f"Service name: {name}")
|
|
51
|
+
console.print(f"Environment: {env}")
|
|
52
|
+
console.print(f"Region: {region}")
|
|
53
|
+
if project_id:
|
|
54
|
+
console.print(f"Project ID: {project_id}")
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Check local state
|
|
58
|
+
deployment_dir = (source_dir or Path.cwd()) / ".deployment"
|
|
59
|
+
state_manager = StateManager(deployment_dir)
|
|
60
|
+
state = state_manager.load_state()
|
|
61
|
+
|
|
62
|
+
if state:
|
|
63
|
+
console.print("\n[bold]Current Deployment:[/bold]")
|
|
64
|
+
console.print(f" Service URL: {state.service_url}")
|
|
65
|
+
console.print(f" Image Tag: {state.image_tag}")
|
|
66
|
+
console.print(f" Status: {state.status}")
|
|
67
|
+
|
|
68
|
+
# Confirmation prompt
|
|
69
|
+
if not yes:
|
|
70
|
+
if not Confirm.ask(f"\nAre you sure you want to destroy {name}-{env}?"):
|
|
71
|
+
console.print("[yellow]Destruction cancelled[/yellow]")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
# Create driver
|
|
75
|
+
driver = create_driver(platform, region, project_id)
|
|
76
|
+
|
|
77
|
+
# Check prerequisites
|
|
78
|
+
prerequisites = driver.check_prerequisites()
|
|
79
|
+
if prerequisites:
|
|
80
|
+
console.print("[bold red]Prerequisites not met:[/]")
|
|
81
|
+
for prereq in prerequisites:
|
|
82
|
+
console.print(f" • {prereq}")
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
# Destroy service
|
|
86
|
+
console.print("\n[bold]Destroying service...[/bold]")
|
|
87
|
+
result = driver.destroy_service(name, env, keep_secrets=False)
|
|
88
|
+
|
|
89
|
+
if result.success:
|
|
90
|
+
console.print("[bold green]Service destroyed successfully[/bold green]")
|
|
91
|
+
|
|
92
|
+
# Clean up local state
|
|
93
|
+
if state_manager.state_file.exists():
|
|
94
|
+
state_manager.delete_state()
|
|
95
|
+
console.print("Cleaned up local deployment state")
|
|
96
|
+
|
|
97
|
+
# Clean up deployment directory
|
|
98
|
+
if deployment_dir.exists():
|
|
99
|
+
import shutil
|
|
100
|
+
|
|
101
|
+
shutil.rmtree(deployment_dir)
|
|
102
|
+
console.print("Cleaned up deployment directory")
|
|
103
|
+
|
|
104
|
+
else:
|
|
105
|
+
console.print(f"[bold red]Destruction failed:[/] {result.error_message}")
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
log.error(f"Destruction failed: {e}")
|
|
109
|
+
console.print(f"[bold red]Destruction failed:[/] {e}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _get_default_region(platform: str) -> str:
|
|
113
|
+
"""Get default region for platform."""
|
|
114
|
+
defaults = {
|
|
115
|
+
"cloud-run": "us-central1",
|
|
116
|
+
"aws-app-runner": "us-east-1",
|
|
117
|
+
"do-app-platform": "nyc3",
|
|
118
|
+
}
|
|
119
|
+
return defaults.get(platform, "us-central1")
|