wasm-cli 0.15.0__tar.gz → 0.15.2__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.
- {wasm_cli-0.15.0/src/wasm_cli.egg-info → wasm_cli-0.15.2}/PKG-INFO +1 -1
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/pyproject.toml +1 -1
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/setup.py +25 -2
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/__init__.py +1 -1
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/webapp.py +12 -5
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/logger.py +20 -2
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/base.py +5 -3
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/docker_compose.py +31 -1
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/python.py +3 -2
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/base_manager.py +4 -8
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/jobs.py +26 -19
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/server.py +12 -11
- {wasm_cli-0.15.0 → wasm_cli-0.15.2/src/wasm_cli.egg-info}/PKG-INFO +1 -1
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/LICENSE +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/MANIFEST.in +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/README.md +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/docs/MONITOR.md +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/docs/OBS_SETUP.md +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/docs/assets/logo.png +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/docs/assets/logo_bg.png +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/man/wasm.1 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/setup.cfg +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/__main__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/backup.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/cert.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/config.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/db.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/env.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/health.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/monitor.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/service.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/setup.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/site.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/store.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/version.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/commands/web.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/interactive.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/cli/parser.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/completions/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/completions/_wasm +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/completions/wasm.bash +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/completions/wasm.fish +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/config.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/dependencies.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/exceptions.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/store.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/update_checker.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/core/utils.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/env_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/nginx_config.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/package_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/path_resolver.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/prisma.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/turbo.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/helpers/workspace.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/monorepo.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/nextjs.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/nodejs.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/registry.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/static.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/deployers/vite.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/main.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/apache_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/backup_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/backup_scheduler.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/cert_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/base.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/mongodb.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/mysql.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/postgres.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/redis.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/database/registry.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/nginx_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/service_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/managers/source_manager.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/monitor/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/monitor/ai_analyzer.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/monitor/email_notifier.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/monitor/process_monitor.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/monitor/threat_store.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/apache/proxy.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/apache/static.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/nginx/advanced.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/nginx/monorepo.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/nginx/proxy.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/nginx/static.conf.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/systemd/app.service.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/systemd/backup-service.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/systemd/backup-timer.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/templates/systemd/docker-compose.service.j2 +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/validators/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/validators/domain.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/validators/port.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/validators/source.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/validators/ssh.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/apps.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/auth.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/backups.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/certs.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/config.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/databases.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/jobs.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/monitor.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/router.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/services.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/sites.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/api/system.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/auth.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/css/main.css +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/index.html +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/components/cards.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/components/jobs.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/components/metrics.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/components/skeleton.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/api.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/dialogs.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/notifications.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/router.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/search.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/shortcuts.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/theme.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/ui.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/core/websocket.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/main.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/apps.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/backups.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/certs.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/config.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/dashboard.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/databases.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/jobs.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/logs.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/monitor.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/services.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/js/pages/sites.js +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/login.html +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/logo.png +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/static/logo.webp +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/websockets/__init__.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm/web/websockets/router.py +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm_cli.egg-info/SOURCES.txt +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm_cli.egg-info/dependency_links.txt +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm_cli.egg-info/entry_points.txt +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm_cli.egg-info/requires.txt +0 -0
- {wasm_cli-0.15.0 → wasm_cli-0.15.2}/src/wasm_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wasm-cli"
|
|
7
|
-
version = "0.15.
|
|
7
|
+
version = "0.15.2"
|
|
8
8
|
description = "Web App System Management - Deploy and manage web applications on Linux servers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "WASM-NCSAL" }
|
|
@@ -9,7 +9,7 @@ from setuptools import setup, find_packages
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="wasm-cli",
|
|
12
|
-
version="0.15.
|
|
12
|
+
version="0.15.2",
|
|
13
13
|
description="Web App System Management - Deploy and manage web applications on Linux servers",
|
|
14
14
|
author="Yago López Prado",
|
|
15
15
|
author_email="yago.lopez.adeje@gmail.com",
|
|
@@ -24,10 +24,33 @@ setup(
|
|
|
24
24
|
],
|
|
25
25
|
extras_require={
|
|
26
26
|
"interactive": ["inquirer>=3.1.0"],
|
|
27
|
+
"web": [
|
|
28
|
+
"fastapi>=0.109.0",
|
|
29
|
+
"uvicorn[standard]>=0.27.0",
|
|
30
|
+
"python-jose[cryptography]>=3.3.0",
|
|
31
|
+
"passlib[bcrypt]>=1.7.4",
|
|
32
|
+
"psutil>=5.9.0",
|
|
33
|
+
"aiofiles>=23.0.0",
|
|
34
|
+
],
|
|
35
|
+
"monitor": [
|
|
36
|
+
"psutil>=5.9.0",
|
|
37
|
+
"httpx>=0.25.0",
|
|
38
|
+
],
|
|
39
|
+
"all": [
|
|
40
|
+
"inquirer>=3.1.0",
|
|
41
|
+
"rich>=13.0",
|
|
42
|
+
"psutil>=5.9.0",
|
|
43
|
+
"httpx>=0.25.0",
|
|
44
|
+
"fastapi>=0.109.0",
|
|
45
|
+
"uvicorn[standard]>=0.27.0",
|
|
46
|
+
"python-jose[cryptography]>=3.3.0",
|
|
47
|
+
"passlib[bcrypt]>=1.7.4",
|
|
48
|
+
"aiofiles>=23.0.0",
|
|
49
|
+
],
|
|
27
50
|
},
|
|
28
51
|
entry_points={
|
|
29
52
|
"console_scripts": [
|
|
30
|
-
"wasm=wasm.main:
|
|
53
|
+
"wasm=wasm.main:cli",
|
|
31
54
|
],
|
|
32
55
|
},
|
|
33
56
|
data_files=[
|
|
@@ -616,12 +616,19 @@ def _handle_update(args: Namespace) -> int:
|
|
|
616
616
|
|
|
617
617
|
# Step 3: Detect app type and configure deployer
|
|
618
618
|
logger.step(3, total_steps, "Detecting application type")
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
619
|
+
|
|
620
|
+
# Prefer stored app type from database (initial deploy already determined it correctly)
|
|
621
|
+
stored_type = app.app_type if app else None
|
|
622
|
+
if stored_type and stored_type != "unknown":
|
|
623
|
+
app_type = stored_type
|
|
624
624
|
logger.substep(f"Detected: {app_type}")
|
|
625
|
+
else:
|
|
626
|
+
app_type = detect_app_type(app_path, verbose=args.verbose)
|
|
627
|
+
if not app_type:
|
|
628
|
+
app_type = "nodejs"
|
|
629
|
+
logger.substep(f"Using default: {app_type}")
|
|
630
|
+
else:
|
|
631
|
+
logger.substep(f"Detected: {app_type}")
|
|
625
632
|
|
|
626
633
|
# Handle monorepo updates with dedicated flow
|
|
627
634
|
if app_type == "monorepo":
|
|
@@ -187,16 +187,34 @@ class Logger:
|
|
|
187
187
|
def debug(self, message: str) -> None:
|
|
188
188
|
"""
|
|
189
189
|
Log a debug message (only shown in verbose mode).
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
Args:
|
|
192
192
|
message: Debug message.
|
|
193
193
|
"""
|
|
194
194
|
if not self.verbose:
|
|
195
195
|
return
|
|
196
|
-
|
|
196
|
+
|
|
197
197
|
prefix = self._colorize("[DEBUG]", Colors.GRAY)
|
|
198
198
|
msg = self._colorize(message, Colors.GRAY)
|
|
199
199
|
self._write(f" {prefix} {msg}")
|
|
200
|
+
|
|
201
|
+
def command_output(self, stdout: str, stderr: str) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Log command stdout/stderr (only shown in verbose mode).
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
stdout: Command standard output.
|
|
207
|
+
stderr: Command standard error output.
|
|
208
|
+
"""
|
|
209
|
+
if not self.verbose:
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
for stream in (stdout, stderr):
|
|
213
|
+
if not stream or not stream.strip():
|
|
214
|
+
continue
|
|
215
|
+
for line in stream.rstrip("\n").split("\n"):
|
|
216
|
+
text = self._colorize(f" {line}", Colors.GRAY)
|
|
217
|
+
self._write(text)
|
|
200
218
|
|
|
201
219
|
def info(self, message: str) -> None:
|
|
202
220
|
"""
|
|
@@ -160,18 +160,20 @@ class BaseDeployer(ABC):
|
|
|
160
160
|
):
|
|
161
161
|
"""Run a command and return result."""
|
|
162
162
|
self.logger.debug(f"Running: {' '.join(command)}")
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
# Merge environment variables
|
|
165
165
|
run_env = self.env_vars.copy()
|
|
166
166
|
if env:
|
|
167
167
|
run_env.update(env)
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
|
|
169
|
+
result = run_command(
|
|
170
170
|
command,
|
|
171
171
|
cwd=cwd or self.app_path,
|
|
172
172
|
env=run_env if run_env else None,
|
|
173
173
|
timeout=timeout,
|
|
174
174
|
)
|
|
175
|
+
self.logger.command_output(result.stdout, result.stderr)
|
|
176
|
+
return result
|
|
175
177
|
|
|
176
178
|
def _detect_package_manager(self) -> str:
|
|
177
179
|
"""
|
|
@@ -105,6 +105,27 @@ class DockerComposeDeployer:
|
|
|
105
105
|
self.services: List[DockerComposeService] = []
|
|
106
106
|
self.compose_path = None
|
|
107
107
|
|
|
108
|
+
# Framework config files that indicate docker-compose.yml is likely
|
|
109
|
+
# just for local development (databases, caches, etc.)
|
|
110
|
+
FRAMEWORK_CONFIG_FILES = [
|
|
111
|
+
# Next.js
|
|
112
|
+
"next.config.js", "next.config.mjs", "next.config.ts",
|
|
113
|
+
# Vite
|
|
114
|
+
"vite.config.js", "vite.config.ts", "vite.config.mjs",
|
|
115
|
+
# Angular
|
|
116
|
+
"angular.json",
|
|
117
|
+
# Nuxt
|
|
118
|
+
"nuxt.config.js", "nuxt.config.ts",
|
|
119
|
+
# Svelte
|
|
120
|
+
"svelte.config.js",
|
|
121
|
+
# Astro
|
|
122
|
+
"astro.config.mjs", "astro.config.ts",
|
|
123
|
+
# Remix
|
|
124
|
+
"remix.config.js", "remix.config.ts",
|
|
125
|
+
# Django
|
|
126
|
+
"manage.py",
|
|
127
|
+
]
|
|
128
|
+
|
|
108
129
|
@classmethod
|
|
109
130
|
def detect(cls, path: Path) -> bool:
|
|
110
131
|
"""
|
|
@@ -113,7 +134,8 @@ class DockerComposeDeployer:
|
|
|
113
134
|
Detection priority:
|
|
114
135
|
- If docker-compose.prod.yml exists -> True (strong signal)
|
|
115
136
|
- If only docker-compose.yml exists AND monorepo signals -> False
|
|
116
|
-
- If only docker-compose.yml exists
|
|
137
|
+
- If only docker-compose.yml exists AND framework config files -> False
|
|
138
|
+
- If only docker-compose.yml exists without other signals -> True
|
|
117
139
|
|
|
118
140
|
Args:
|
|
119
141
|
path: Path to check.
|
|
@@ -146,6 +168,14 @@ class DockerComposeDeployer:
|
|
|
146
168
|
if app_count >= 2:
|
|
147
169
|
return False
|
|
148
170
|
|
|
171
|
+
# Check for framework config files - if present, docker-compose.yml
|
|
172
|
+
# is likely just for local development (databases, caches, etc.)
|
|
173
|
+
has_framework = any(
|
|
174
|
+
(path / f).exists() for f in cls.FRAMEWORK_CONFIG_FILES
|
|
175
|
+
)
|
|
176
|
+
if has_framework:
|
|
177
|
+
return False
|
|
178
|
+
|
|
149
179
|
return True
|
|
150
180
|
|
|
151
181
|
def configure(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Python deployer for WASM.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Dict, List
|
|
7
8
|
import re
|
|
@@ -80,7 +81,8 @@ class PythonDeployer(BaseDeployer):
|
|
|
80
81
|
# Check for common patterns
|
|
81
82
|
if self.framework == "django":
|
|
82
83
|
# Look for wsgi.py or asgi.py
|
|
83
|
-
for root, dirs, files in (self.app_path)
|
|
84
|
+
for root, dirs, files in os.walk(self.app_path):
|
|
85
|
+
root = Path(root)
|
|
84
86
|
if "wsgi.py" in files:
|
|
85
87
|
# Get module path
|
|
86
88
|
rel_path = root.relative_to(self.app_path)
|
|
@@ -90,7 +92,6 @@ class PythonDeployer(BaseDeployer):
|
|
|
90
92
|
rel_path = root.relative_to(self.app_path)
|
|
91
93
|
module = str(rel_path).replace("/", ".")
|
|
92
94
|
asgi_app = f"{module}.asgi:application"
|
|
93
|
-
break
|
|
94
95
|
|
|
95
96
|
elif self.framework == "flask":
|
|
96
97
|
# Check for app.py, main.py, or __init__.py
|
|
@@ -57,10 +57,8 @@ class BaseManager(ABC):
|
|
|
57
57
|
"""
|
|
58
58
|
self.logger.debug(f"Running: {' '.join(command)}")
|
|
59
59
|
result = run_command(command, cwd=cwd, env=env, timeout=timeout)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self.logger.debug(f"Command failed: {result.stderr}")
|
|
63
|
-
|
|
60
|
+
self.logger.command_output(result.stdout, result.stderr)
|
|
61
|
+
|
|
64
62
|
return result
|
|
65
63
|
|
|
66
64
|
def _run_sudo(
|
|
@@ -84,10 +82,8 @@ class BaseManager(ABC):
|
|
|
84
82
|
"""
|
|
85
83
|
self.logger.debug(f"Running (sudo): {' '.join(command)}")
|
|
86
84
|
result = run_command_sudo(command, cwd=cwd, env=env, timeout=timeout)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self.logger.debug(f"Command failed: {result.stderr}")
|
|
90
|
-
|
|
85
|
+
self.logger.command_output(result.stdout, result.stderr)
|
|
86
|
+
|
|
91
87
|
return result
|
|
92
88
|
|
|
93
89
|
@abstractmethod
|
|
@@ -17,6 +17,13 @@ from enum import Enum
|
|
|
17
17
|
from typing import Dict, List, Optional, Callable, Any
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
+
from wasm.core.exceptions import (
|
|
21
|
+
BackupError,
|
|
22
|
+
CertificateError,
|
|
23
|
+
DeploymentError,
|
|
24
|
+
RollbackError,
|
|
25
|
+
)
|
|
26
|
+
|
|
20
27
|
|
|
21
28
|
class JobStatus(str, Enum):
|
|
22
29
|
"""Job execution status."""
|
|
@@ -477,12 +484,12 @@ def deploy_app_job(
|
|
|
477
484
|
|
|
478
485
|
if job_context.is_cancelled:
|
|
479
486
|
process.terminate()
|
|
480
|
-
raise
|
|
481
|
-
|
|
487
|
+
raise DeploymentError("Job cancelled by user")
|
|
488
|
+
|
|
482
489
|
process.wait()
|
|
483
|
-
|
|
490
|
+
|
|
484
491
|
if process.returncode != 0:
|
|
485
|
-
raise
|
|
492
|
+
raise DeploymentError(f"Deployment failed with exit code {process.returncode}")
|
|
486
493
|
|
|
487
494
|
job_context.update("Deployment complete", 100)
|
|
488
495
|
|
|
@@ -523,10 +530,10 @@ def update_app_job(
|
|
|
523
530
|
process.wait()
|
|
524
531
|
|
|
525
532
|
if process.returncode != 0:
|
|
526
|
-
raise
|
|
527
|
-
|
|
533
|
+
raise DeploymentError(f"Update failed with exit code {process.returncode}")
|
|
534
|
+
|
|
528
535
|
job_context.update("Update complete", 100)
|
|
529
|
-
|
|
536
|
+
|
|
530
537
|
return {"domain": domain, "status": "updated"}
|
|
531
538
|
|
|
532
539
|
|
|
@@ -566,10 +573,10 @@ def delete_app_job(
|
|
|
566
573
|
process.wait()
|
|
567
574
|
|
|
568
575
|
if process.returncode != 0:
|
|
569
|
-
raise
|
|
570
|
-
|
|
576
|
+
raise DeploymentError(f"Deletion failed with exit code {process.returncode}")
|
|
577
|
+
|
|
571
578
|
job_context.update("Deletion complete", 100)
|
|
572
|
-
|
|
579
|
+
|
|
573
580
|
return {"domain": domain, "status": "deleted"}
|
|
574
581
|
|
|
575
582
|
|
|
@@ -605,10 +612,10 @@ def backup_app_job(
|
|
|
605
612
|
process.wait()
|
|
606
613
|
|
|
607
614
|
if process.returncode != 0:
|
|
608
|
-
raise
|
|
609
|
-
|
|
615
|
+
raise BackupError(f"Backup failed with exit code {process.returncode}")
|
|
616
|
+
|
|
610
617
|
job_context.update("Backup complete", 100)
|
|
611
|
-
|
|
618
|
+
|
|
612
619
|
return {"domain": domain, "status": "backup_created", "path": backup_path}
|
|
613
620
|
|
|
614
621
|
|
|
@@ -644,10 +651,10 @@ def rollback_app_job(
|
|
|
644
651
|
process.wait()
|
|
645
652
|
|
|
646
653
|
if process.returncode != 0:
|
|
647
|
-
raise
|
|
648
|
-
|
|
654
|
+
raise RollbackError(f"Rollback failed with exit code {process.returncode}")
|
|
655
|
+
|
|
649
656
|
job_context.update("Rollback complete", 100)
|
|
650
|
-
|
|
657
|
+
|
|
651
658
|
return {"domain": domain, "status": "rolled_back"}
|
|
652
659
|
|
|
653
660
|
|
|
@@ -683,8 +690,8 @@ def cert_create_job(
|
|
|
683
690
|
process.wait()
|
|
684
691
|
|
|
685
692
|
if process.returncode != 0:
|
|
686
|
-
raise
|
|
687
|
-
|
|
693
|
+
raise CertificateError(f"Certificate creation failed with exit code {process.returncode}")
|
|
694
|
+
|
|
688
695
|
job_context.update("Certificate created", 100)
|
|
689
|
-
|
|
696
|
+
|
|
690
697
|
return {"domain": domain, "status": "certificate_created"}
|
|
@@ -6,6 +6,7 @@ API routing, and static file serving.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
|
+
import logging
|
|
9
10
|
import os
|
|
10
11
|
import signal
|
|
11
12
|
import socket
|
|
@@ -13,6 +14,8 @@ from contextlib import asynccontextmanager
|
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Optional
|
|
15
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
16
19
|
from fastapi import FastAPI, Request, Response, HTTPException, Depends
|
|
17
20
|
from fastapi.middleware.cors import CORSMiddleware
|
|
18
21
|
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
|
|
@@ -417,18 +420,16 @@ def run_server(
|
|
|
417
420
|
|
|
418
421
|
if show_token:
|
|
419
422
|
local_ip = get_local_ip() if host == "0.0.0.0" else host
|
|
420
|
-
print(
|
|
421
|
-
print("🌐 WASM Web Interface")
|
|
423
|
+
print()
|
|
422
424
|
print("=" * 60)
|
|
423
|
-
print(
|
|
424
|
-
print(
|
|
425
|
-
print(f"
|
|
426
|
-
print("
|
|
427
|
-
print("
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
sys.stdout.flush()
|
|
425
|
+
print("WASM Web Interface")
|
|
426
|
+
print("=" * 60)
|
|
427
|
+
print(f"Access Token: {master_token}")
|
|
428
|
+
print(f"Server: http://{local_ip}:{port}")
|
|
429
|
+
print(f"Quick Login: http://{local_ip}:{port}/login?token={master_token}")
|
|
430
|
+
print("Keep this token secure! It grants full access to WASM.")
|
|
431
|
+
print("=" * 60)
|
|
432
|
+
print(flush=True)
|
|
432
433
|
|
|
433
434
|
# Run server
|
|
434
435
|
uvicorn.run(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|