odoo-dev 0.3.1__tar.gz → 0.3.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 (63) hide show
  1. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/.claude/settings.local.json +2 -1
  2. odoo_dev-0.3.3/PKG-INFO +183 -0
  3. odoo_dev-0.3.3/README.md +169 -0
  4. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/pyproject.toml +1 -1
  5. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/__init__.py +1 -1
  6. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/commands/run.py +42 -16
  7. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/commands/setup.py +17 -82
  8. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/.gitignore +1 -0
  9. odoo_dev-0.3.3/tests/test_drop_database.py +165 -0
  10. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_setup.py +50 -0
  11. odoo_dev-0.3.1/PKG-INFO +0 -19
  12. odoo_dev-0.3.1/README.md +0 -5
  13. odoo_dev-0.3.1/tests/fixtures/odoo-empty/.git +0 -1
  14. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/.claude/settings.json +0 -0
  15. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/.gitignore +0 -0
  16. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/.gitmodules +0 -0
  17. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/.python-version +0 -0
  18. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/cli.py +0 -0
  19. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/commands/__init__.py +0 -0
  20. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/commands/db.py +0 -0
  21. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/commands/docker.py +0 -0
  22. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/config.py +0 -0
  23. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/__init__.py +0 -0
  24. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/docker/Dockerfile +0 -0
  25. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/docker/__init__.py +0 -0
  26. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/docker/docker-entrypoint.sh +0 -0
  27. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/vscode/__init__.py +0 -0
  28. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/vscode/launch.json +0 -0
  29. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/vscode/settings.json +0 -0
  30. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/templates/vscode/tasks.json +0 -0
  31. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/utils/__init__.py +0 -0
  32. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/src/odoo_dev/utils/console.py +0 -0
  33. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/__init__.py +0 -0
  34. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/__init__.py +0 -0
  35. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/.gitlab-ci.yml +0 -0
  36. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/.gitmodules +0 -0
  37. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/.repos/.gitkeep +0 -0
  38. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/Notes/Warning Upgrade all +0 -0
  39. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/README.md +0 -0
  40. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/addons/.gitkeep +0 -0
  41. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/note.txt +0 -0
  42. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/requirements.txt +0 -0
  43. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/abstorelsymlink.sh +0 -0
  44. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/adaptation.md +0 -0
  45. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/after_migration.sh +0 -0
  46. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/checkout2 +0 -0
  47. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/cleanup.sql +0 -0
  48. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/deploy +0 -0
  49. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/fix_mail_templates.sql +0 -0
  50. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/gitpull +0 -0
  51. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/main.py +0 -0
  52. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/modules_cleanup.sql +0 -0
  53. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/post-upgrade steps.md +0 -0
  54. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/pre_commit_hook.py +0 -0
  55. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/requirements.txt +0 -0
  56. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/test +0 -0
  57. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/fixtures/odoo-empty/tools/update_addon_versions.py +0 -0
  58. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_cli.py +0 -0
  59. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_config.py +0 -0
  60. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_db.py +0 -0
  61. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_integration.py +0 -0
  62. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/tests/test_run.py +0 -0
  63. {odoo_dev-0.3.1 → odoo_dev-0.3.3}/uv.lock +0 -0
@@ -6,7 +6,8 @@
6
6
  "Bash(ls:*)",
7
7
  "Bash(xargs:*)",
8
8
  "Bash(git commit:*)",
9
- "Bash(git push:*)"
9
+ "Bash(git push:*)",
10
+ "Bash(python -m pytest tests/test_drop_database.py -v -m slow)"
10
11
  ]
11
12
  }
