rocketdoo 2.1.0__tar.gz → 2.2.0__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 (54) hide show
  1. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/LICENSE +1 -1
  2. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/MANIFEST.in +2 -0
  3. {rocketdoo-2.1.0/rocketdoo.egg-info → rocketdoo-2.2.0}/PKG-INFO +3 -3
  4. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/README.md +1 -1
  5. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/pyproject.toml +2 -1
  6. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/cli.py +21 -0
  7. rocketdoo-2.2.0/rocketdoo/core/deploy/__init__.py +19 -0
  8. rocketdoo-2.2.0/rocketdoo/core/deploy/base.py +319 -0
  9. rocketdoo-2.2.0/rocketdoo/core/deploy/config_manager.py +611 -0
  10. rocketdoo-2.2.0/rocketdoo/core/deploy/module_packager.py +353 -0
  11. rocketdoo-2.2.0/rocketdoo/core/deploy/odoo_sh.py +591 -0
  12. rocketdoo-2.2.0/rocketdoo/core/deploy/vps.py +622 -0
  13. rocketdoo-2.2.0/rocketdoo/core/module_scanner.py +267 -0
  14. rocketdoo-2.2.0/rocketdoo/deploy_cli.py +507 -0
  15. rocketdoo-2.2.0/rocketdoo/templates/deploy/.deployignore +75 -0
  16. rocketdoo-2.2.0/rocketdoo/templates/deploy/deploy.yaml +0 -0
  17. rocketdoo-2.2.0/rocketdoo/templates/deploy/deploy.yaml.jinja +172 -0
  18. {rocketdoo-2.1.0 → rocketdoo-2.2.0/rocketdoo.egg-info}/PKG-INFO +3 -3
  19. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo.egg-info/SOURCES.txt +12 -1
  20. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/__init__.py +0 -0
  21. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/config.py +0 -0
  22. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/__init__.py +0 -0
  23. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/config_loader.py +0 -0
  24. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/edition_setup.py +0 -0
  25. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/generator.py +0 -0
  26. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/gitman_config.py +0 -0
  27. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/port_validation.py +0 -0
  28. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/ssh_manager.py +0 -0
  29. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/core/utils.py +0 -0
  30. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/delete_identifiers.py +0 -0
  31. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/docker_cli.py +0 -0
  32. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/init_project.py +0 -0
  33. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/main.py +0 -0
  34. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/project_info.py +0 -0
  35. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/rocketdoo.py +0 -0
  36. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/scaffold.py +0 -0
  37. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/.devcontainer/devcontainer.json +0 -0
  38. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/.devcontainer/docker-compose.yaml +0 -0
  39. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/.vscode/launch.json +0 -0
  40. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/.vscode/launch.json.jinja +0 -0
  41. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/.vscode/settings.json +0 -0
  42. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/Dockerfile.jinja +0 -0
  43. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/addons/.gitkeep +0 -0
  44. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/config/odoo.conf +0 -0
  45. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/config/odoo.conf.jinja +0 -0
  46. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/docker-compose.yaml.jinja +0 -0
  47. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/install_dependencies.sh +0 -0
  48. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/templates/odoo_pg_pass +0 -0
  49. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo/welcome.py +0 -0
  50. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo.egg-info/dependency_links.txt +0 -0
  51. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo.egg-info/entry_points.txt +0 -0
  52. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo.egg-info/requires.txt +0 -0
  53. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/rocketdoo.egg-info/top_level.txt +0 -0
  54. {rocketdoo-2.1.0 → rocketdoo-2.2.0}/setup.cfg +0 -0
@@ -57,7 +57,7 @@ If the Library as you received it specifies that a proxy can decide whether futu
57
57
  <div RKD as ROCKETDOO V2=""></div>
58
58
 
59
59
  Licencia: LGPL-3.0+
60
- Versión: "2.1.0"
60
+ Versión: "2.2.0"
61
61
  Autor: Horacio Montaño, Elias Braceras
