wasm-cli 0.14.1__tar.gz → 0.14.3__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.14.1/src/wasm_cli.egg-info → wasm_cli-0.14.3}/PKG-INFO +1 -1
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/pyproject.toml +1 -1
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/setup.py +1 -1
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/__init__.py +1 -1
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/webapp.py +143 -15
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/parser.py +22 -3
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/__init__.py +2 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/logger.py +2 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/store.py +26 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/update_checker.py +21 -16
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/__init__.py +2 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/__init__.py +4 -0
- wasm_cli-0.14.3/src/wasm/deployers/helpers/turbo.py +337 -0
- wasm_cli-0.14.3/src/wasm/deployers/helpers/workspace.py +423 -0
- wasm_cli-0.14.3/src/wasm/deployers/monorepo.py +1257 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/registry.py +2 -0
- wasm_cli-0.14.3/src/wasm/templates/nginx/monorepo.conf.j2 +144 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3/src/wasm_cli.egg-info}/PKG-INFO +1 -1
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/SOURCES.txt +4 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/LICENSE +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/MANIFEST.in +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/README.md +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/MONITOR.md +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/OBS_SETUP.md +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/assets/logo.png +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/assets/logo_bg.png +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/man/wasm.1 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/setup.cfg +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/__main__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/backup.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/cert.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/config.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/db.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/health.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/monitor.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/service.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/setup.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/site.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/store.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/version.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/web.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/interactive.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/_wasm +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/wasm.bash +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/wasm.fish +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/config.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/dependencies.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/exceptions.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/utils.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/base.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/package_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/path_resolver.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/prisma.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/nextjs.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/nodejs.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/python.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/static.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/vite.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/main.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/apache_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/backup_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/base_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/cert_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/base.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/mongodb.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/mysql.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/postgres.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/redis.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/registry.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/nginx_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/service_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/source_manager.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/ai_analyzer.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/email_notifier.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/process_monitor.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/threat_store.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/apache/proxy.conf.j2 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/apache/static.conf.j2 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/nginx/proxy.conf.j2 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/nginx/static.conf.j2 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/systemd/app.service.j2 +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/domain.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/port.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/source.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/ssh.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/apps.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/auth.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/backups.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/certs.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/config.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/databases.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/jobs.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/monitor.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/router.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/services.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/sites.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/system.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/auth.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/jobs.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/server.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/css/main.css +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/index.html +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/cards.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/jobs.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/metrics.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/skeleton.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/api.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/dialogs.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/notifications.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/router.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/search.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/shortcuts.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/theme.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/ui.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/websocket.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/main.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/apps.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/backups.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/certs.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/config.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/dashboard.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/databases.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/jobs.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/logs.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/monitor.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/services.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/sites.js +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/login.html +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/logo.png +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/logo.webp +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/websockets/__init__.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/websockets/router.py +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/dependency_links.txt +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/entry_points.txt +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/requires.txt +0 -0
- {wasm_cli-0.14.1 → wasm_cli-0.14.3}/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.14.
|
|
7
|
+
version = "0.14.3"
|
|
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.14.
|
|
12
|
+
version="0.14.3",
|
|
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",
|
|
@@ -202,10 +202,14 @@ def _handle_create(args: Namespace) -> int:
|
|
|
202
202
|
logger.key_value("Package Manager", package_manager)
|
|
203
203
|
logger.key_value("SSL", "Yes" if not args.no_ssl else "No")
|
|
204
204
|
logger.blank()
|
|
205
|
-
|
|
205
|
+
|
|
206
|
+
# Handle monorepo deployments specially
|
|
207
|
+
if app_type == "monorepo":
|
|
208
|
+
return _handle_monorepo_create(args, domain, env_vars, logger)
|
|
209
|
+
|
|
206
210
|
# Get deployer
|
|
207
211
|
deployer = get_deployer(app_type, verbose=args.verbose)
|
|
208
|
-
|
|
212
|
+
|
|
209
213
|
# Configure deployer
|
|
210
214
|
deployer.configure(
|
|
211
215
|
domain=domain,
|
|
@@ -217,10 +221,51 @@ def _handle_create(args: Namespace) -> int:
|
|
|
217
221
|
env_vars=env_vars,
|
|
218
222
|
package_manager=package_manager,
|
|
219
223
|
)
|
|
220
|
-
|
|
224
|
+
|
|
221
225
|
# Run deployment
|
|
222
226
|
deployer.deploy()
|
|
223
|
-
|
|
227
|
+
|
|
228
|
+
return 0
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _handle_monorepo_create(args: Namespace, domain: str, env_vars: Dict, logger: Logger) -> int:
|
|
232
|
+
"""Handle monorepo deployment specially."""
|
|
233
|
+
from wasm.deployers.monorepo import MonorepoDeployer
|
|
234
|
+
|
|
235
|
+
# Parse subdomain overrides
|
|
236
|
+
subdomain_overrides = {}
|
|
237
|
+
if getattr(args, "subdomains", None):
|
|
238
|
+
for mapping in args.subdomains:
|
|
239
|
+
if ":" in mapping:
|
|
240
|
+
app_name, subdomain = mapping.split(":", 1)
|
|
241
|
+
subdomain_overrides[app_name] = subdomain
|
|
242
|
+
else:
|
|
243
|
+
logger.warning(f"Invalid subdomain mapping: {mapping} (expected app:subdomain)")
|
|
244
|
+
|
|
245
|
+
# Get workspace filter
|
|
246
|
+
workspace_filter = getattr(args, "workspaces", None)
|
|
247
|
+
|
|
248
|
+
# Get skip_database flag
|
|
249
|
+
skip_database = getattr(args, "no_database", False)
|
|
250
|
+
|
|
251
|
+
# Create and configure deployer
|
|
252
|
+
deployer = MonorepoDeployer(verbose=args.verbose)
|
|
253
|
+
|
|
254
|
+
deployer.configure(
|
|
255
|
+
domain=domain,
|
|
256
|
+
source=args.source,
|
|
257
|
+
webserver=args.webserver,
|
|
258
|
+
ssl=not args.no_ssl,
|
|
259
|
+
branch=args.branch,
|
|
260
|
+
env_vars=env_vars,
|
|
261
|
+
subdomain_overrides=subdomain_overrides,
|
|
262
|
+
workspace_filter=workspace_filter,
|
|
263
|
+
skip_database=skip_database,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Run deployment
|
|
267
|
+
deployer.deploy()
|
|
268
|
+
|
|
224
269
|
return 0
|
|
225
270
|
|
|
226
271
|
|
|
@@ -457,7 +502,7 @@ def _handle_start(args: Namespace) -> int:
|
|
|
457
502
|
def _handle_update(args: Namespace) -> int:
|
|
458
503
|
"""
|
|
459
504
|
Handle webapp update command with zero-downtime strategy.
|
|
460
|
-
|
|
505
|
+
|
|
461
506
|
Strategy:
|
|
462
507
|
1. Create pre-update backup (for rollback)
|
|
463
508
|
2. Pull/fetch new code
|
|
@@ -468,11 +513,24 @@ def _handle_update(args: Namespace) -> int:
|
|
|
468
513
|
"""
|
|
469
514
|
logger = Logger(verbose=args.verbose)
|
|
470
515
|
config = Config()
|
|
471
|
-
|
|
516
|
+
|
|
517
|
+
from wasm.core.store import get_store
|
|
518
|
+
store = get_store()
|
|
519
|
+
|
|
472
520
|
domain = validate_domain(args.domain)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
521
|
+
|
|
522
|
+
# Consultar BD primero para obtener el app_path real
|
|
523
|
+
app = store.get_app(domain)
|
|
524
|
+
|
|
525
|
+
if app and app.app_path:
|
|
526
|
+
# Usar el path almacenado en la BD (soporta apps legacy con prefijo wasm-)
|
|
527
|
+
app_path = Path(app.app_path)
|
|
528
|
+
app_name = app_path.name
|
|
529
|
+
else:
|
|
530
|
+
# Fallback para apps no registradas en BD
|
|
531
|
+
app_name = domain_to_app_name(domain)
|
|
532
|
+
app_path = config.apps_directory / app_name
|
|
533
|
+
|
|
476
534
|
if not app_path.exists():
|
|
477
535
|
raise WASMError(f"Application not found: {domain}")
|
|
478
536
|
|
|
@@ -536,22 +594,28 @@ def _handle_update(args: Namespace) -> int:
|
|
|
536
594
|
else:
|
|
537
595
|
logger.substep(f"Detected: {app_type}")
|
|
538
596
|
|
|
597
|
+
# Handle monorepo updates with dedicated flow
|
|
598
|
+
if app_type == "monorepo":
|
|
599
|
+
return _handle_monorepo_update(
|
|
600
|
+
args, app_path, app_name, domain, logger, total_steps
|
|
601
|
+
)
|
|
602
|
+
|
|
539
603
|
deployer = get_deployer(app_type, verbose=args.verbose)
|
|
540
604
|
deployer.app_path = app_path
|
|
541
605
|
deployer.app_name = app_name
|
|
542
606
|
deployer.domain = domain
|
|
543
607
|
deployer._package_manager = package_manager
|
|
544
|
-
|
|
608
|
+
|
|
545
609
|
# Run pre_install to detect package manager and prisma
|
|
546
610
|
deployer.pre_install()
|
|
547
611
|
logger.substep(f"Package manager: {deployer.package_manager}")
|
|
548
612
|
if deployer.has_prisma:
|
|
549
613
|
logger.substep("Prisma detected")
|
|
550
|
-
|
|
614
|
+
|
|
551
615
|
# Step 4: Install dependencies (without stopping the app)
|
|
552
616
|
logger.step(4, total_steps, "Installing dependencies")
|
|
553
617
|
deployer.install_dependencies()
|
|
554
|
-
|
|
618
|
+
|
|
555
619
|
# Step 5: Generate Prisma and run migrations if needed
|
|
556
620
|
if deployer.has_prisma:
|
|
557
621
|
logger.step(5, total_steps, "Updating Prisma")
|
|
@@ -559,11 +623,11 @@ def _handle_update(args: Namespace) -> int:
|
|
|
559
623
|
deployer.run_prisma_migrate(deploy=True)
|
|
560
624
|
else:
|
|
561
625
|
logger.step(5, total_steps, "Prisma not detected, skipping")
|
|
562
|
-
|
|
626
|
+
|
|
563
627
|
# Step 6: Build application (without stopping the app)
|
|
564
628
|
logger.step(6, total_steps, "Building application")
|
|
565
629
|
deployer.build()
|
|
566
|
-
|
|
630
|
+
|
|
567
631
|
# Step 7: Restart service (only if not static)
|
|
568
632
|
logger.step(7, total_steps, "Restarting application")
|
|
569
633
|
service_manager = ServiceManager(verbose=args.verbose)
|
|
@@ -602,7 +666,71 @@ def _handle_update(args: Namespace) -> int:
|
|
|
602
666
|
else:
|
|
603
667
|
logger.warning("Application restarted but may not be running correctly")
|
|
604
668
|
logger.info(f"Check logs with: wasm logs {domain}")
|
|
605
|
-
|
|
669
|
+
|
|
670
|
+
return 0
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def _handle_monorepo_update(args, app_path, app_name, domain, logger, total_steps):
|
|
674
|
+
"""Handle update for monorepo applications."""
|
|
675
|
+
from wasm.deployers.monorepo import MonorepoDeployer
|
|
676
|
+
|
|
677
|
+
deployer = MonorepoDeployer(verbose=args.verbose)
|
|
678
|
+
deployer.app_path = app_path
|
|
679
|
+
deployer.app_name = app_name
|
|
680
|
+
deployer.domain = domain
|
|
681
|
+
deployer.package_manager = "pnpm"
|
|
682
|
+
|
|
683
|
+
# Step 4: Install dependencies
|
|
684
|
+
logger.step(4, total_steps, "Installing dependencies")
|
|
685
|
+
deployer._install_dependencies()
|
|
686
|
+
|
|
687
|
+
# Step 5: Run Prisma migrations
|
|
688
|
+
logger.step(5, total_steps, "Running database migrations")
|
|
689
|
+
deployer._run_prisma_migrations()
|
|
690
|
+
|
|
691
|
+
# Step 6: Build all workspaces
|
|
692
|
+
logger.step(6, total_steps, "Building applications")
|
|
693
|
+
deployer._set_permissions()
|
|
694
|
+
deployer._build_all()
|
|
695
|
+
|
|
696
|
+
# Step 7: Restart all workspace services
|
|
697
|
+
logger.step(7, total_steps, "Restarting applications")
|
|
698
|
+
service_manager = ServiceManager(verbose=args.verbose)
|
|
699
|
+
|
|
700
|
+
# Find all services for this monorepo
|
|
701
|
+
from wasm.core.store import get_store
|
|
702
|
+
store = get_store()
|
|
703
|
+
app = store.get_app(domain)
|
|
704
|
+
|
|
705
|
+
restarted = []
|
|
706
|
+
if app:
|
|
707
|
+
all_services = store.list_services()
|
|
708
|
+
services = [s for s in all_services if s.app_id == app.id]
|
|
709
|
+
for service in services:
|
|
710
|
+
logger.substep(f"Restarting {service.name}")
|
|
711
|
+
try:
|
|
712
|
+
service_manager.restart(service.name)
|
|
713
|
+
restarted.append(service.name)
|
|
714
|
+
except Exception as e:
|
|
715
|
+
logger.warning(f"Failed to restart {service.name}: {e}")
|
|
716
|
+
else:
|
|
717
|
+
# Fallback: restart by app_name pattern
|
|
718
|
+
status = service_manager.get_status(app_name)
|
|
719
|
+
if status.get("exists"):
|
|
720
|
+
service_manager.restart(app_name)
|
|
721
|
+
restarted.append(app_name)
|
|
722
|
+
|
|
723
|
+
if restarted:
|
|
724
|
+
import time
|
|
725
|
+
time.sleep(3)
|
|
726
|
+
logger.success(f"Monorepo updated successfully: {domain}")
|
|
727
|
+
logger.blank()
|
|
728
|
+
for name in restarted:
|
|
729
|
+
logger.key_value("Restarted", name)
|
|
730
|
+
else:
|
|
731
|
+
logger.warning("No services found to restart")
|
|
732
|
+
logger.info(f"Try redeploying: wasm create -d {domain}")
|
|
733
|
+
|
|
606
734
|
return 0
|
|
607
735
|
|
|
608
736
|
|
|
@@ -36,7 +36,7 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
36
36
|
CLI tool for deploying and managing web applications on Linux servers.
|
|
37
37
|
Automates: Git clone, build, systemd, Nginx/Apache, SSL (Let's Encrypt).
|
|
38
38
|
|
|
39
|
-
Supported app types: nextjs, nodejs, vite, python, static
|
|
39
|
+
Supported app types: nextjs, nodejs, vite, python, static, monorepo
|
|
40
40
|
|
|
41
41
|
QUICK START:
|
|
42
42
|
wasm setup init Initialize directories (first time, requires sudo)
|
|
@@ -179,7 +179,7 @@ def _add_webapp_commands(subparsers) -> None:
|
|
|
179
179
|
)
|
|
180
180
|
create.add_argument(
|
|
181
181
|
"--type", "-t",
|
|
182
|
-
choices=["nextjs", "nodejs", "vite", "python", "static", "auto"],
|
|
182
|
+
choices=["nextjs", "nodejs", "vite", "python", "static", "monorepo", "auto"],
|
|
183
183
|
default="auto",
|
|
184
184
|
help="Application type (default: auto-detect)",
|
|
185
185
|
)
|
|
@@ -213,7 +213,26 @@ def _add_webapp_commands(subparsers) -> None:
|
|
|
213
213
|
default="auto",
|
|
214
214
|
help="Package manager to use (default: auto-detect)",
|
|
215
215
|
)
|
|
216
|
-
|
|
216
|
+
|
|
217
|
+
# Monorepo-specific options
|
|
218
|
+
create.add_argument(
|
|
219
|
+
"--subdomains",
|
|
220
|
+
nargs="+",
|
|
221
|
+
metavar="APP:SUBDOMAIN",
|
|
222
|
+
help="Subdomain mapping for monorepo apps (e.g., erp-backend:api web-gateway:app)",
|
|
223
|
+
)
|
|
224
|
+
create.add_argument(
|
|
225
|
+
"--workspaces",
|
|
226
|
+
nargs="+",
|
|
227
|
+
metavar="NAME",
|
|
228
|
+
help="Specific workspace apps to deploy (default: all)",
|
|
229
|
+
)
|
|
230
|
+
create.add_argument(
|
|
231
|
+
"--no-database",
|
|
232
|
+
action="store_true",
|
|
233
|
+
help="Skip database provisioning for monorepo",
|
|
234
|
+
)
|
|
235
|
+
|
|
217
236
|
# list (alias: ls)
|
|
218
237
|
subparsers.add_parser(
|
|
219
238
|
"list",
|
|
@@ -11,6 +11,7 @@ from wasm.core.store import (
|
|
|
11
11
|
Service,
|
|
12
12
|
Database,
|
|
13
13
|
DatabaseUser,
|
|
14
|
+
MonorepoWorkspace,
|
|
14
15
|
AppType,
|
|
15
16
|
AppStatus,
|
|
16
17
|
WebServer,
|
|
@@ -28,6 +29,7 @@ __all__ = [
|
|
|
28
29
|
"Service",
|
|
29
30
|
"Database",
|
|
30
31
|
"DatabaseUser",
|
|
32
|
+
"MonorepoWorkspace",
|
|
31
33
|
"AppType",
|
|
32
34
|
"AppStatus",
|
|
33
35
|
"WebServer",
|
|
@@ -35,6 +35,7 @@ class AppType(str, Enum):
|
|
|
35
35
|
PYTHON = "python"
|
|
36
36
|
VITE = "vite"
|
|
37
37
|
STATIC = "static"
|
|
38
|
+
MONOREPO = "monorepo"
|
|
38
39
|
UNKNOWN = "unknown"
|
|
39
40
|
|
|
40
41
|
|
|
@@ -211,6 +212,31 @@ class DatabaseUser:
|
|
|
211
212
|
return cls(**dict(row))
|
|
212
213
|
|
|
213
214
|
|
|
215
|
+
@dataclass
|
|
216
|
+
class MonorepoWorkspace:
|
|
217
|
+
"""
|
|
218
|
+
Configuration for a workspace app within a monorepo.
|
|
219
|
+
|
|
220
|
+
Used by MonorepoDeployer to track individual apps in a Turborepo/pnpm
|
|
221
|
+
workspace monorepo.
|
|
222
|
+
"""
|
|
223
|
+
name: str = ""
|
|
224
|
+
path: str = ""
|
|
225
|
+
app_type: str = AppType.UNKNOWN.value
|
|
226
|
+
subdomain: str = ""
|
|
227
|
+
port: int = 3000
|
|
228
|
+
start_command: Optional[str] = None
|
|
229
|
+
health_check: str = "/"
|
|
230
|
+
env_vars: Dict[str, str] = field(default_factory=dict)
|
|
231
|
+
|
|
232
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
233
|
+
"""Convert to dictionary."""
|
|
234
|
+
d = asdict(self)
|
|
235
|
+
if isinstance(d.get('env_vars'), dict):
|
|
236
|
+
d['env_vars'] = json.dumps(d['env_vars'])
|
|
237
|
+
return d
|
|
238
|
+
|
|
239
|
+
|
|
214
240
|
# Schema version for migrations
|
|
215
241
|
SCHEMA_VERSION = 1
|
|
216
242
|
|
|
@@ -45,7 +45,10 @@ class UpdateChecker:
|
|
|
45
45
|
if cls._is_cache_valid():
|
|
46
46
|
cached_data = cls._read_cache()
|
|
47
47
|
if cached_data and cached_data.get("has_update"):
|
|
48
|
-
|
|
48
|
+
cached_version = cached_data["latest_version"]
|
|
49
|
+
# Re-verify against current version (user may have updated)
|
|
50
|
+
if cls._is_newer_version(cached_version, __version__):
|
|
51
|
+
cls._update_version = cached_version
|
|
49
52
|
return
|
|
50
53
|
|
|
51
54
|
# Start background thread for network request
|
|
@@ -270,28 +273,17 @@ class UpdateChecker:
|
|
|
270
273
|
if pipx_path.exists():
|
|
271
274
|
return "pipx"
|
|
272
275
|
|
|
273
|
-
# Check
|
|
274
|
-
|
|
275
|
-
[sys.executable, "-m", "pip", "show", "wasm-cli"],
|
|
276
|
-
capture_output=True,
|
|
277
|
-
text=True,
|
|
278
|
-
timeout=2
|
|
279
|
-
)
|
|
280
|
-
if result.returncode == 0:
|
|
281
|
-
# Check if installed in editable mode (from source)
|
|
282
|
-
if "Editable project location:" in result.stdout or "-e " in result.stdout:
|
|
283
|
-
return "source"
|
|
284
|
-
return "pip"
|
|
276
|
+
# Check system package managers BEFORE pip, since system packages
|
|
277
|
+
# install Python files that pip can also detect (false positive)
|
|
285
278
|
|
|
286
|
-
# Check system package managers
|
|
287
279
|
# APT (Ubuntu, Debian) - package is called "wasm" not "wasm-cli"
|
|
288
280
|
if Path("/var/lib/dpkg/status").exists():
|
|
289
281
|
result = subprocess.run(
|
|
290
|
-
["dpkg", "-
|
|
282
|
+
["dpkg", "-s", "wasm"],
|
|
291
283
|
capture_output=True,
|
|
292
284
|
timeout=2
|
|
293
285
|
)
|
|
294
|
-
if result.returncode == 0
|
|
286
|
+
if result.returncode == 0:
|
|
295
287
|
return "apt"
|
|
296
288
|
|
|
297
289
|
# DNF/YUM (Fedora, RHEL, CentOS)
|
|
@@ -319,6 +311,19 @@ class UpdateChecker:
|
|
|
319
311
|
except FileNotFoundError:
|
|
320
312
|
pass
|
|
321
313
|
|
|
314
|
+
# Check pip (after system package managers)
|
|
315
|
+
result = subprocess.run(
|
|
316
|
+
[sys.executable, "-m", "pip", "show", "wasm-cli"],
|
|
317
|
+
capture_output=True,
|
|
318
|
+
text=True,
|
|
319
|
+
timeout=2
|
|
320
|
+
)
|
|
321
|
+
if result.returncode == 0:
|
|
322
|
+
# Check if installed in editable mode (from source)
|
|
323
|
+
if "Editable project location:" in result.stdout or "-e " in result.stdout:
|
|
324
|
+
return "source"
|
|
325
|
+
return "pip"
|
|
326
|
+
|
|
322
327
|
except Exception as e:
|
|
323
328
|
logger.debug(f"Failed to detect installation method: {e}")
|
|
324
329
|
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from wasm.deployers.base import BaseDeployer
|
|
4
4
|
from wasm.deployers.registry import DeployerRegistry, get_deployer, detect_app_type
|
|
5
|
+
from wasm.deployers.monorepo import MonorepoDeployer
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"BaseDeployer",
|
|
8
9
|
"DeployerRegistry",
|
|
9
10
|
"get_deployer",
|
|
10
11
|
"detect_app_type",
|
|
12
|
+
"MonorepoDeployer",
|
|
11
13
|
]
|
|
@@ -12,9 +12,13 @@ to improve maintainability and testability.
|
|
|
12
12
|
from wasm.deployers.helpers.package_manager import PackageManagerHelper
|
|
13
13
|
from wasm.deployers.helpers.path_resolver import PathResolver
|
|
14
14
|
from wasm.deployers.helpers.prisma import PrismaHelper
|
|
15
|
+
from wasm.deployers.helpers.workspace import WorkspaceHelper
|
|
16
|
+
from wasm.deployers.helpers.turbo import TurboHelper
|
|
15
17
|
|
|
16
18
|
__all__ = [
|
|
17
19
|
"PackageManagerHelper",
|
|
18
20
|
"PathResolver",
|
|
19
21
|
"PrismaHelper",
|
|
22
|
+
"WorkspaceHelper",
|
|
23
|
+
"TurboHelper",
|
|
20
24
|
]
|