12
13
  }
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: odoo-dev
3
+ Version: 0.3.3
4
+ Summary: Odoo Development Environment Helper
5
+ Project-URL: Homepage, https://git.bemade.org/bemade/odoo-dev
6
+ Project-URL: Repository, https://git.bemade.org/bemade/odoo-dev
7
+ Project-URL: Issues, https://git.bemade.org/bemade/odoo-dev/-/issues
8
+ License-Expression: LGPL-3.0
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: pyyaml>=6.0
11
+ Requires-Dist: rich>=13.0.0
12
+ Requires-Dist: typer>=0.9.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # odoo-dev
16
+
17
+ A CLI tool for managing Odoo development environments. Handles local Python setup, Docker containers, database operations, and more.
18
+
19
+ It is the successor to the older `odoo-deploy` shell scripts — if you have notes for `odoo-deploy`, the rough mapping is: `odoo-dev docker start/stop/build` replaces the old `odoo-dev.sh start/stop/build`, and `odoo-dev run`/`test`/`shell` give you a local (venv-based) workflow that `odoo-deploy` didn't.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ # Install with uv (recommended) — installs the published package from PyPI
25
+ uv tool install odoo-dev
26
+
27
+ # Or with pip
28
+ pip install odoo-dev
29
+ ```
30
+
31
+ Then make sure the install location is on your `PATH` (uv prints the path; usually `~/.local/bin`):
32
+
33
+ ```bash
34
+ uv tool update-shell # or: export PATH="$HOME/.local/bin:$PATH"
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ # In your Odoo project directory
41
+ cd my-odoo-project
42
+
43
+ # Full setup: clone Odoo repos, create venv, install deps, configure VSCode
44
+ odoo-dev setup
45
+
46
+ # Or for community edition only
47
+ odoo-dev setup --community
48
+ ```
49
+
50
+ `setup` reads `ODOO_VERSION` / `PYTHON_VERSION` from a `.env` in the project root (or
51
+ prompts for them and offers to save). It then clones the Odoo repos, builds a `.venv`,
52
+ installs system dependencies, and generates `conf/odoo.conf`. It will offer to set up
53
+ Docker at the end — answer "no" if you only want the local venv workflow.
54
+
55
+ ## Database setup (read this before your first `run`/`test`)
56
+
57
+ The generated `conf/odoo.conf` connects as PostgreSQL user **`odoo`** over the local
58
+ socket. You need a running PostgreSQL server and a matching role. `setup` installs
59
+ PostgreSQL but does **not** start it or create the role, so on a fresh machine do this
60
+ once:
61
+
62
+ **macOS (Homebrew):**
63
+
64
+ ```bash
65
+ brew services start postgresql@18 # start the server (use your installed version)
66
+ createuser -s odoo # create the role odoo.conf expects
67
+ # Homebrew's versioned postgres is keg-only; add its bin to PATH if psql/createuser aren't found:
68
+ # export PATH="$(brew --prefix postgresql@18)/bin:$PATH"
69
+ ```
70
+
71
+ **Debian/Ubuntu:**
72
+
73
+ ```bash
74
+ sudo systemctl start postgresql
75
+ sudo -u postgres createuser -s odoo
76
+ ```
77
+
78
+ You can change the DB user/password in `conf/odoo.conf` if you prefer a different role.
79
+
80
+ ## Commands
81
+
82
+ ### Local Development (default)
83
+
84
+ ```bash
85
+ odoo-dev run # Start Odoo locally (default port 8069)
86
+ odoo-dev run -d mydb -p 8070 # Pick a database and HTTP port
87
+ odoo-dev run -d mydb -i base # Initialize module(s) on start
88
+ odoo-dev run -d mydb --dev reload # With hot reload
89
+ odoo-dev run --debug # With debugpy (VSCode attach on 5678)
90
+ odoo-dev shell mydb # Open an Odoo shell
91
+ odoo-dev update base -d mydb # Update modules
92
+ odoo-dev test my_module # Run a module's tests (coverage on by default)
93
+ odoo-dev test my_module --test-tags my_module --no-coverage
94
+ odoo-dev test # Auto-discover & test all addons in addons/
95
+ odoo-dev scaffold my_module # Create a new module
96
+ ```
97
+
98
+ > Note: the HTTP port flag is `-p` / `--port` (not `--http-port`).
99
+
100
+ ### Database Operations
101
+
102
+ ```bash
103
+ odoo-dev db list # List databases
104
+ odoo-dev db restore backup.zip # Restore from backup (neutralized by default)
105
+ odoo-dev db restore backup.zip mydb --no-neutralize
106
+ odoo-dev db drop mydb # Drop database
107
+ odoo-dev db neutralize mydb # Disable emails/crons
108
+ ```
109
+
110
+ ### Docker (optional)
111
+
112
+ ```bash
113
+ odoo-dev docker start # Start containers
114
+ odoo-dev docker stop # Stop containers
115
+ odoo-dev docker restart # Restart containers
116
+ odoo-dev docker logs # View logs
117
+ odoo-dev docker build # Rebuild image
118
+ odoo-dev docker shell mydb # Shell in container
119
+ odoo-dev docker psql # PostgreSQL shell
120
+ ```
121
+
122
+ ### Setup Commands
123
+
124
+ ```bash
125
+ odoo-dev setup # Full setup
126
+ odoo-dev setup --community # Community edition only
127
+ odoo-dev setup-venv # Just create the venv (no repo clone)
128
+ odoo-dev vscode # Configure VSCode debugging
129
+ ```
130
+
131
+ ## Project Structure
132
+
133
+ odoo-dev expects this project structure:
134
+
135
+ ```
136
+ my-odoo-project/
137
+ ├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
138
+ ├── addons/ # Your custom addons
139
+ ├── requirements.txt # Project-specific Python deps
140
+ ├── odoo/ # Cloned by setup
141
+ ├── enterprise/ # Cloned by setup (unless --community)
142
+ ├── design-themes/ # Cloned by setup
143
+ ├── .venv/ # Created by setup
144
+ └── conf/
145
+ └── odoo.conf # Created by setup
146
+ ```
147
+
148
+ ## Configuration
149
+
150
+ Create a `.env` file in your project root:
151
+
152
+ ```bash
153
+ ODOO_VERSION=19.0
154
+ PYTHON_VERSION=3.12
155
+ ```
156
+
157
+ ## Requirements
158
+
159
+ - Python 3.12+
160
+ - uv (recommended) or pip
161
+ - Git
162
+ - PostgreSQL (for local development — server + an `odoo` role; see "Database setup")
163
+ - Docker (optional, for containerized development)
164
+
165
+ ## Development
166
+
167
+ ```bash
168
+ # Clone and install for development
169
+ git clone git@github.com:bemade/odoo-dev.git
170
+ cd odoo-dev
171
+ uv sync
172
+
173
+ # Run tests
174
+ uv run pytest # All tests
175
+ uv run pytest -m "not slow" # Fast tests only
176
+
177
+ # Build
178
+ uv build
179
+ ```
180
+
181
+ ## License
182
+
183
+ LGPL-3. For complete license terms, visit https://www.gnu.org/licenses/lgpl-3.0.en.html
@@ -0,0 +1,169 @@
1
+ # odoo-dev
2
+
3
+ A CLI tool for managing Odoo development environments. Handles local Python setup, Docker containers, database operations, and more.
4
+
5
+ It is the successor to the older `odoo-deploy` shell scripts — if you have notes for `odoo-deploy`, the rough mapping is: `odoo-dev docker start/stop/build` replaces the old `odoo-dev.sh start/stop/build`, and `odoo-dev run`/`test`/`shell` give you a local (venv-based) workflow that `odoo-deploy` didn't.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Install with uv (recommended) — installs the published package from PyPI
11
+ uv tool install odoo-dev
12
+
13
+ # Or with pip
14
+ pip install odoo-dev
15
+ ```
16
+
17
+ Then make sure the install location is on your `PATH` (uv prints the path; usually `~/.local/bin`):
18
+
19
+ ```bash
20
+ uv tool update-shell # or: export PATH="$HOME/.local/bin:$PATH"
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # In your Odoo project directory
27
+ cd my-odoo-project
28
+
29
+ # Full setup: clone Odoo repos, create venv, install deps, configure VSCode
30
+ odoo-dev setup
31
+
32
+ # Or for community edition only
33
+ odoo-dev setup --community
34
+ ```
35
+
36
+ `setup` reads `ODOO_VERSION` / `PYTHON_VERSION` from a `.env` in the project root (or
37
+ prompts for them and offers to save). It then clones the Odoo repos, builds a `.venv`,
38
+ installs system dependencies, and generates `conf/odoo.conf`. It will offer to set up
39
+ Docker at the end — answer "no" if you only want the local venv workflow.
40
+
41
+ ## Database setup (read this before your first `run`/`test`)
42
+
43
+ The generated `conf/odoo.conf` connects as PostgreSQL user **`odoo`** over the local
44
+ socket. You need a running PostgreSQL server and a matching role. `setup` installs
45
+ PostgreSQL but does **not** start it or create the role, so on a fresh machine do this
46
+ once:
47
+
48
+ **macOS (Homebrew):**
49
+
50
+ ```bash
51
+ brew services start postgresql@18 # start the server (use your installed version)
52
+ createuser -s odoo # create the role odoo.conf expects
53
+ # Homebrew's versioned postgres is keg-only; add its bin to PATH if psql/createuser aren't found:
54
+ # export PATH="$(brew --prefix postgresql@18)/bin:$PATH"
55
+ ```
56
+
57
+ **Debian/Ubuntu:**
58
+
59
+ ```bash
60
+ sudo systemctl start postgresql
61
+ sudo -u postgres createuser -s odoo
62
+ ```
63
+
64
+ You can change the DB user/password in `conf/odoo.conf` if you prefer a different role.
65
+
66
+ ## Commands
67
+
68
+ ### Local Development (default)
69
+
70
+ ```bash
71
+ odoo-dev run # Start Odoo locally (default port 8069)
72
+ odoo-dev run -d mydb -p 8070 # Pick a database and HTTP port
73
+ odoo-dev run -d mydb -i base # Initialize module(s) on start
74
+ odoo-dev run -d mydb --dev reload # With hot reload
75
+ odoo-dev run --debug # With debugpy (VSCode attach on 5678)
76
+ odoo-dev shell mydb # Open an Odoo shell
77
+ odoo-dev update base -d mydb # Update modules
78
+ odoo-dev test my_module # Run a module's tests (coverage on by default)
79
+ odoo-dev test my_module --test-tags my_module --no-coverage
80
+ odoo-dev test # Auto-discover & test all addons in addons/
81
+ odoo-dev scaffold my_module # Create a new module
82
+ ```
83
+
84
+ > Note: the HTTP port flag is `-p` / `--port` (not `--http-port`).
85
+
86
+ ### Database Operations
87
+
88
+ ```bash
89
+ odoo-dev db list # List databases
90
+ odoo-dev db restore backup.zip # Restore from backup (neutralized by default)
91
+ odoo-dev db restore backup.zip mydb --no-neutralize
92
+ odoo-dev db drop mydb # Drop database
93
+ odoo-dev db neutralize mydb # Disable emails/crons
94
+ ```
95
+
96
+ ### Docker (optional)
97
+
98
+ ```bash
99
+ odoo-dev docker start # Start containers
100
+ odoo-dev docker stop # Stop containers
101
+ odoo-dev docker restart # Restart containers
102
+ odoo-dev docker logs # View logs
103
+ odoo-dev docker build # Rebuild image
104
+ odoo-dev docker shell mydb # Shell in container
105
+ odoo-dev docker psql # PostgreSQL shell
106
+ ```
107
+
108
+ ### Setup Commands
109
+
110
+ ```bash
111
+ odoo-dev setup # Full setup
112
+ odoo-dev setup --community # Community edition only
113
+ odoo-dev setup-venv # Just create the venv (no repo clone)
114
+ odoo-dev vscode # Configure VSCode debugging
115
+ ```
116
+
117
+ ## Project Structure
118
+
119
+ odoo-dev expects this project structure:
120
+
121
+ ```
122
+ my-odoo-project/
123
+ ├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
124
+ ├── addons/ # Your custom addons
125
+ ├── requirements.txt # Project-specific Python deps
126
+ ├── odoo/ # Cloned by setup
127
+ ├── enterprise/ # Cloned by setup (unless --community)
128
+ ├── design-themes/ # Cloned by setup
129
+ ├── .venv/ # Created by setup
130
+ └── conf/
131
+ └── odoo.conf # Created by setup
132
+ ```
133
+
134
+ ## Configuration
135
+
136
+ Create a `.env` file in your project root:
137
+
138
+ ```bash
139
+ ODOO_VERSION=19.0
140
+ PYTHON_VERSION=3.12
141
+ ```
142
+
143
+ ## Requirements
144
+
145
+ - Python 3.12+
146
+ - uv (recommended) or pip
147
+ - Git
148
+ - PostgreSQL (for local development — server + an `odoo` role; see "Database setup")
149
+ - Docker (optional, for containerized development)
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ # Clone and install for development
155
+ git clone git@github.com:bemade/odoo-dev.git
156
+ cd odoo-dev
157
+ uv sync
158
+
159
+ # Run tests
160
+ uv run pytest # All tests
161
+ uv run pytest -m "not slow" # Fast tests only
162
+
163
+ # Build
164
+ uv build
165
+ ```
166
+
167
+ ## License
168
+
169
+ LGPL-3. For complete license terms, visit https://www.gnu.org/licenses/lgpl-3.0.en.html
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "odoo-dev"
3
- version = "0.3.1"
3
+ version = "0.3.3"
4
4
  description = "Odoo Development Environment Helper"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,3 +1,3 @@
1
1
  """Odoo Development Environment Helper."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.3.3"
@@ -480,28 +480,35 @@ def _drop_database(cfg, db_name: str) -> None:
480
480
  venv_python = cfg.venv_path / "bin" / "python"
481
481
 
482
482
  # Try odoo-bin db drop first
483
- result = subprocess.run(
484
- [
485
- str(venv_python),
486
- str(cfg.odoo_bin),
487
- "-c",
488
- str(cfg.config_file),
489
- "db",
490
- "drop",
491
- db_name,
492
- ],
493
- capture_output=True,
494
- text=True,
495
- )
496
- if result.returncode == 0:
497
- return
483
+ try:
484
+ result = subprocess.run(
485
+ [
486
+ str(venv_python),
487
+ str(cfg.odoo_bin),
488
+ "-c",
489
+ str(cfg.config_file),
490
+ "db",
491
+ "drop",
492
+ db_name,
493
+ ],
494
+ capture_output=True,
495
+ text=True,
496
+ )
497
+ if result.returncode == 0:
498
+ return
499
+ except OSError:
500
+ pass
498
501
 
499
502
  # Fallback to dropdb
500
- warning(f"odoo-bin db drop failed, falling back to dropdb...")
503
+ warning("odoo-bin db drop failed, falling back to dropdb...")
501
504
  from odoo_dev.commands.db import _parse_db_config
502
505
 
503
506
  db_config = _parse_db_config(cfg.config_file)
504
507
  env = {**os.environ, "PGPASSWORD": db_config["password"]}
508
+
509
+ # Terminate active connections first — dropdb will fail if any remain
510
+ _terminate_connections(db_name, db_config)
511
+
505
512
  result = subprocess.run(
506
513
  [
507
514
  "dropdb",
@@ -521,6 +528,25 @@ def _drop_database(cfg, db_name: str) -> None:
521
528
  warning(result.stderr.strip())
522
529
 
523
530
 
531
+ def _terminate_connections(db_name: str, db_config: dict[str, str]) -> None:
532
+ """Terminate all active connections to a database."""
533
+ env = {**os.environ, "PGPASSWORD": db_config["password"]}
534
+ subprocess.run(
535
+ [
536
+ "psql",
537
+ "-h", db_config["host"],
538
+ "-p", db_config["port"],
539
+ "-U", db_config["user"],
540
+ "-d", "postgres",
541
+ "-c",
542
+ f"SELECT pg_terminate_backend(pid) FROM pg_stat_activity "
543
+ f"WHERE datname = '{db_name}' AND pid <> pg_backend_pid()",
544
+ ],
545
+ capture_output=True,
546
+ text=True,
547
+ env=env,
548
+ )
549
+
524
550
  def _get_addons_path(config_file) -> str:
525
551
  """Extract addons_path from odoo.conf."""
526
552
  for line in config_file.read_text().splitlines():
@@ -98,18 +98,6 @@ def setup(
98
98
  "--clean",
99
99
  help="Clean up before setup (remove existing venv, etc.)",
100
100
  ),
101
- db_user: str = typer.Option(
102
- "odoo",
103
- "-u",
104
- "--db-user",
105
- help="Database user for odoo.conf",
106
- ),
107
- master_password: str = typer.Option(
108
- "admin",
109
- "-w",
110
- "--master-password",
111
- help="Master (admin) password for odoo.conf",
112
- ),
113
101
  ) -> None:
114
102
  """Complete setup: clone Odoo repos, configure VSCode, build image."""
115
103
  # Prompt for versions if not configured
@@ -138,13 +126,13 @@ def setup(
138
126
  warning(
139
127
  f"This will install system dependencies and Python {cfg.python_version} if needed"
140
128
  )
141
- setup_venv(db_user=db_user, master_password=master_password)
129
+ setup_venv()
142
130
 
143
131
  # Prompt for Docker setup
144
132
  warning("\nDo you want to set up the Docker environment?")
145
133
  if typer.confirm("Continue with Docker setup?", default=True):
146
134
  # Set up Docker files first
147
- _setup_docker_files(cfg, community_only=community, db_user=db_user, master_password=master_password)
135
+ _setup_docker_files(cfg, community_only=community)
148
136
 
149
137
  success("\nBuilding Docker image...")
150
138
  from odoo_dev.commands.docker import build
@@ -159,25 +147,15 @@ def setup(
159
147
  success(" - Debug with VSCode: Use the configured launch profiles")
160
148
 
161
149
 
162
- def setup_venv(
163
- db_user: str = typer.Option(
164
- "odoo",
165
- "-u",
166
- "--db-user",
167
- help="Database user for odoo.conf",
168
- ),
169
- master_password: str = typer.Option(
170
- "admin",
171
- "-w",
172
- "--master-password",
173
- help="Master (admin) password for odoo.conf",
174
- ),
175
- ) -> None:
150
+ def setup_venv() -> None:
176
151
  """Set up local Python virtual environment for development."""
177
152
  cfg = load_config()
178
153
 
154
+ # Initialize submodules before anything else
155
+ _init_submodules(cfg)
156
+
179
157
  # Create config file first
180
- _setup_odoo_config(cfg, db_user=db_user, master_password=master_password)
158
+ _setup_odoo_config(cfg)
181
159
 
182
160
  success(f"Setting up Python virtual environment for Odoo {cfg.odoo_version}...")
183
161
 
@@ -242,49 +220,6 @@ def setup_venv(
242
220
  [*venv_pip, "pytest", "pytest-odoo", "debugpy", "manifestoo", "coverage"]
243
221
  )
244
222
 
245
- # Install Python external dependencies declared in addon manifests (runtime + test)
246
- if cfg.addons_dir.is_dir():
247
- success("Installing addon Python dependencies from manifests...")
248
- venv_python = cfg.venv_path / "bin" / "python"
249
- # Runtime deps via manifestoo
250
- result = subprocess.run(
251
- [
252
- str(venv_python),
253
- "-m",
254
- "manifestoo",
255
- "--select-addons-dir",
256
- str(cfg.addons_dir),
257
- "list-external-dependencies",
258
- "python",
259
- ],
260
- capture_output=True,
261
- text=True,
262
- env={**os.environ, "ODOO_VERSION": "", "ODOO_SERIES": ""},
263
- )
264
- all_python_deps: set[str] = set(result.stdout.split())
265
- # Test deps via direct manifest scan (manifestoo doesn't read test_external_dependencies)
266
- import ast
267
- for manifest_path in cfg.addons_dir.glob("*/__manifest__.py"):
268
- try:
269
- manifest = ast.literal_eval(manifest_path.read_text())
270
- test_deps = manifest.get("test_external_dependencies", {}).get("python", [])
271
- all_python_deps.update(test_deps)
272
- except Exception:
273
- pass
274
- python_deps = " ".join(all_python_deps)
275
- if python_deps:
276
- subprocess.run(
277
- [
278
- "uv",
279
- "pip",
280
- "install",
281
- "--python",
282
- str(venv_python),
283
- *python_deps.split(),
284
- ],
285
- check=False,
286
- )
287
-
288
223
  success("\nVirtual environment setup complete!")
289
224
  success(f"To activate: source {cfg.venv_path}/bin/activate")
290
225
 
@@ -341,7 +276,7 @@ def _init_submodules(cfg) -> None:
341
276
  warning(f"Submodule initialization had issues: {result.stderr}")
342
277
 
343
278
 
344
- def _setup_odoo_config(cfg, community_only: bool = False, db_user: str = "odoo", master_password: str = "admin") -> None:
279
+ def _setup_odoo_config(cfg, community_only: bool = False) -> None:
345
280
  """Create odoo.conf configuration file."""
346
281
  conf_dir = cfg.project_dir / "conf"
347
282
  conf_file = conf_dir / "odoo.conf"
@@ -376,8 +311,8 @@ def _setup_odoo_config(cfg, community_only: bool = False, db_user: str = "odoo",
376
311
 
377
312
  config_content = f"""[options]
378
313
  addons_path = {",".join(addons_paths)}
379
- admin_passwd = {master_password}
380
- db_user = {db_user}
314
+ admin_passwd = admin
315
+ db_user = odoo
381
316
  db_password = odoo
382
317
  """
383
318
 
@@ -386,7 +321,7 @@ db_password = odoo
386
321
  success(f"Config file created at {conf_file}")
387
322
 
388
323
 
389
- def _setup_docker_files(cfg, community_only: bool = False, db_user: str = "odoo", master_password: str = "admin") -> None:
324
+ def _setup_docker_files(cfg, community_only: bool = False) -> None:
390
325
  """Copy Docker templates to .odoo-deploy directory."""
391
326
  success("Setting up Docker files...")
392
327
 
@@ -406,10 +341,10 @@ def _setup_docker_files(cfg, community_only: bool = False, db_user: str = "odoo"
406
341
  success(f"Copied {filename}")
407
342
 
408
343
  # Create Docker odoo.conf
409
- _setup_docker_odoo_config(cfg, community_only=community_only, db_user=db_user, master_password=master_password)
344
+ _setup_docker_odoo_config(cfg, community_only=community_only)
410
345
 
411
346
 
412
- def _generate_docker_odoo_conf(community_only: bool = False, db_user: str = "odoo", master_password: str = "admin") -> str:
347
+ def _generate_docker_odoo_conf(community_only: bool = False) -> str:
413
348
  """Generate Docker-specific odoo.conf content."""
414
349
  # Build addons path for Docker (using /opt/project paths)
415
350
  addons_paths = [
@@ -430,10 +365,10 @@ def _generate_docker_odoo_conf(community_only: bool = False, db_user: str = "odo
430
365
 
431
366
  return f"""[options]
432
367
  addons_path = {",".join(addons_paths)}
433
- admin_passwd = {master_password}
368
+ admin_passwd = admin
434
369
  db_host = db
435
370
  db_port = 5432
436
- db_user = {db_user}
371
+ db_user = odoo
437
372
  db_password = odoo
438
373
  http_port = 8069
439
374
  gevent_port = 8072
@@ -451,7 +386,7 @@ data_dir = /opt/odoo-filestore
451
386
  """
452
387
 
453
388
 
454
- def _setup_docker_odoo_config(cfg, community_only: bool = False, db_user: str = "odoo", master_password: str = "admin") -> None:
389
+ def _setup_docker_odoo_config(cfg, community_only: bool = False) -> None:
455
390
  """Create Docker-specific odoo.conf configuration file."""
456
391
  conf_file = cfg.docker_config_file
457
392
 
@@ -460,7 +395,7 @@ def _setup_docker_odoo_config(cfg, community_only: bool = False, db_user: str =
460
395
  return
461
396
 
462
397
  success("Creating Docker Odoo configuration file...")
463
- conf_file.write_text(_generate_docker_odoo_conf(community_only=community_only, db_user=db_user, master_password=master_password))
398
+ conf_file.write_text(_generate_docker_odoo_conf(community_only=community_only))
464
399
  conf_file.chmod(0o600)
465
400
  success(f"Docker config file created at {conf_file}")
466
401
 
@@ -75,3 +75,4 @@ tsconfig.json
75
75
 
76
76
  # Ctags
77
77
  /tags
78
+ .odoo-data/
@@ -0,0 +1,165 @@
1
+ """Integration tests for _drop_database.
2
+
3
+ These tests verify that test databases are actually dropped after a test run.
4
+ Requires a local PostgreSQL instance accessible via peer authentication.
5
+ """
6
+
7
+ import subprocess
8
+ import time
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+
12
+ import pytest
13
+
14
+
15
+ def _pg_run(cmd: list[str], **kwargs) -> subprocess.CompletedProcess:
16
+ """Run a PostgreSQL command using peer auth on the local socket."""
17
+ return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
18
+
19
+
20
+ def _db_exists(db_name: str) -> bool:
21
+ result = _pg_run(["psql", "-d", "postgres", "-tAc",
22
+ f"SELECT 1 FROM pg_database WHERE datname = '{db_name}'"])
23
+ return result.stdout.strip() == "1"
24
+
25
+
26
+ def _create_db(db_name: str) -> None:
27
+ result = _pg_run(["createdb", db_name])
28
+ assert result.returncode == 0, f"createdb failed: {result.stderr}"
29
+
30
+
31
+ def _drop_db_force(db_name: str) -> None:
32
+ """Forcefully drop a database (for test cleanup)."""
33
+ _pg_run(["psql", "-d", "postgres", "-c",
34
+ f"DROP DATABASE IF EXISTS \"{db_name}\" WITH (FORCE)"])
35
+
36
+
37
+ @dataclass
38
+ class FakeConfig:
39
+ """Minimal config to satisfy _drop_database's interface."""
40
+ project_dir: Path
41
+ venv_path: Path
42
+ odoo_bin: Path
43
+ config_file: Path
44
+
45
+
46
+ def _make_config(tmp_path: Path) -> FakeConfig:
47
+ """Create a fake config with an odoo.conf using peer auth.
48
+
49
+ Sets up a real python symlink but a fake odoo-bin that always exits 1,
50
+ so the odoo-bin drop method fails cleanly (nonzero exit) and the
51
+ dropdb fallback path is exercised — matching real-world behavior where
52
+ 'odoo-bin db drop' fails.
53
+ """
54
+ conf = tmp_path / "conf"
55
+ conf.mkdir()
56
+ config_file = conf / "odoo.conf"
57
+ # Use peer auth (no password, local socket) matching the test environment
58
+ config_file.write_text(
59
+ "[options]\n"
60
+ "db_host = /var/run/postgresql\n"
61
+ "db_port = 5432\n"
62
+ f"db_user = {subprocess.run(['whoami'], capture_output=True, text=True).stdout.strip()}\n"
63
+ "db_password =\n"
64
+ )
65
+ # Create a venv with a real python and a fake odoo-bin that always fails
66
+ venv_path = tmp_path / ".venv"
67
+ venv_path.mkdir()
68
+ bin_dir = venv_path / "bin"
69
+ bin_dir.mkdir()
70
+ import sys
71
+ (bin_dir / "python").symlink_to(sys.executable)
72
+
73
+ fake_odoo_bin = tmp_path / "odoo-bin"
74
+ fake_odoo_bin.write_text("#!/usr/bin/env python3\nimport sys; sys.exit(1)\n")
75
+ fake_odoo_bin.chmod(0o755)
76
+
77
+ return FakeConfig(
78
+ project_dir=tmp_path,
79
+ venv_path=venv_path,
80
+ odoo_bin=fake_odoo_bin,
81
+ config_file=config_file,
82
+ )
83
+
84
+
85
+ @pytest.mark.slow
86
+ class TestDropDatabase:
87
+ """Verify _drop_database actually removes the database."""
88
+
89
+ def _unique_db(self) -> str:
90
+ return f"test_odoodev_{int(time.time() * 1000)}"
91
+
92
+ def test_drop_removes_database(self, tmp_path: Path):
93
+ """Basic case: database with no active connections is dropped."""
94
+ from odoo_dev.commands.run import _drop_database
95
+
96
+ db_name = self._unique_db()
97
+ cfg = _make_config(tmp_path)
98
+
99
+ _create_db(db_name)
100
+ assert _db_exists(db_name), "Precondition: database should exist"
101
+
102
+ try:
103
+ _drop_database(cfg, db_name)
104
+ assert not _db_exists(db_name), (
105
+ f"Database {db_name} still exists after _drop_database!"
106
+ )
107
+ finally:
108
+ _drop_db_force(db_name)
109
+
110
+ def test_drop_works_when_venv_missing(self, tmp_path: Path):
111
+ """When venv python doesn't exist, should fall back to dropdb
112
+ instead of crashing with FileNotFoundError."""
113
+ from odoo_dev.commands.run import _drop_database
114
+
115
+ db_name = self._unique_db()
116
+ cfg = _make_config(tmp_path)
117
+ # Remove the python symlink to simulate missing venv
118
+ (cfg.venv_path / "bin" / "python").unlink()
119
+
120
+ _create_db(db_name)
121
+ assert _db_exists(db_name), "Precondition: database should exist"
122
+
123
+ try:
124
+ _drop_database(cfg, db_name)
125
+ assert not _db_exists(db_name), (
126
+ f"Database {db_name} still exists after _drop_database!"
127
+ )
128
+ finally:
129
+ _drop_db_force(db_name)
130
+
131
+ def test_drop_removes_database_with_active_connections(self, tmp_path: Path):
132
+ """Simulates a lingering Odoo worker holding a connection open.
133
+
134
+ This is the primary failure mode: dropdb refuses to drop a database
135
+ that has active connections, and _drop_database doesn't terminate
136
+ them first.
137
+ """
138
+ from odoo_dev.commands.run import _drop_database
139
+
140
+ db_name = self._unique_db()
141
+ cfg = _make_config(tmp_path)
142
+
143
+ _create_db(db_name)
144
+ assert _db_exists(db_name), "Precondition: database should exist"
145
+
146
+ # Hold an active connection (simulates a lingering Odoo worker)
147
+ blocker = subprocess.Popen(
148
+ ["psql", "-d", db_name, "-c", "SELECT pg_sleep(300)"],
149
+ stdout=subprocess.PIPE,
150
+ stderr=subprocess.PIPE,
151
+ )
152
+
153
+ try:
154
+ # Give the connection a moment to establish
155
+ time.sleep(0.5)
156
+
157
+ _drop_database(cfg, db_name)
158
+ assert not _db_exists(db_name), (
159
+ f"Database {db_name} still exists after _drop_database! "
160
+ "Active connections were not terminated before drop."
161
+ )
162
+ finally:
163
+ blocker.terminate()
164
+ blocker.wait(timeout=5)
165
+ _drop_db_force(db_name)
@@ -158,6 +158,56 @@ class TestSetupDockerFiles:
158
158
  assert cfg.docker_config_file.read_text() == "existing config"
159
159
 
160
160
 
161
+ class TestInitSubmodules:
162
+ """Test _init_submodules is called correctly from setup_venv."""
163
+
164
+ def test_setup_venv_calls_init_submodules(self, tmp_path: Path):
165
+ """setup_venv should initialize submodules before setting up the venv."""
166
+ from unittest.mock import call, patch
167
+
168
+ cfg = _make_cfg(tmp_path)
169
+ # Create .gitmodules to trigger the submodule check
170
+ (tmp_path / ".gitmodules").write_text("[submodule]\n")
171
+
172
+ with patch("odoo_dev.commands.setup.load_config", return_value=cfg), \
173
+ patch("odoo_dev.commands.setup._init_submodules") as mock_init, \
174
+ patch("odoo_dev.commands.setup._setup_odoo_config"), \
175
+ patch("odoo_dev.commands.setup._install_system_dependencies"), \
176
+ patch("odoo_dev.commands.setup._update_python_path"), \
177
+ patch("subprocess.run", return_value=type("R", (), {"returncode": 0})()):
178
+ from odoo_dev.commands.setup import setup_venv
179
+ setup_venv()
180
+
181
+ mock_init.assert_called_once_with(cfg)
182
+
183
+ def test_init_submodules_skips_if_no_gitmodules(self, tmp_path: Path):
184
+ """_init_submodules should do nothing if .gitmodules does not exist."""
185
+ from odoo_dev.commands.setup import _init_submodules
186
+
187
+ cfg = _make_cfg(tmp_path)
188
+ with patch("subprocess.run") as mock_run:
189
+ _init_submodules(cfg)
190
+ mock_run.assert_not_called()
191
+
192
+ def test_init_submodules_runs_git_command(self, tmp_path: Path):
193
+ """_init_submodules should run git submodule update when .gitmodules exists."""
194
+ from odoo_dev.commands.setup import _init_submodules
195
+
196
+ cfg = _make_cfg(tmp_path)
197
+ (tmp_path / ".gitmodules").write_text("[submodule]\n")
198
+
199
+ with patch("subprocess.run", return_value=type("R", (), {"returncode": 0, "stderr": ""})()) as mock_run:
200
+ _init_submodules(cfg)
201
+
202
+ mock_run.assert_called_once()
203
+ args = mock_run.call_args[0][0]
204
+ assert "git" in args
205
+ assert "submodule" in args
206
+ assert "update" in args
207
+ assert "--init" in args
208
+ assert "--recursive" in args
209
+
210
+
161
211
  class TestVscodeSetup:
162
212
  """Test vscode() copies templates correctly."""
163
213
 
odoo_dev-0.3.1/PKG-INFO DELETED
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: odoo-dev
3
- Version: 0.3.1
4
- Summary: Odoo Development Environment Helper
5
- Project-URL: Homepage, https://git.bemade.org/bemade/odoo-dev
6
- Project-URL: Repository, https://git.bemade.org/bemade/odoo-dev
7
- Project-URL: Issues, https://git.bemade.org/bemade/odoo-dev/-/issues
8
- License-Expression: LGPL-3.0
9
- Requires-Python: >=3.12
10
- Requires-Dist: pyyaml>=6.0
11
- Requires-Dist: rich>=13.0.0
12
- Requires-Dist: typer>=0.9.0
13
- Description-Content-Type: text/markdown
14
-
15
- # odoo-dev
16
-
17
- > **This repository has moved to GitHub: https://github.com/bemade/odoo-dev**
18
- >
19
- > This GitLab repository is archived and no longer maintained.
odoo_dev-0.3.1/README.md DELETED
@@ -1,5 +0,0 @@
1
- # odoo-dev
2
-
3
- > **This repository has moved to GitHub: https://github.com/bemade/odoo-dev**
4
- >
5
- > This GitLab repository is archived and no longer maintained.
@@ -1 +0,0 @@
1
- gitdir: ../../../.git/modules/tests/fixtures/odoo-empty
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes