odoo-dev 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ """Setup commands for initializing Odoo development environment."""
2
+
3
+ import subprocess
4
+ from pathlib import Path
5
+ from typing import Annotated
6
+
7
+ import typer
8
+
9
+ from odoo_dev.config import load_config
10
+ from odoo_dev.utils.console import error, success, warning
11
+
12
+
13
+ def setup(
14
+ community: bool = typer.Option(
15
+ False, "--community", help="Set up Community edition only (skip Enterprise repos)"
16
+ ),
17
+ ) -> None:
18
+ """Complete setup: clone Odoo repos, configure VSCode, build image."""
19
+ cfg = load_config()
20
+
21
+ success("Setting up complete Odoo development environment...")
22
+
23
+ # Initialize/update git submodules first
24
+ _init_submodules(cfg)
25
+
26
+ # Clone/update Odoo repositories
27
+ if community:
28
+ warning("Setting up Community Edition")
29
+ _clone_odoo_repos(cfg, community_only=community)
30
+
31
+ # Set up VSCode configuration
32
+ success("\nSetting up VSCode configuration...")
33
+ vscode(cfg)
34
+
35
+ # Set up local virtual environment
36
+ success("\nSetting up local Python virtual environment...")
37
+ warning(f"This will install system dependencies and Python {cfg.python_version} if needed")
38
+ setup_venv()
39
+
40
+ # Prompt for Docker setup
41
+ warning("\nDo you want to set up the Docker environment?")
42
+ if typer.confirm("Continue with Docker setup?", default=True):
43
+ success("\nBuilding Docker image...")
44
+ from odoo_dev.commands.docker import build
45
+ build(community=community)
46
+ else:
47
+ warning("Skipping Docker setup. Run 'odoo-dev build' later.")
48
+
49
+ success("\nSetup complete! You can now:")
50
+ success(" - Start Odoo with Docker: odoo-dev start")
51
+ success(" - Use local Python environment: source .venv/bin/activate")
52
+ success(" - Debug with VSCode: Use the configured launch profiles")
53
+
54
+
55
+ def setup_venv() -> None:
56
+ """Set up local Python virtual environment for development."""
57
+ cfg = load_config()
58
+
59
+ # Create config file first
60
+ _setup_odoo_config(cfg)
61
+
62
+ success(f"Setting up Python virtual environment for Odoo {cfg.odoo_version}...")
63
+
64
+ # Install system dependencies
65
+ _install_system_dependencies()
66
+
67
+ # Ensure uv is available
68
+ if subprocess.run(["which", "uv"], capture_output=True).returncode != 0:
69
+ success("Installing uv package manager...")
70
+ subprocess.run(
71
+ ["curl", "-LsSf", "https://astral.sh/uv/install.sh"],
72
+ stdout=subprocess.PIPE,
73
+ )
74
+ # Would pipe to sh, but let's be explicit
75
+ warning("Please install uv manually: curl -LsSf https://astral.sh/uv/install.sh | sh")
76
+
77
+ # Create virtual environment
78
+ if not cfg.venv_path.exists():
79
+ success("Creating new Python virtual environment...")
80
+ subprocess.run(
81
+ ["uv", "venv", str(cfg.venv_path), "--python", cfg.python_version],
82
+ check=True,
83
+ )
84
+ else:
85
+ warning("Virtual environment already exists. Skipping creation.")
86
+
87
+ # Update PYTHONPATH in activate script
88
+ _update_python_path(cfg)
89
+
90
+ # Install requirements
91
+ venv_pip = ["uv", "pip", "install", "--python", str(cfg.venv_path / "bin" / "python")]
92
+
93
+ # Install Odoo requirements
94
+ odoo_requirements = cfg.project_dir / "odoo" / "requirements.txt"
95
+ if odoo_requirements.exists():
96
+ success("Installing Odoo requirements...")
97
+ # Create temp requirements with psycopg2-binary substitution
98
+ temp_req = cfg.project_dir / "temp_requirements.txt"
99
+ content = odoo_requirements.read_text()
100
+ content = content.replace("psycopg2==", "psycopg2-binary>=")
101
+ temp_req.write_text(content)
102
+ subprocess.run([*venv_pip, "-r", str(temp_req)])
103
+ temp_req.unlink()
104
+
105
+ # Install project requirements
106
+ project_requirements = cfg.project_dir / "requirements.txt"
107
+ if project_requirements.exists():
108
+ success("Installing project requirements...")
109
+ subprocess.run([*venv_pip, "-r", str(project_requirements)])
110
+
111
+ # Install dev tools
112
+ success("Installing development tools...")
113
+ subprocess.run([*venv_pip, "pytest", "pytest-odoo", "debugpy", "manifestoo", "coverage"])
114
+
115
+ success("\nVirtual environment setup complete!")
116
+ success(f"To activate: source {cfg.venv_path}/bin/activate")
117
+
118
+
119
+ def vscode(cfg=None) -> None:
120
+ """Set up VSCode configuration for debugging."""
121
+ if cfg is None:
122
+ cfg = load_config()
123
+
124
+ vscode_dir = cfg.project_dir / ".vscode"
125
+ vscode_dir.mkdir(exist_ok=True)
126
+
127
+ template_dir = cfg.script_dir / "templates" / "vscode"
128
+
129
+ # Copy template files
130
+ import shutil
131
+
132
+ for template_file in ["launch.json", "tasks.json"]:
133
+ src = template_dir / template_file
134
+ dst = vscode_dir / template_file
135
+ if src.exists():
136
+ shutil.copy(src, dst)
137
+ success(f"Copied {template_file}")
138
+
139
+ # Only copy settings.json if it doesn't exist
140
+ settings_src = template_dir / "settings.json"
141
+ settings_dst = vscode_dir / "settings.json"
142
+ if settings_src.exists() and not settings_dst.exists():
143
+ shutil.copy(settings_src, settings_dst)
144
+ success("Copied settings.json")
145
+ elif settings_dst.exists():
146
+ warning("settings.json already exists. Skipping.")
147
+
148
+ success("VSCode configuration set up successfully!")
149
+
150
+
151
+ def _init_submodules(cfg) -> None:
152
+ """Initialize and update git submodules if present."""
153
+ gitmodules = cfg.project_dir / ".gitmodules"
154
+
155
+ if not gitmodules.exists():
156
+ return
157
+
158
+ success("Initializing git submodules...")
159
+
160
+ result = subprocess.run(
161
+ ["git", "submodule", "update", "--init", "--recursive"],
162
+ cwd=cfg.project_dir,
163
+ capture_output=True,
164
+ text=True,
165
+ )
166
+
167
+ if result.returncode == 0:
168
+ success("Git submodules initialized.")
169
+ else:
170
+ warning(f"Submodule initialization had issues: {result.stderr}")
171
+
172
+
173
+ def _setup_odoo_config(cfg, community_only: bool = False) -> None:
174
+ """Create odoo.conf configuration file."""
175
+ conf_dir = cfg.project_dir / "conf"
176
+ conf_file = conf_dir / "odoo.conf"
177
+
178
+ # Create conf directory
179
+ conf_dir.mkdir(exist_ok=True)
180
+
181
+ if conf_file.exists():
182
+ warning(f"Config file already exists at {conf_file}")
183
+ return
184
+
185
+ success("Creating Odoo configuration file...")
186
+
187
+ # Build addons path
188
+ addons_paths = [
189
+ str(cfg.project_dir / "odoo" / "addons"),
190
+ str(cfg.project_dir / "odoo" / "odoo" / "addons"),
191
+ ]
192
+
193
+ if not community_only:
194
+ addons_paths.append(str(cfg.project_dir / "enterprise"))
195
+
196
+ addons_paths.extend([
197
+ str(cfg.project_dir / "design-themes"),
198
+ str(cfg.project_dir / "addons"),
199
+ ])
200
+
201
+ # Filter to only existing paths
202
+ addons_paths = [p for p in addons_paths if Path(p).exists()]
203
+
204
+ config_content = f"""[options]
205
+ addons_path = {",".join(addons_paths)}
206
+ admin_passwd = admin
207
+ db_user = odoo
208
+ db_password = odoo
209
+ """
210
+
211
+ conf_file.write_text(config_content)
212
+ conf_file.chmod(0o600)
213
+ success(f"Config file created at {conf_file}")
214
+
215
+
216
+ def _clone_odoo_repos(cfg, community_only: bool = False) -> None:
217
+ """Clone or update Odoo repositories."""
218
+ success("Setting up Odoo repositories...")
219
+
220
+ if community_only:
221
+ warning("Community edition mode: Enterprise repositories will be skipped")
222
+
223
+ repos = [
224
+ (f"git@github.com:odoo/odoo.git", "odoo", cfg.odoo_version),
225
+ (f"git@github.com:odoo/design-themes.git", "design-themes", cfg.odoo_version),
226
+ ]
227
+
228
+ # Add enterprise if not community only
229
+ if not community_only:
230
+ repos.append(
231
+ (f"git@github.com:odoo/enterprise.git", "enterprise", cfg.odoo_version)
232
+ )
233
+
234
+ # Add industry for Odoo 18+
235
+ if cfg.odoo_version.startswith("18") or cfg.odoo_version.startswith("19"):
236
+ repos.append(
237
+ (f"git@github.com:odoo/industry.git", "industry", cfg.odoo_version)
238
+ )
239
+
240
+ for repo_url, repo_dir, branch in repos:
241
+ repo_path = cfg.project_dir / repo_dir
242
+
243
+ if repo_path.exists():
244
+ warning(f"Updating {repo_dir} repository...")
245
+ subprocess.run(
246
+ ["git", "fetch", "--depth", "1", "origin", branch],
247
+ cwd=repo_path,
248
+ )
249
+ subprocess.run(["git", "checkout", branch], cwd=repo_path)
250
+ subprocess.run(
251
+ ["git", "pull", "--ff-only", "origin", branch],
252
+ cwd=repo_path,
253
+ )
254
+ else:
255
+ warning(f"Cloning {repo_dir} repository...")
256
+ subprocess.run(
257
+ [
258
+ "git",
259
+ "clone",
260
+ "--depth",
261
+ "1",
262
+ "--branch",
263
+ branch,
264
+ repo_url,
265
+ str(repo_path),
266
+ ],
267
+ cwd=cfg.project_dir,
268
+ )
269
+
270
+ success("Odoo repositories setup complete.")
271
+
272
+
273
+ def _install_system_dependencies() -> None:
274
+ """Install system dependencies based on OS."""
275
+ import platform
276
+
277
+ system = platform.system()
278
+
279
+ if system == "Darwin":
280
+ success("Installing dependencies for macOS...")
281
+ subprocess.run(
282
+ [
283
+ "brew",
284
+ "install",
285
+ "postgresql",
286
+ "libpq",
287
+ "openssl",
288
+ "libxml2",
289
+ "libxslt",
290
+ ],
291
+ check=False,
292
+ )
293
+ elif system == "Linux":
294
+ success("Installing dependencies for Linux...")
295
+ subprocess.run(
296
+ [
297
+ "sudo",
298
+ "apt-get",
299
+ "update",
300
+ ],
301
+ check=False,
302
+ )
303
+ subprocess.run(
304
+ [
305
+ "sudo",
306
+ "apt-get",
307
+ "install",
308
+ "-y",
309
+ "--no-install-recommends",
310
+ "build-essential",
311
+ "libldap2-dev",
312
+ "libpq-dev",
313
+ "libsasl2-dev",
314
+ "libssl-dev",
315
+ "libxml2-dev",
316
+ "libxslt1-dev",
317
+ "postgresql-client",
318
+ ],
319
+ check=False,
320
+ )
321
+ else:
322
+ warning(f"Unsupported OS: {system}. Please install dependencies manually.")
323
+
324
+
325
+ def _update_python_path(cfg) -> None:
326
+ """Update PYTHONPATH in venv activate script."""
327
+ activate_script = cfg.venv_path / "bin" / "activate"
328
+
329
+ if not activate_script.exists():
330
+ return
331
+
332
+ content = activate_script.read_text()
333
+
334
+ # Check if already modified
335
+ if "# Odoo PYTHONPATH setup" in content:
336
+ return
337
+
338
+ pythonpath_setup = '''
339
+ # Odoo PYTHONPATH setup
340
+ if [ -z "$_OLD_VIRTUAL_PYTHONPATH" ]; then
341
+ _OLD_VIRTUAL_PYTHONPATH="$PYTHONPATH"
342
+ fi
343
+
344
+ ODOO_PYTHONPATH=""
345
+ for dir in "odoo" "enterprise" "design-themes" "industry" "addons"; do
346
+ if [ -d "$VIRTUAL_ENV/../$dir" ]; then
347
+ if [ -z "$ODOO_PYTHONPATH" ]; then
348
+ ODOO_PYTHONPATH="$VIRTUAL_ENV/../$dir"
349
+ else
350
+ ODOO_PYTHONPATH="$ODOO_PYTHONPATH:$VIRTUAL_ENV/../$dir"
351
+ fi
352
+ fi
353
+ done
354
+
355
+ if [ -n "$ODOO_PYTHONPATH" ]; then
356
+ PYTHONPATH="$ODOO_PYTHONPATH${_OLD_VIRTUAL_PYTHONPATH:+:$_OLD_VIRTUAL_PYTHONPATH}"
357
+ export PYTHONPATH
358
+ fi
359
+ # End Odoo PYTHONPATH setup
360
+ '''
361
+
362
+ content += pythonpath_setup
363
+ activate_script.write_text(content)
364
+ success("PYTHONPATH updated in virtual environment activation script.")
odoo_dev/config.py ADDED
@@ -0,0 +1,101 @@
1
+ """Configuration loading and project detection."""
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+
8
+ @dataclass
9
+ class ProjectConfig:
10
+ """Configuration for an Odoo development project."""
11
+
12
+ project_dir: Path
13
+ script_dir: Path
14
+ odoo_version: str
15
+ python_version: str
16
+ project_name: str
17
+
18
+ @property
19
+ def venv_path(self) -> Path:
20
+ """Path to the Python virtual environment."""
21
+ return self.project_dir / ".venv"
22
+
23
+ @property
24
+ def config_file(self) -> Path:
25
+ """Path to the local odoo.conf file."""
26
+ return self.project_dir / "conf" / "odoo.conf"
27
+
28
+ @property
29
+ def docker_config_file(self) -> Path:
30
+ """Path to the Docker odoo.conf file."""
31
+ return self.script_dir / "odoo.conf"
32
+
33
+ @property
34
+ def odoo_bin(self) -> Path:
35
+ """Path to the odoo-bin executable."""
36
+ return self.project_dir / "odoo" / "odoo-bin"
37
+
38
+ @property
39
+ def addons_dir(self) -> Path:
40
+ """Path to the project's custom addons directory."""
41
+ return self.project_dir / "addons"
42
+
43
+
44
+ def find_project_root(start: Path | None = None) -> Path:
45
+ """Walk up directory tree to find git repository root.
46
+
47
+ Args:
48
+ start: Starting directory. Defaults to current working directory.
49
+
50
+ Returns:
51
+ Path to the git repository root, or current directory if not found.
52
+ """
53
+ current = start or Path.cwd()
54
+ while current != current.parent:
55
+ git_path = current / ".git"
56
+ # .git can be a directory (normal repo) or a file (submodule/worktree)
57
+ if git_path.is_dir() or git_path.is_file():
58
+ return current
59
+ current = current.parent
60
+ return Path.cwd()
61
+
62
+
63
+ def load_dotenv(path: Path) -> None:
64
+ """Load environment variables from a .env file.
65
+
66
+ Args:
67
+ path: Path to the .env file.
68
+ """
69
+ if not path.exists():
70
+ return
71
+
72
+ for line in path.read_text().splitlines():
73
+ line = line.strip()
74
+ if line and not line.startswith("#") and "=" in line:
75
+ key, _, value = line.partition("=")
76
+ # Don't override existing environment variables
77
+ os.environ.setdefault(key.strip(), value.strip())
78
+
79
+
80
+ def load_config(project_dir: Path | None = None) -> ProjectConfig:
81
+ """Load configuration from environment and .env file.
82
+
83
+ Args:
84
+ project_dir: Optional project directory override.
85
+
86
+ Returns:
87
+ ProjectConfig with all settings loaded.
88
+ """
89
+ if project_dir is None:
90
+ project_dir = find_project_root()
91
+
92
+ # Load .env if it exists
93
+ load_dotenv(project_dir / ".env")
94
+
95
+ return ProjectConfig(
96
+ project_dir=project_dir,
97
+ script_dir=project_dir / ".odoo-deploy",
98
+ odoo_version=os.getenv("ODOO_VERSION", "18.0"),
99
+ python_version=os.getenv("PYTHON_VERSION", "3.12"),
100
+ project_name=project_dir.name,
101
+ )
@@ -0,0 +1 @@
1
+ """Utility modules for odoo-dev."""
@@ -0,0 +1,25 @@
1
+ """Console output helpers with colored formatting."""
2
+
3
+ from rich.console import Console
4
+
5
+ console = Console()
6
+
7
+
8
+ def success(msg: str) -> None:
9
+ """Print a success message in green."""
10
+ console.print(f"[green]{msg}[/green]")
11
+
12
+
13
+ def warning(msg: str) -> None:
14
+ """Print a warning message in yellow."""
15
+ console.print(f"[yellow]{msg}[/yellow]")
16
+
17
+
18
+ def error(msg: str) -> None:
19
+ """Print an error message in red."""
20
+ console.print(f"[red]{msg}[/red]")
21
+
22
+
23
+ def info(msg: str) -> None:
24
+ """Print an info message."""
25
+ console.print(msg)
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: odoo-dev
3
+ Version: 0.1.0
4
+ Summary: Odoo Development Environment Helper
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: rich>=13.0.0
7
+ Requires-Dist: typer>=0.9.0
8
+ Description-Content-Type: text/markdown
9
+
10
+ # odoo-dev
11
+
12
+ A CLI tool for managing Odoo development environments. Handles local Python setup, Docker containers, database operations, and more.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Install with uv (recommended)
18
+ uv tool install odoo-dev
19
+
20
+ # Or with pip
21
+ pip install odoo-dev
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ # In your Odoo project directory
28
+ cd my-odoo-project
29
+
30
+ # Full setup: clone Odoo repos, create venv, configure VSCode
31
+ odoo-dev setup
32
+
33
+ # Or for community edition only
34
+ odoo-dev setup --community
35
+ ```
36
+
37
+ ## Commands
38
+
39
+ ### Local Development (default)
40
+
41
+ ```bash
42
+ odoo-dev run # Start Odoo locally
43
+ odoo-dev run -d mydb --dev reload # With hot reload
44
+ odoo-dev run --debug # With debugpy (VSCode attach)
45
+ odoo-dev shell mydb # Open Odoo shell
46
+ odoo-dev update base -d mydb # Update modules
47
+ odoo-dev test my_module # Run tests with coverage
48
+ odoo-dev scaffold my_module # Create new module
49
+ ```
50
+
51
+ ### Database Operations
52
+
53
+ ```bash
54
+ odoo-dev db list # List databases
55
+ odoo-dev db restore backup.zip # Restore from backup
56
+ odoo-dev db restore backup.zip mydb --no-neutralize
57
+ odoo-dev db drop mydb # Drop database
58
+ odoo-dev db neutralize mydb # Disable emails/crons
59
+ ```
60
+
61
+ ### Docker (optional)
62
+
63
+ ```bash
64
+ odoo-dev docker start # Start containers
65
+ odoo-dev docker stop # Stop containers
66
+ odoo-dev docker logs # View logs
67
+ odoo-dev docker build # Rebuild image
68
+ odoo-dev docker shell mydb # Shell in container
69
+ odoo-dev docker psql # PostgreSQL shell
70
+ ```
71
+
72
+ ### Setup Commands
73
+
74
+ ```bash
75
+ odoo-dev setup # Full setup
76
+ odoo-dev setup --community # Community edition only
77
+ odoo-dev setup-venv # Just create venv
78
+ odoo-dev vscode # Configure VSCode debugging
79
+ ```
80
+
81
+ ## Project Structure
82
+
83
+ odoo-dev expects this project structure:
84
+
85
+ ```
86
+ my-odoo-project/
87
+ ├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
88
+ ├── addons/ # Your custom addons
89
+ ├── requirements.txt # Project-specific Python deps
90
+ ├── odoo/ # Cloned by setup
91
+ ├── enterprise/ # Cloned by setup (unless --community)
92
+ ├── design-themes/ # Cloned by setup
93
+ ├── .venv/ # Created by setup
94
+ └── conf/
95
+ └── odoo.conf # Created by setup
96
+ ```
97
+
98
+ ## Configuration
99
+
100
+ Create a `.env` file in your project root:
101
+
102
+ ```bash
103
+ ODOO_VERSION=18.0
104
+ PYTHON_VERSION=3.12
105
+ ```
106
+
107
+ ## Requirements
108
+
109
+ - Python 3.12+
110
+ - uv (recommended) or pip
111
+ - Git
112
+ - PostgreSQL (for local development)
113
+ - Docker (optional, for containerized development)
114
+
115
+ ## Development
116
+
117
+ ```bash
118
+ # Clone and install for development
119
+ git clone git@git.bemade.org:bemade/odoo-dev.git
120
+ cd odoo-dev
121
+ uv sync
122
+
123
+ # Run tests
124
+ uv run pytest # All tests
125
+ uv run pytest -m "not slow" # Fast tests only
126
+
127
+ # Build
128
+ uv build
129
+ ```
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,14 @@
1
+ odoo_dev/__init__.py,sha256=k91qzqyJaW_n2LwmsJoDER8z90x2wcVNODzKACLUxKc,66
2
+ odoo_dev/cli.py,sha256=SqY07yLgOGEUkrUvbBM9Sfon87nUUbM_wDk96n_pScg,714
3
+ odoo_dev/config.py,sha256=LbWsJbcvIfE5fbxkZK_iItaYDhiB0t8mF6wQk2d8hpM,2865
4
+ odoo_dev/commands/__init__.py,sha256=yOxitS1Oes2ZS95F0nXyw3LDOBXCFWmudZInYbTu1eQ,33
5
+ odoo_dev/commands/db.py,sha256=7DnI28y2YpugnjoMkyTYymLMZ5MjNagfD6T15RWvZ0c,9605
6
+ odoo_dev/commands/docker.py,sha256=I5dvxRX4uHzwBwTM7d7yhMphge8fdKoqjXCKo_-7FQw,5386
7
+ odoo_dev/commands/run.py,sha256=FPi_rIyMOWadZfMPin0TXHbdBcW1YIuqUseBEhr4PnE,11882
8
+ odoo_dev/commands/setup.py,sha256=M67rE1qWA2iYA3S15cZAt_-Za2iXWau1QAbt-nvOCQU,11260
9
+ odoo_dev/utils/__init__.py,sha256=EDPdElYZN3cPJtZIkNjXFoItz1mPBPUsasQzb6mRrss,36
10
+ odoo_dev/utils/console.py,sha256=bRbXVD15QiMhdU8Rp4cyaoA1-J8vFX8r16TwsYurUug,549
11
+ odoo_dev-0.1.0.dist-info/METADATA,sha256=stKenzdej2YmQ_ubYOG2vsHzZdAGT6zhQu9re0gHkQE,3155
12
+ odoo_dev-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
+ odoo_dev-0.1.0.dist-info/entry_points.txt,sha256=EKJCgGArQtoRNYRBRcbkRfIXO_APFkYujr2u8-UCfXo,46
14
+ odoo_dev-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ odoo-dev = odoo_dev.cli:app