62
62
  Fecha: 16/10/2024
63
63
  Descripción: Framework to development Odoo
@@ -1,5 +1,7 @@
1
1
  include README.md
2
2
  include LICENSE
3
+ include rocketdoo/templates/deploy/.deployignore
4
+ include rocketdoo/templates/deploy/deploy.yaml.jinja
3
5
  recursive-include rocketdoo/templates *
4
6
  recursive-include rocketdoo/templates .*
5
7
  recursive-include rocketdoo/templates *.jinja
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocketdoo
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Framework for creating Odoo development environments with Docker and custom templates.
5
5
  Author-email: Horacio Montaño <horaciomontano@hdmsoft.com.ar>
6
6
  License: GNU LESSER GENERAL PUBLIC LICENSE
@@ -62,7 +62,7 @@ License: GNU LESSER GENERAL PUBLIC LICENSE
62
62
  <div RKD as ROCKETDOO V2=""></div>
63
63
 
64
64
  Licencia: LGPL-3.0+
65
- Versión: "2.1.0"
65
+ Versión: "2.2.0"
66
66
  Autor: Horacio Montaño, Elias Braceras
67
67
  Fecha: 16/10/2024
68
68
  Descripción: Framework to development Odoo
@@ -95,7 +95,7 @@ Odoo Development Framework
95
95
  - "Horacio Montaño" and "Elias Braceras"
96
96
 
97
97
  ## Version:
98
- - "2.1.0"
98
+ - "2.2.0"
99
99
 
100
100
  ----------------------------------------------------------------------------------------------------------------------------------------------------------
101
101
 
@@ -15,7 +15,7 @@ Odoo Development Framework
15
15
  - "Horacio Montaño" and "Elias Braceras"
16
16
 
17
17
  ## Version:
18
- - "2.1.0"
18
+ - "2.2.0"
19
19
 
20
20
  ----------------------------------------------------------------------------------------------------------------------------------------------------------
