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.
Files changed (146) hide show
  1. {wasm_cli-0.14.1/src/wasm_cli.egg-info → wasm_cli-0.14.3}/PKG-INFO +1 -1
  2. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/pyproject.toml +1 -1
  3. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/setup.py +1 -1
  4. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/__init__.py +1 -1
  5. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/webapp.py +143 -15
  6. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/parser.py +22 -3
  7. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/__init__.py +2 -0
  8. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/logger.py +2 -0
  9. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/store.py +26 -0
  10. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/update_checker.py +21 -16
  11. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/__init__.py +2 -0
  12. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/__init__.py +4 -0
  13. wasm_cli-0.14.3/src/wasm/deployers/helpers/turbo.py +337 -0
  14. wasm_cli-0.14.3/src/wasm/deployers/helpers/workspace.py +423 -0
  15. wasm_cli-0.14.3/src/wasm/deployers/monorepo.py +1257 -0
  16. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/registry.py +2 -0
  17. wasm_cli-0.14.3/src/wasm/templates/nginx/monorepo.conf.j2 +144 -0
  18. {wasm_cli-0.14.1 → wasm_cli-0.14.3/src/wasm_cli.egg-info}/PKG-INFO +1 -1
  19. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/SOURCES.txt +4 -0
  20. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/LICENSE +0 -0
  21. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/MANIFEST.in +0 -0
  22. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/README.md +0 -0
  23. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/MONITOR.md +0 -0
  24. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/OBS_SETUP.md +0 -0
  25. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/assets/logo.png +0 -0
  26. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/docs/assets/logo_bg.png +0 -0
  27. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/man/wasm.1 +0 -0
  28. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/setup.cfg +0 -0
  29. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/__main__.py +0 -0
  30. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/__init__.py +0 -0
  31. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/__init__.py +0 -0
  32. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/backup.py +0 -0
  33. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/cert.py +0 -0
  34. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/config.py +0 -0
  35. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/db.py +0 -0
  36. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/health.py +0 -0
  37. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/monitor.py +0 -0
  38. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/service.py +0 -0
  39. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/setup.py +0 -0
  40. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/site.py +0 -0
  41. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/store.py +0 -0
  42. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/version.py +0 -0
  43. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/commands/web.py +0 -0
  44. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/cli/interactive.py +0 -0
  45. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/__init__.py +0 -0
  46. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/_wasm +0 -0
  47. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/wasm.bash +0 -0
  48. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/completions/wasm.fish +0 -0
  49. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/config.py +0 -0
  50. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/dependencies.py +0 -0
  51. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/exceptions.py +0 -0
  52. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/core/utils.py +0 -0
  53. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/base.py +0 -0
  54. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/package_manager.py +0 -0
  55. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/path_resolver.py +0 -0
  56. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/helpers/prisma.py +0 -0
  57. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/nextjs.py +0 -0
  58. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/nodejs.py +0 -0
  59. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/python.py +0 -0
  60. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/static.py +0 -0
  61. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/deployers/vite.py +0 -0
  62. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/main.py +0 -0
  63. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/__init__.py +0 -0
  64. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/apache_manager.py +0 -0
  65. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/backup_manager.py +0 -0
  66. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/base_manager.py +0 -0
  67. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/cert_manager.py +0 -0
  68. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/__init__.py +0 -0
  69. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/base.py +0 -0
  70. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/mongodb.py +0 -0
  71. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/mysql.py +0 -0
  72. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/postgres.py +0 -0
  73. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/redis.py +0 -0
  74. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/database/registry.py +0 -0
  75. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/nginx_manager.py +0 -0
  76. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/service_manager.py +0 -0
  77. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/managers/source_manager.py +0 -0
  78. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/__init__.py +0 -0
  79. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/ai_analyzer.py +0 -0
  80. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/email_notifier.py +0 -0
  81. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/process_monitor.py +0 -0
  82. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/monitor/threat_store.py +0 -0
  83. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/__init__.py +0 -0
  84. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/apache/proxy.conf.j2 +0 -0
  85. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/apache/static.conf.j2 +0 -0
  86. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/nginx/proxy.conf.j2 +0 -0
  87. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/nginx/static.conf.j2 +0 -0
  88. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/templates/systemd/app.service.j2 +0 -0
  89. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/__init__.py +0 -0
  90. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/domain.py +0 -0
  91. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/port.py +0 -0
  92. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/source.py +0 -0
  93. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/validators/ssh.py +0 -0
  94. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/__init__.py +0 -0
  95. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/__init__.py +0 -0
  96. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/apps.py +0 -0
  97. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/auth.py +0 -0
  98. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/backups.py +0 -0
  99. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/certs.py +0 -0
  100. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/config.py +0 -0
  101. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/databases.py +0 -0
  102. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/jobs.py +0 -0
  103. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/monitor.py +0 -0
  104. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/router.py +0 -0
  105. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/services.py +0 -0
  106. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/sites.py +0 -0
  107. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/api/system.py +0 -0
  108. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/auth.py +0 -0
  109. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/jobs.py +0 -0
  110. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/server.py +0 -0
  111. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/css/main.css +0 -0
  112. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/index.html +0 -0
  113. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/cards.js +0 -0
  114. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/jobs.js +0 -0
  115. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/metrics.js +0 -0
  116. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/components/skeleton.js +0 -0
  117. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/api.js +0 -0
  118. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/dialogs.js +0 -0
  119. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/notifications.js +0 -0
  120. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/router.js +0 -0
  121. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/search.js +0 -0
  122. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/shortcuts.js +0 -0
  123. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/theme.js +0 -0
  124. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/ui.js +0 -0
  125. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/core/websocket.js +0 -0
  126. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/main.js +0 -0
  127. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/apps.js +0 -0
  128. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/backups.js +0 -0
  129. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/certs.js +0 -0
  130. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/config.js +0 -0
  131. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/dashboard.js +0 -0
  132. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/databases.js +0 -0
  133. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/jobs.js +0 -0
  134. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/logs.js +0 -0
  135. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/monitor.js +0 -0
  136. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/services.js +0 -0
  137. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/js/pages/sites.js +0 -0
  138. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/login.html +0 -0
  139. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/logo.png +0 -0
  140. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/static/logo.webp +0 -0
  141. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/websockets/__init__.py +0 -0
  142. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm/web/websockets/router.py +0 -0
  143. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/dependency_links.txt +0 -0
  144. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/entry_points.txt +0 -0
  145. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/requires.txt +0 -0
  146. {wasm_cli-0.14.1 → wasm_cli-0.14.3}/src/wasm_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wasm-cli