21
21
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rocketdoo"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "Framework for creating Odoo development environments with Docker and custom templates."
5
5
  authors = [
6
6
  { name = "Horacio Montaño", email = "horaciomontano@hdmsoft.com.ar" }
@@ -38,6 +38,7 @@ rocketdoo = [
38
38
  "templates/**/**/*",
39
39
  "templates/**/.vscode/*",
40
40
  "templates/**/*.jinja",
41
+ "templates/deploy/*",
41
42
  "templates/**/.*",
42
43
  "templates/**/**/.*",
43
44
  ]
@@ -11,6 +11,14 @@ from .init_project import init_project
11
11
  from .project_info import get_project_info, project_exists
12
12
  from rocketdoo import __version__
13
13
  from rocketdoo.docker_cli import docker, up, down, status, stop, pause, logs, build, restart
14
+ from rocketdoo.deploy_cli import (
15
+ deploy,
16
+ deploy_init,
17
+ list_modules,
18
+ deploy_config,
19
+ deploy_run,
20
+ validate_modules
21
+ )
14
22
 
15
23
  # Detect the command name used to invoke the CLI
16
24
  PROG_NAME = "rkd" if "rkd" in sys.argv[0] else "rocketdoo"
@@ -417,5 +425,18 @@ main.add_command(logs)
417
425
  main.add_command(build)
418
426
  main.add_command(delete_command)
419
427
 
428
+ # ============================================================
429
+ # 🚀 Register Deploy commands as Rocketdoo subcommands
430
+ # ============================================================
431
+ # Complete subgroup
432
+ main.add_command(deploy, name="deploy")
433
+
434
+ # Direct aliases for common deploy commands
435
+ main.add_command(deploy_init, name="deploy-init")
436
+ main.add_command(list_modules, name="deploy-list")
437
+ main.add_command(deploy_run, name="deploy-run")
438
+ main.add_command(deploy_config, name="deploy-config")
439
+ main.add_command(validate_modules, name="deploy-validate")
440
+
420
441
  if __name__ == "__main__":
421
442
  main()
@@ -0,0 +1,19 @@
1
+ """
2
+ RocketDoo Deploy Module
3
+ Deployment functionality for Odoo modules to VPS and Odoo.sh
4
+ """
5
+
6
+ from .base import BaseDeployer, DeploymentResult
7
+ from .config_manager import DeployConfigManager
8
+ from .module_packager import ModulePackager
9
+ from .vps import VPSDeployer
10
+ from .odoo_sh import OdooSHDeployer
11
+
12
+ __all__ = [
13
+ 'BaseDeployer',
14
+ 'DeploymentResult',
15
+ 'DeployConfigManager',
16
+ 'ModulePackager',
17
+ 'VPSDeployer',
18
+ 'OdooSHDeployer'
19
+ ]
@@ -0,0 +1,319 @@
1
+
2
+ """
3
+ RocketDoo Deploy - Base Deployer
4
+ Abstract base class for all deployers
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from pathlib import Path
9
+ from typing import List, Dict, Optional
10
+ from datetime import datetime
11
+ from rich.console import Console
12
+ from rich.progress import Progress, SpinnerColumn, TextColumn
13
+
14
+ console = Console()
15
+
16
+
17
+ class DeploymentResult:
18
+ """Result of a deployment operation"""
19
+
20
+ def __init__(self, success: bool, message: str, details: Optional[Dict] = None):
21
+ self.success = success
22
+ self.message = message
23
+ self.details = details or {}
24
+ self.timestamp = datetime.now()
25
+
26
+ def __bool__(self):
27
+ return self.success
28
+
29
+ def __str__(self):
30
+ status = "✅ SUCCESS" if self.success else "❌ FAILED"
31
+ return f"{status}: {self.message}"
32
+
33
+
34
+ class BaseDeployer(ABC):
35
+ """
36
+ Abstract base class for deployers
37
+
38
+ Each deployment type (VPS, Odoo.sh, etc) inherits from this class
39
+ and implements the abstract methods.
40
+ """
41
+
42
+ def __init__(self, target_name: str, config: Dict, project_path: Path):
43
+ """
44
+ Initialize the deployer
45
+
46
+ Args:
47
+ target_name: Name of the target (e.g., 'vps_production')
48
+ config: Target configuration from deploy.yaml
49
+ project_path: Root path of the project
50
+ """
51
+ self.target_name = target_name
52
+ self.config = config
53
+ self.project_path = Path(project_path)
54
+ self.addons_path = self.project_path / config.get('modules', {}).get('base_path', 'addons')
55
+ self.console = console
56
+
57
+ # Deployment logs
58
+ self.logs = []
59
+
60
+ def log(self, message: str, level: str = "info"):
61
+ """Log a message"""
62
+ timestamp = datetime.now().strftime("%H:%M:%S")
63
+ self.logs.append({
64
+ 'timestamp': timestamp,
65
+ 'level': level,
66
+ 'message': message
67
+ })
68
+
69
+ # Also print to console
70
+ styles = {
71
+ 'info': 'cyan',
72
+ 'success': 'green',
73
+ 'warning': 'yellow',
74
+ 'error': 'red'
75
+ }
76
+ self.console.print(f"[{timestamp}] {message}", style=styles.get(level, 'white'))
77
+
78
+ @abstractmethod
79
+ def validate_config(self) -> List[str]:
80
+ """
81
+ Validate deployer configuration
82
+
83
+ Returns:
84
+ List of configuration errors (empty if OK)
85
+ """
86
+ pass
87
+
88
+ @abstractmethod
89
+ def pre_deploy_check(self) -> bool:
90
+ """
91
+ Pre-deployment checks (connectivity, permissions, etc)
92
+
93
+ Returns:
94
+ True if all checks pass
95
+ """
96
+ pass
97
+
98
+ @abstractmethod
99
+ def deploy_modules(self, modules: List[Dict]) -> DeploymentResult:
100
+ """
101
+ Deploy the modules
102
+
103
+ Args:
104
+ modules: List of modules to deploy (from ModuleScanner)
105
+
106
+ Returns:
107
+ DeploymentResult with the operation result
108
+ """
109
+ pass
110
+
111
+ @abstractmethod
112
+ def post_deploy_actions(self) -> DeploymentResult:
113
+ """
114
+ Post-deployment actions (restart, update, etc)
115
+
116
+ Returns:
117
+ DeploymentResult with the operation result
118
+ """
119
+ pass
120
+
121
+ def rollback(self) -> DeploymentResult:
122
+ """
123
+ Rollback in case of error
124
+ Default does nothing, subclasses can implement
125
+
126
+ Returns:
127
+ DeploymentResult with rollback result
128
+ """
129
+ self.log("Rollback not implemented for this deployer", "warning")
130
+ return DeploymentResult(
131
+ success=True,
132
+ message="No rollback needed"
133
+ )
134
+
135
+ def create_backup(self, modules: List[Dict]) -> bool:
136
+ """
137
+ Create backup of modules before deployment
138
+
139
+ Args:
140
+ modules: List of modules to backup
141
+
142
+ Returns:
143
+ True if backup was successful
144
+ """
145
+ backup_config = self.config.get('backup', {})
146
+ if not backup_config.get('enabled', True):
147
+ return True
148
+
149
+ try:
150
+ backup_path = self.project_path / backup_config.get('path', '.rkd/deploy_backups')
151
+ backup_path.mkdir(parents=True, exist_ok=True)
152
+
153
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
154
+ backup_dir = backup_path / f"{self.target_name}_{timestamp}"
155
+ backup_dir.mkdir()
156
+
157
+ # Copy modules to backup
158
+ import shutil
159
+ for module in modules:
160
+ src = Path(module['full_path'])
161
+ dst = backup_dir / module['name']
162
+ shutil.copytree(src, dst)
163
+
164
+ self.log(f"Backup created at: {backup_dir}", "success")
165
+
166
+ # Clean up old backups
167
+ self._cleanup_old_backups(backup_path, backup_config.get('keep_last', 3))
168
+
169
+ return True
170
+
171
+ except Exception as e:
172
+ self.log(f"Error creating backup: {e}", "error")
173
+ return False
174
+
175
+ def _cleanup_old_backups(self, backup_path: Path, keep_last: int):
176
+ """Delete old backups keeping only the last N"""
177
+ try:
178
+ backups = sorted(
179
+ [d for d in backup_path.iterdir() if d.is_dir() and d.name.startswith(self.target_name)],
180
+ key=lambda x: x.stat().st_mtime,
181
+ reverse=True
182
+ )
183
+
184
+ # Delete old backups
185
+ for backup in backups[keep_last:]:
186
+ import shutil
187
+ shutil.rmtree(backup)
188
+ self.log(f"Old backup deleted: {backup.name}", "info")
189
+
190
+ except Exception as e:
191
+ self.log(f"Error cleaning up backups: {e}", "warning")
192
+
193
+ def validate_modules(self, modules: List[Dict]) -> List[str]:
194
+ """
195
+ Validate modules before deployment
196
+
197
+ Args:
198
+ modules: List of modules to validate
199
+
200
+ Returns:
201
+ List of errors found
202
+ """
203
+ errors = []
204
+ validations = self.config.get('validations', {})
205
+
206
+ if not validations:
207
+ return errors
208
+
209
+ for module in modules:
210
+ module_name = module['name']
211
+ module_path = Path(module['full_path'])
212
+
213
+ # Validate manifest
214
+ if validations.get('check_manifest', True):
215
+ manifest_path = module_path / "__manifest__.py"
216
+ if not manifest_path.exists():
217
+ errors.append(f"{module_name}: __manifest__.py not found")
218
+
219
+ # Validate Python syntax
220
+ if validations.get('check_python_syntax', True):
221
+ py_files = list(module_path.rglob("*.py"))
222
+ for py_file in py_files:
223
+ try:
224
+ with open(py_file, 'r', encoding='utf-8') as f:
225
+ compile(f.read(), str(py_file), 'exec')
226
+ except SyntaxError as e:
227
+ errors.append(f"{module_name}: Syntax error in {py_file.name}: {e}")
228
+
229
+ # Validate XML
230
+ if validations.get('check_xml_syntax', True):
231
+ xml_files = list(module_path.rglob("*.xml"))
232
+ for xml_file in xml_files:
233
+ try:
234
+ import xml.etree.ElementTree as ET
235
+ ET.parse(xml_file)
236
+ except ET.ParseError as e:
237
+ errors.append(f"{module_name}: Invalid XML in {xml_file.name}: {e}")
238
+
239
+ return errors
240
+
241
+ def execute(self, modules: List[Dict]) -> DeploymentResult:
242
+ """
243
+ Execute the complete deployment
244
+
245
+ This is the main method that orchestrates the entire process
246
+
247
+ Args:
248
+ modules: List of modules to deploy
249
+
250
+ Returns:
251
+ DeploymentResult with final result
252
+ """
253
+ self.console.print(f"\n🚀 Starting deployment to: {self.target_name}", style="bold blue")
254
+ self.console.print(f"📦 Modules to deploy: {len(modules)}\n")
255
+
256
+ # 1. Validate configuration
257
+ self.log("Validating configuration...", "info")
258
+ config_errors = self.validate_config()
259
+ if config_errors:
260
+ for error in config_errors:
261
+ self.log(f" ❌ {error}", "error")
262
+ return DeploymentResult(
263
+ success=False,
264
+ message="Invalid configuration",
265
+ details={'errors': config_errors}
266
+ )
267
+
268
+ # 2. Validate modules
269
+ self.log("Validating modules...", "info")
270
+ validation_errors = self.validate_modules(modules)
271
+ if validation_errors:
272
+ for error in validation_errors:
273
+ self.log(f" ❌ {error}", "error")
274
+ return DeploymentResult(
275
+ success=False,
276
+ message="Module validation failed",
277
+ details={'errors': validation_errors}
278
+ )
279
+
280
+ # 3. Create backup
281
+ self.log("Creating backup...", "info")
282
+ if not self.create_backup(modules):
283
+ return DeploymentResult(
284
+ success=False,
285
+ message="Backup failed"
286
+ )
287
+
288
+ # 4. Pre-deploy check
289
+ self.log("Checking connectivity...", "info")
290
+ if not self.pre_deploy_check():
291
+ return DeploymentResult(
292
+ success=False,
293
+ message="Pre-deploy check failed"
294
+ )
295
+
296
+ # 5. Deploy
297
+ self.log("Deploying modules...", "info")
298
+ deploy_result = self.deploy_modules(modules)
299
+ if not deploy_result.success:
300
+ self.log("Deployment failed, initiating rollback...", "error")
301
+ self.rollback()
302
+ return deploy_result
303
+
304
+ # 6. Post-deploy
305
+ self.log("Executing post-deploy actions...", "info")
306
+ post_result = self.post_deploy_actions()
307
+ if not post_result.success:
308
+ self.log("Post-deploy failed", "warning")
309
+ # Don't rollback here, modules are already deployed
310
+
311
+ self.console.print(f"\n✅ Deployment completed successfully!", style="bold green")
312
+ return DeploymentResult(
313
+ success=True,
314
+ message=f"Deployment to {self.target_name} completed",
315
+ details={
316
+ 'modules_deployed': len(modules),
317
+ 'logs': self.logs
318
+ }
319
+ )