3
- Version: 0.14.1
3
+ Version: 0.14.3
4
4
  Summary: Web App System Management - Deploy and manage web applications on Linux servers
5
5
  Author: Yago López Prado
6
6
  Author-email: Yago López Prado <yago.lopez.adeje@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wasm-cli"
7
- version = "0.14.1"
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.1",
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",
@@ -8,7 +8,7 @@ WASM - Web App System Management
8
8
  A robust CLI tool for deploying and managing web applications on Linux servers.
9
9
  """
10
10
 
11
- __version__ = "0.14.1"
11
+ __version__ = "0.14.3"
12
12
  __author__ = "Yago López Prado"
13
13
  __license__ = "WASM-NCSAL"
14
14
 
@@ -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
- app_name = domain_to_app_name(domain)
474
- app_path = config.apps_directory / app_name
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",
@@ -63,6 +63,8 @@ class Icons:
63
63
  CLOCK = "⏱"
64
64
  FOLDER = "📁"
65
65
  FILE = "📄"
66
+ DATABASE = "🗄️"
67
+ SEARCH = "🔍"
66
68
 
67
69
 
68
70
  class LogLevel(Enum):
@@ -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
- cls._update_version = cached_data["latest_version"]
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 if installed with pip in current Python environment
274
- result = subprocess.run(
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", "-l", "wasm"],
282
+ ["dpkg", "-s", "wasm"],
291
283
  capture_output=True,
292
284
  timeout=2
293
285
  )
294
- if result.returncode == 0 and b"wasm" in result.stdout:
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
  ]