odoo-dev 1.1.0__tar.gz → 1.2.1__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.
- odoo_dev-1.2.1/.release-please-manifest.json +3 -0
- odoo_dev-1.2.1/CHANGELOG.md +60 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/PKG-INFO +55 -3
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/README.md +54 -2
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/pyproject.toml +1 -1
- odoo_dev-1.2.1/src/odoo_dev/__init__.py +3 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/cli.py +2 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/run.py +53 -12
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/setup.py +80 -20
- odoo_dev-1.2.1/src/odoo_dev/commands/vendor.py +287 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/config.py +10 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/__init__.py +10 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/develop.py +321 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/edit.py +91 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/lock.py +86 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/materialize.py +130 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/migrate.py +208 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/sources.py +146 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/sync.py +33 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/update.py +124 -0
- odoo_dev-1.2.1/src/odoo_dev/vendor/verify.py +189 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_setup.py +125 -7
- odoo_dev-1.2.1/tests/test_vendor_develop.py +226 -0
- odoo_dev-1.2.1/tests/test_vendor_edit.py +139 -0
- odoo_dev-1.2.1/tests/test_vendor_lock.py +108 -0
- odoo_dev-1.2.1/tests/test_vendor_materialize.py +145 -0
- odoo_dev-1.2.1/tests/test_vendor_migrate.py +173 -0
- odoo_dev-1.2.1/tests/test_vendor_update.py +148 -0
- odoo_dev-1.2.1/tests/test_vendor_verify.py +180 -0
- odoo_dev-1.1.0/.release-please-manifest.json +0 -3
- odoo_dev-1.1.0/CHANGELOG.md +0 -26
- odoo_dev-1.1.0/src/odoo_dev/__init__.py +0 -3
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/.github/workflows/release-please.yml +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/.gitignore +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/.gitmodules +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/.python-version +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/release-please-config.json +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/bump.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/db.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/commands/docker.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/preflight.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/docker/Dockerfile +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/docker/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/docker/docker-entrypoint.sh +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/vscode/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/vscode/launch.json +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/vscode/settings.json +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/templates/vscode/tasks.json +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/utils/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/utils/console.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/src/odoo_dev/utils/manifest.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/fixtures/__init__.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_bump.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_cli.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_config.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_db.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_db_config.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_drop_database.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_integration.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_preflight.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_run.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/tests/test_setup_db.py +0 -0
- {odoo_dev-1.1.0 → odoo_dev-1.2.1}/uv.lock +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.2.1](https://github.com/bemade/odoo-dev/compare/v1.2.0...v1.2.1) (2026-07-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **test:** make full-suite runs work under a dev odoo.conf ([2a1b752](https://github.com/bemade/odoo-dev/commit/2a1b7527d29b3bf8bb776e41ce739b4864543b73))
|
|
9
|
+
* **vendor:** bump clears stale version when pinning to a commit ([19544ba](https://github.com/bemade/odoo-dev/commit/19544bac55a3dded4b912cc9f2ac7226f5d72d61))
|
|
10
|
+
* **vendor:** constrain `vendor update` to the pin's own major series ([5b58e26](https://github.com/bemade/odoo-dev/commit/5b58e2676de6c1b63a678d1dd365efca9e4c8b99))
|
|
11
|
+
* **vendor:** migrate sets version only when the tag resolves to the pin ([ea59b1d](https://github.com/bemade/odoo-dev/commit/ea59b1d62c9844fb831925d9973b4ec179b803e6))
|
|
12
|
+
|
|
13
|
+
## [1.2.0](https://github.com/bemade/odoo-dev/compare/v1.1.0...v1.2.0) (2026-07-07)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* add 'vendor' command for per-addon vendoring ([7dfe8cb](https://github.com/bemade/odoo-dev/commit/7dfe8cbbf20031b012bc3326485f9aa3b634146c))
|
|
19
|
+
* **setup:** add --no-docker and --yes for headless agentic setup ([daa62c4](https://github.com/bemade/odoo-dev/commit/daa62c496ceb923152305091c5f94958014f1a51))
|
|
20
|
+
* **setup:** skip submodule init for fully-vendored repos ([83b7818](https://github.com/bemade/odoo-dev/commit/83b78186a0a23b3d33eb12a5364d312794315158))
|
|
21
|
+
* **vendor:** add `vendor develop` local dev-loop for vendored addons ([89325f8](https://github.com/bemade/odoo-dev/commit/89325f88a9a51bfc4d4cb4ea755381c55e62989a))
|
|
22
|
+
* **vendor:** add `vendor update` — the pull side of vendoring ([fb88c1e](https://github.com/bemade/odoo-dev/commit/fb88c1e3b62604f59932811b772cd5e420eb5152))
|
|
23
|
+
* **vendor:** add vendored/ to the addons_path when present ([0fdc512](https://github.com/bemade/odoo-dev/commit/0fdc512a9c9df2541028a8743d1afb33240ac6de))
|
|
24
|
+
* **vendor:** assert-no-hybrid guard in `vendor check --no-hybrid` ([d424eca](https://github.com/bemade/odoo-dev/commit/d424eca69fac72f27a44e24ea3ca68fe9e7b37af))
|
|
25
|
+
* **vendor:** test and cover vendored/ addons alongside local ones ([a989d17](https://github.com/bemade/odoo-dev/commit/a989d174cc6cc84da5cae7fad327c017cbaf8030))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* **vendor:** compare python-dep package NAMES, not full spec strings ([6903fa3](https://github.com/bemade/odoo-dev/commit/6903fa307dfab977cb40b9d7dfee2d017f67a0ff))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
|
|
35
|
+
* document the vendor command + agentic setup flag ([523ac05](https://github.com/bemade/odoo-dev/commit/523ac050bcd103d18b99abc8cc3a50ab0c6886c4))
|
|
36
|
+
|
|
37
|
+
## [1.1.0](https://github.com/bemade/odoo-dev/compare/v1.0.0...v1.1.0) (2026-06-26)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Features
|
|
41
|
+
|
|
42
|
+
* add `bump` command for series-agnostic manifest version bumps ([#7](https://github.com/bemade/odoo-dev/issues/7)) ([992c8aa](https://github.com/bemade/odoo-dev/commit/992c8aa3731d262803a6f329209275462d88d80a))
|
|
43
|
+
|
|
44
|
+
## [1.0.0](https://github.com/bemade/odoo-dev/compare/v0.4.0...v1.0.0) (2026-06-11)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### ⚠ BREAKING CHANGES
|
|
48
|
+
|
|
49
|
+
* macOS `setup` no longer installs PostgreSQL. Fresh macOS setups must install a server themselves (or point DB_HOST/DB_PORT at a remote/Docker server).
|
|
50
|
+
|
|
51
|
+
### Features
|
|
52
|
+
|
|
53
|
+
* treat PostgreSQL as an external prerequisite on all platforms ([#5](https://github.com/bemade/odoo-dev/issues/5)) ([e817107](https://github.com/bemade/odoo-dev/commit/e817107ac434a5b23dd2675d59a95d1a6f8d1e71))
|
|
54
|
+
|
|
55
|
+
## [0.4.0](https://github.com/bemade/odoo-dev/compare/v0.3.3...v0.4.0) (2026-06-11)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Features
|
|
59
|
+
|
|
60
|
+
* configurable DB connection + connectivity preflight ([#3](https://github.com/bemade/odoo-dev/issues/3)) ([632738e](https://github.com/bemade/odoo-dev/commit/632738e37c1d22d79ec4a03cba72a28ca580f7c8))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-dev
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Odoo Development Environment Helper
|
|
5
5
|
Project-URL: Homepage, https://git.bemade.org/bemade/odoo-dev
|
|
6
6
|
Project-URL: Repository, https://git.bemade.org/bemade/odoo-dev
|
|
@@ -102,7 +102,7 @@ odoo-dev shell mydb # Open an Odoo shell
|
|
|
102
102
|
odoo-dev update base -d mydb # Update modules
|
|
103
103
|
odoo-dev test my_module # Run a module's tests (coverage on by default)
|
|
104
104
|
odoo-dev test my_module --test-tags my_module --no-coverage
|
|
105
|
-
odoo-dev test # Auto-discover & test all addons in addons/
|
|
105
|
+
odoo-dev test # Auto-discover & test all addons in addons/ + vendored/
|
|
106
106
|
odoo-dev scaffold my_module # Create a new module
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -133,12 +133,58 @@ odoo-dev docker psql # PostgreSQL shell
|
|
|
133
133
|
### Setup Commands
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
odoo-dev setup # Full setup
|
|
136
|
+
odoo-dev setup # Full setup (interactive; offers Docker)
|
|
137
137
|
odoo-dev setup --community # Community edition only
|
|
138
|
+
odoo-dev setup --no-docker --yes # Headless/agentic: clone + venv + conf, no Docker, no prompts
|
|
138
139
|
odoo-dev setup-venv # Just create the venv (no repo clone)
|
|
139
140
|
odoo-dev vscode # Configure VSCode debugging
|
|
140
141
|
```
|
|
141
142
|
|
|
143
|
+
### Vendored Addons
|
|
144
|
+
|
|
145
|
+
Shared addons can be **vendored** — materialized as real committed directories
|
|
146
|
+
under `vendored/`, pinned per-addon by `addons.lock` — instead of pulled in as
|
|
147
|
+
`.repos/` git submodules. This makes each promotion a normal, reviewable file
|
|
148
|
+
diff and deploys as plain files (what Odoo.sh needs). `vendored/` and `addons/`
|
|
149
|
+
are both on the addons path.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
odoo-dev vendor migrate # Convert .repos submodule+symlink addons -> vendored/
|
|
153
|
+
odoo-dev vendor migrate --dry-run # Preview the pins without changing anything
|
|
154
|
+
odoo-dev vendor sync # Materialize vendored/ from addons.lock
|
|
155
|
+
odoo-dev vendor check # CI gate: verify vendored/ byte-matches the pins
|
|
156
|
+
odoo-dev vendor add fsm --source github.com/bemade/bemade-addons --version 18.0.1.3.2
|
|
157
|
+
odoo-dev vendor bump fsm --version 18.0.1.4.0 # Move a pin and re-materialize
|
|
158
|
+
odoo-dev vendor update # Pull newest upstream for all tracked addons
|
|
159
|
+
odoo-dev vendor update fsm --dry-run # Show what would update, change nothing
|
|
160
|
+
odoo-dev vendor develop fsm # Edit fsm against a live source clone
|
|
161
|
+
odoo-dev vendor develop fsm --stop # Leave develop mode (keeps the clone)
|
|
162
|
+
odoo-dev vendor status # Show vendored pins, symlinks, develop mode
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Because `vendored/<addon>` is a materialized copy (re-`sync` clobbers it), you
|
|
166
|
+
don't edit it in place. `vendor develop <addon>` clones the addon's source repo
|
|
167
|
+
into a git-ignored `.vendor-dev/`, checks out a work branch at the pinned commit,
|
|
168
|
+
and prepends an overlay to your **local** `conf/odoo.conf` addons_path so Odoo
|
|
169
|
+
loads the live tree instead of the vendored copy (first path wins). `vendored/`
|
|
170
|
+
stays byte-for-byte pristine and only the local conf is touched, so nothing
|
|
171
|
+
dev-only can leak into a commit or into CI/prod. Edit in the clone, run/test in
|
|
172
|
+
this project's Odoo, commit + push upstream, then `vendor bump` once the new
|
|
173
|
+
version is tagged. `--branch NAME` names the work branch; `--base REF` bases it on
|
|
174
|
+
something other than the current pin.
|
|
175
|
+
|
|
176
|
+
`vendor check` verifies, per addon: the vendored files byte-match the pinned
|
|
177
|
+
commit; a `version` tag (if set) still resolves to that commit; every manifest
|
|
178
|
+
`external_dependencies['python']` is named in `requirements.txt`; and no addon
|
|
179
|
+
name collides between `addons/` and `vendored/`.
|
|
180
|
+
|
|
181
|
+
`vendor update` is the **pull** side: each client owns its pins. For every addon
|
|
182
|
+
that tracks upstream — a `version` (bump to the newest `<addon>/<version>` tag) or
|
|
183
|
+
a `branch` (bump to its HEAD) — it moves the pin forward and re-materializes.
|
|
184
|
+
Addons pinned to a bare commit are left alone. Run it on a schedule (a client's own
|
|
185
|
+
CI, its own token, opening a `vendor-bump/…` MR) so shared-addon changes propagate
|
|
186
|
+
without any per-source push/fan-out machinery.
|
|
187
|
+
|
|
142
188
|
## Project Structure
|
|
143
189
|
|
|
144
190
|
odoo-dev expects this project structure:
|
|
@@ -147,6 +193,8 @@ odoo-dev expects this project structure:
|
|
|
147
193
|
my-odoo-project/
|
|
148
194
|
├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
|
|
149
195
|
├── addons/ # Your custom addons
|
|
196
|
+
├── vendored/ # Vendored shared addons (real files, pinned by addons.lock)
|
|
197
|
+
├── addons.lock # Per-addon pins for vendored/ (see `vendor`)
|
|
150
198
|
├── requirements.txt # Project-specific Python deps
|
|
151
199
|
├── odoo/ # Cloned by setup
|
|
152
200
|
├── enterprise/ # Cloned by setup (unless --community)
|
|
@@ -156,6 +204,10 @@ my-odoo-project/
|
|
|
156
204
|
└── odoo.conf # Created by setup
|
|
157
205
|
```
|
|
158
206
|
|
|
207
|
+
`vendored/` and `addons.lock` are only present once a project adopts vendoring
|
|
208
|
+
(`odoo-dev vendor migrate`); submodule-based projects use `.repos/` + symlinks
|
|
209
|
+
into `addons/` instead.
|
|
210
|
+
|
|
159
211
|
## Configuration
|
|
160
212
|
|
|
161
213
|
Create a `.env` file in your project root:
|
|
@@ -88,7 +88,7 @@ odoo-dev shell mydb # Open an Odoo shell
|
|
|
88
88
|
odoo-dev update base -d mydb # Update modules
|
|
89
89
|
odoo-dev test my_module # Run a module's tests (coverage on by default)
|
|
90
90
|
odoo-dev test my_module --test-tags my_module --no-coverage
|
|
91
|
-
odoo-dev test # Auto-discover & test all addons in addons/
|
|
91
|
+
odoo-dev test # Auto-discover & test all addons in addons/ + vendored/
|
|
92
92
|
odoo-dev scaffold my_module # Create a new module
|
|
93
93
|
```
|
|
94
94
|
|
|
@@ -119,12 +119,58 @@ odoo-dev docker psql # PostgreSQL shell
|
|
|
119
119
|
### Setup Commands
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
|
-
odoo-dev setup # Full setup
|
|
122
|
+
odoo-dev setup # Full setup (interactive; offers Docker)
|
|
123
123
|
odoo-dev setup --community # Community edition only
|
|
124
|
+
odoo-dev setup --no-docker --yes # Headless/agentic: clone + venv + conf, no Docker, no prompts
|
|
124
125
|
odoo-dev setup-venv # Just create the venv (no repo clone)
|
|
125
126
|
odoo-dev vscode # Configure VSCode debugging
|
|
126
127
|
```
|
|
127
128
|
|
|
129
|
+
### Vendored Addons
|
|
130
|
+
|
|
131
|
+
Shared addons can be **vendored** — materialized as real committed directories
|
|
132
|
+
under `vendored/`, pinned per-addon by `addons.lock` — instead of pulled in as
|
|
133
|
+
`.repos/` git submodules. This makes each promotion a normal, reviewable file
|
|
134
|
+
diff and deploys as plain files (what Odoo.sh needs). `vendored/` and `addons/`
|
|
135
|
+
are both on the addons path.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
odoo-dev vendor migrate # Convert .repos submodule+symlink addons -> vendored/
|
|
139
|
+
odoo-dev vendor migrate --dry-run # Preview the pins without changing anything
|
|
140
|
+
odoo-dev vendor sync # Materialize vendored/ from addons.lock
|
|
141
|
+
odoo-dev vendor check # CI gate: verify vendored/ byte-matches the pins
|
|
142
|
+
odoo-dev vendor add fsm --source github.com/bemade/bemade-addons --version 18.0.1.3.2
|
|
143
|
+
odoo-dev vendor bump fsm --version 18.0.1.4.0 # Move a pin and re-materialize
|
|
144
|
+
odoo-dev vendor update # Pull newest upstream for all tracked addons
|
|
145
|
+
odoo-dev vendor update fsm --dry-run # Show what would update, change nothing
|
|
146
|
+
odoo-dev vendor develop fsm # Edit fsm against a live source clone
|
|
147
|
+
odoo-dev vendor develop fsm --stop # Leave develop mode (keeps the clone)
|
|
148
|
+
odoo-dev vendor status # Show vendored pins, symlinks, develop mode
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Because `vendored/<addon>` is a materialized copy (re-`sync` clobbers it), you
|
|
152
|
+
don't edit it in place. `vendor develop <addon>` clones the addon's source repo
|
|
153
|
+
into a git-ignored `.vendor-dev/`, checks out a work branch at the pinned commit,
|
|
154
|
+
and prepends an overlay to your **local** `conf/odoo.conf` addons_path so Odoo
|
|
155
|
+
loads the live tree instead of the vendored copy (first path wins). `vendored/`
|
|
156
|
+
stays byte-for-byte pristine and only the local conf is touched, so nothing
|
|
157
|
+
dev-only can leak into a commit or into CI/prod. Edit in the clone, run/test in
|
|
158
|
+
this project's Odoo, commit + push upstream, then `vendor bump` once the new
|
|
159
|
+
version is tagged. `--branch NAME` names the work branch; `--base REF` bases it on
|
|
160
|
+
something other than the current pin.
|
|
161
|
+
|
|
162
|
+
`vendor check` verifies, per addon: the vendored files byte-match the pinned
|
|
163
|
+
commit; a `version` tag (if set) still resolves to that commit; every manifest
|
|
164
|
+
`external_dependencies['python']` is named in `requirements.txt`; and no addon
|
|
165
|
+
name collides between `addons/` and `vendored/`.
|
|
166
|
+
|
|
167
|
+
`vendor update` is the **pull** side: each client owns its pins. For every addon
|
|
168
|
+
that tracks upstream — a `version` (bump to the newest `<addon>/<version>` tag) or
|
|
169
|
+
a `branch` (bump to its HEAD) — it moves the pin forward and re-materializes.
|
|
170
|
+
Addons pinned to a bare commit are left alone. Run it on a schedule (a client's own
|
|
171
|
+
CI, its own token, opening a `vendor-bump/…` MR) so shared-addon changes propagate
|
|
172
|
+
without any per-source push/fan-out machinery.
|
|
173
|
+
|
|
128
174
|
## Project Structure
|
|
129
175
|
|
|
130
176
|
odoo-dev expects this project structure:
|
|
@@ -133,6 +179,8 @@ odoo-dev expects this project structure:
|
|
|
133
179
|
my-odoo-project/
|
|
134
180
|
├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
|
|
135
181
|
├── addons/ # Your custom addons
|
|
182
|
+
├── vendored/ # Vendored shared addons (real files, pinned by addons.lock)
|
|
183
|
+
├── addons.lock # Per-addon pins for vendored/ (see `vendor`)
|
|
136
184
|
├── requirements.txt # Project-specific Python deps
|
|
137
185
|
├── odoo/ # Cloned by setup
|
|
138
186
|
├── enterprise/ # Cloned by setup (unless --community)
|
|
@@ -142,6 +190,10 @@ my-odoo-project/
|
|
|
142
190
|
└── odoo.conf # Created by setup
|
|
143
191
|
```
|
|
144
192
|
|
|
193
|
+
`vendored/` and `addons.lock` are only present once a project adopts vendoring
|
|
194
|
+
(`odoo-dev vendor migrate`); submodule-based projects use `.repos/` + symlinks
|
|
195
|
+
into `addons/` instead.
|
|
196
|
+
|
|
145
197
|
## Configuration
|
|
146
198
|
|
|
147
199
|
Create a `.env` file in your project root:
|
|
@@ -7,6 +7,7 @@ import typer
|
|
|
7
7
|
from odoo_dev import __version__
|
|
8
8
|
from odoo_dev.commands import bump as bump_cmd
|
|
9
9
|
from odoo_dev.commands import db, docker, run, setup
|
|
10
|
+
from odoo_dev.commands import vendor as vendor_cmd
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def _version_callback(value: bool) -> None:
|
|
@@ -40,6 +41,7 @@ def _main(
|
|
|
40
41
|
# Command groups
|
|
41
42
|
app.add_typer(db.app, name="db")
|
|
42
43
|
app.add_typer(docker.app, name="docker")
|
|
44
|
+
app.add_typer(vendor_cmd.app, name="vendor")
|
|
43
45
|
|
|
44
46
|
# Setup commands
|
|
45
47
|
app.command()(setup.setup)
|
|
@@ -235,17 +235,18 @@ def test(
|
|
|
235
235
|
break
|
|
236
236
|
|
|
237
237
|
# Auto-load exclusions from repo_deps.yaml when not explicitly provided
|
|
238
|
-
|
|
238
|
+
no_ci_modules: list[str] = []
|
|
239
|
+
if repo_deps_file is not None:
|
|
239
240
|
try:
|
|
240
241
|
import yaml
|
|
241
242
|
|
|
242
243
|
data = yaml.safe_load(repo_deps_file.read_text()) or {}
|
|
243
|
-
|
|
244
|
-
if no_ci:
|
|
245
|
-
exclude = ",".join(no_ci)
|
|
246
|
-
info(f"Excluding (from {repo_deps_file} no_ci): {exclude}")
|
|
244
|
+
no_ci_modules = list(data.get("no_ci", []) or [])
|
|
247
245
|
except Exception:
|
|
248
|
-
|
|
246
|
+
no_ci_modules = []
|
|
247
|
+
if exclude is None and no_ci_modules:
|
|
248
|
+
exclude = ",".join(no_ci_modules)
|
|
249
|
+
info(f"Excluding (from {repo_deps_file} no_ci): {exclude}")
|
|
249
250
|
|
|
250
251
|
# Verify setup
|
|
251
252
|
if not cfg.venv_path.exists():
|
|
@@ -268,7 +269,9 @@ def test(
|
|
|
268
269
|
|
|
269
270
|
# Determine modules to test
|
|
270
271
|
if modules is None:
|
|
271
|
-
# Use manifestoo to list addons
|
|
272
|
+
# Use manifestoo to list addons across addons/ and vendored/, so a no-args
|
|
273
|
+
# run tests all custom AND vendored addons together (catches vendored
|
|
274
|
+
# addons that break when combined with local addons or with each other).
|
|
272
275
|
manifestoo_cmd = [
|
|
273
276
|
str(venv_python),
|
|
274
277
|
"-m",
|
|
@@ -276,6 +279,8 @@ def test(
|
|
|
276
279
|
"--select-addons-dir",
|
|
277
280
|
str(cfg.addons_dir),
|
|
278
281
|
]
|
|
282
|
+
if cfg.vendored_dir.is_dir():
|
|
283
|
+
manifestoo_cmd.extend(["--select-addons-dir", str(cfg.vendored_dir)])
|
|
279
284
|
if exclude:
|
|
280
285
|
manifestoo_cmd.extend(["--select-exclude", exclude])
|
|
281
286
|
manifestoo_cmd.extend(["list", "--separator=,"])
|
|
@@ -287,7 +292,7 @@ def test(
|
|
|
287
292
|
)
|
|
288
293
|
modules = result.stdout.strip()
|
|
289
294
|
if not modules:
|
|
290
|
-
error(f"No addons found in {cfg.addons_dir}")
|
|
295
|
+
error(f"No addons found in {cfg.addons_dir} or {cfg.vendored_dir}")
|
|
291
296
|
raise typer.Exit(1)
|
|
292
297
|
|
|
293
298
|
# Clone external repo dependencies declared in repo_deps.yaml and symlink into addons_dir
|
|
@@ -372,6 +377,9 @@ def test(
|
|
|
372
377
|
"--stop-after-init",
|
|
373
378
|
"--http-port",
|
|
374
379
|
str(http_port),
|
|
380
|
+
# No need to fork prefork workers for a stop-after-init install.
|
|
381
|
+
"--workers",
|
|
382
|
+
"0",
|
|
375
383
|
],
|
|
376
384
|
)
|
|
377
385
|
|
|
@@ -387,9 +395,9 @@ def test(
|
|
|
387
395
|
success(f"Running tests: {modules}")
|
|
388
396
|
|
|
389
397
|
if coverage:
|
|
390
|
-
# Build coverage source paths
|
|
398
|
+
# Build coverage source paths (a module may live in addons/ or vendored/)
|
|
391
399
|
coverage_source = ",".join(
|
|
392
|
-
str(cfg
|
|
400
|
+
str(_module_dir(cfg, m)) for m in modules.split(",")
|
|
393
401
|
)
|
|
394
402
|
|
|
395
403
|
test_cmd = [
|
|
@@ -418,11 +426,36 @@ def test(
|
|
|
418
426
|
"--stop-after-init",
|
|
419
427
|
"--http-port",
|
|
420
428
|
str(http_port),
|
|
429
|
+
# HttpCase needs the threaded server (ThreadedServer.httpd);
|
|
430
|
+
# a prefork/multi-worker conf raises
|
|
431
|
+
# "'PreforkServer' object has no attribute 'httpd'" in
|
|
432
|
+
# setUpClass. Force single-process regardless of odoo.conf.
|
|
433
|
+
"--workers",
|
|
434
|
+
"0",
|
|
435
|
+
# A restrictive dbfilter in odoo.conf (e.g. `dbfilter = ^rwi$`)
|
|
436
|
+
# never matches the ephemeral test DB, so HttpCase requests get
|
|
437
|
+
# "dbfilter rejects it; logging session out" -> auth='user'
|
|
438
|
+
# routes 404 and JSON endpoints return non-JSON. Scope the
|
|
439
|
+
# filter to this run's test DB so the test server serves it.
|
|
440
|
+
f"--db-filter=^{db_name}$",
|
|
421
441
|
]
|
|
422
442
|
)
|
|
423
443
|
|
|
424
|
-
|
|
425
|
-
|
|
444
|
+
# Excluding no_ci modules from the tested-module *list* is not enough:
|
|
445
|
+
# they still install as transitive deps, and Odoo's button_upgrade
|
|
446
|
+
# cascades 'to upgrade' to downstream dependents of any upgraded target
|
|
447
|
+
# module (`-u modules`). A no_ci module that depends on a tested module
|
|
448
|
+
# thus gets re-upgraded and its post_install tests run. Exclude their
|
|
449
|
+
# tests explicitly via Odoo test-tags. The TagsSelector auto-adds
|
|
450
|
+
# '+standard' when only '-' filters are present, so this keeps running
|
|
451
|
+
# every standard test except those belonging to a no_ci module.
|
|
452
|
+
effective_test_tags = test_tags
|
|
453
|
+
if effective_test_tags is None and no_ci_modules:
|
|
454
|
+
effective_test_tags = ",".join(f"-/{m}" for m in no_ci_modules)
|
|
455
|
+
info(f"Excluding no_ci module tests via --test-tags: {effective_test_tags}")
|
|
456
|
+
|
|
457
|
+
if effective_test_tags:
|
|
458
|
+
test_cmd.extend(["--test-tags", effective_test_tags])
|
|
426
459
|
|
|
427
460
|
result = subprocess.run(test_cmd)
|
|
428
461
|
test_exit_code = result.returncode
|
|
@@ -575,6 +608,14 @@ def _terminate_connections(db_name: str, db_config: dict[str, str]) -> None:
|
|
|
575
608
|
)
|
|
576
609
|
|
|
577
610
|
|
|
611
|
+
def _module_dir(cfg, module: str) -> Path:
|
|
612
|
+
"""Return the directory of ``module`` — under addons/ or vendored/."""
|
|
613
|
+
for base in (cfg.addons_dir, cfg.vendored_dir):
|
|
614
|
+
if (base / module).is_dir():
|
|
615
|
+
return base / module
|
|
616
|
+
return cfg.addons_dir / module
|
|
617
|
+
|
|
618
|
+
|
|
578
619
|
def _get_addons_path(config_file) -> str:
|
|
579
620
|
"""Extract addons_path from odoo.conf."""
|
|
580
621
|
for line in config_file.read_text().splitlines():
|
|
@@ -18,11 +18,13 @@ from odoo_dev.config import (
|
|
|
18
18
|
from odoo_dev.utils.console import info, success, warning
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _prompt_for_versions() -> None:
|
|
22
|
-
"""
|
|
21
|
+
def _prompt_for_versions(non_interactive: bool = False) -> None:
|
|
22
|
+
"""Resolve ODOO_VERSION and PYTHON_VERSION.
|
|
23
23
|
|
|
24
|
-
Checks environment and .env file. If
|
|
25
|
-
and optionally saves to .env.
|
|
24
|
+
Checks environment and .env file. If unset and ``non_interactive`` is False,
|
|
25
|
+
prompts interactively and optionally saves to .env. If ``non_interactive``,
|
|
26
|
+
falls back to the defaults with no prompts (agentic/headless use) and records
|
|
27
|
+
them to .env silently.
|
|
26
28
|
"""
|
|
27
29
|
project_dir = find_project_root()
|
|
28
30
|
env_file = project_dir / ".env"
|
|
@@ -36,24 +38,28 @@ def _prompt_for_versions() -> None:
|
|
|
36
38
|
updates = {}
|
|
37
39
|
|
|
38
40
|
if not odoo_version:
|
|
39
|
-
odoo_version =
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
odoo_version = (
|
|
42
|
+
DEFAULT_ODOO_VERSION
|
|
43
|
+
if non_interactive
|
|
44
|
+
else typer.prompt("Odoo version", default=DEFAULT_ODOO_VERSION)
|
|
42
45
|
)
|
|
43
46
|
os.environ["ODOO_VERSION"] = odoo_version
|
|
44
47
|
updates["ODOO_VERSION"] = odoo_version
|
|
45
48
|
|
|
46
49
|
if not python_version:
|
|
47
|
-
python_version =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
python_version = (
|
|
51
|
+
DEFAULT_PYTHON_VERSION
|
|
52
|
+
if non_interactive
|
|
53
|
+
else typer.prompt("Python version", default=DEFAULT_PYTHON_VERSION)
|
|
50
54
|
)
|
|
51
55
|
os.environ["PYTHON_VERSION"] = python_version
|
|
52
56
|
updates["PYTHON_VERSION"] = python_version
|
|
53
57
|
|
|
54
|
-
#
|
|
58
|
+
# Persist anything we resolved. Non-interactive saves silently; interactive asks.
|
|
55
59
|
if updates:
|
|
56
|
-
if typer.confirm(
|
|
60
|
+
if non_interactive or typer.confirm(
|
|
61
|
+
"Save these settings to .env?", default=True
|
|
62
|
+
):
|
|
57
63
|
_update_env_file(env_file, updates)
|
|
58
64
|
success(f"Settings saved to {env_file}")
|
|
59
65
|
|
|
@@ -98,10 +104,27 @@ def setup(
|
|
|
98
104
|
"--clean",
|
|
99
105
|
help="Clean up before setup (remove existing venv, etc.)",
|
|
100
106
|
),
|
|
107
|
+
no_docker: bool = typer.Option(
|
|
108
|
+
False,
|
|
109
|
+
"--no-docker",
|
|
110
|
+
help="Skip Docker file setup and image build (local venv only).",
|
|
111
|
+
),
|
|
112
|
+
yes: bool = typer.Option(
|
|
113
|
+
False,
|
|
114
|
+
"--yes",
|
|
115
|
+
"-y",
|
|
116
|
+
help="Non-interactive: no prompts, use defaults. For agentic/headless use "
|
|
117
|
+
"(combine with --no-docker for a fully unattended local setup).",
|
|
118
|
+
),
|
|
101
119
|
) -> None:
|
|
102
|
-
"""Complete setup: clone Odoo repos, configure VSCode, build image.
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
"""Complete setup: clone Odoo repos, configure VSCode, build image.
|
|
121
|
+
|
|
122
|
+
For headless/agentic use run ``odoo-dev setup --no-docker --yes``: clones the
|
|
123
|
+
Odoo repos, builds the local venv and odoo.conf, and never prompts or touches
|
|
124
|
+
Docker.
|
|
125
|
+
"""
|
|
126
|
+
# Resolve versions (no prompts under --yes)
|
|
127
|
+
_prompt_for_versions(non_interactive=yes)
|
|
105
128
|
|
|
106
129
|
cfg = load_config()
|
|
107
130
|
|
|
@@ -128,9 +151,14 @@ def setup(
|
|
|
128
151
|
)
|
|
129
152
|
setup_venv()
|
|
130
153
|
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
# Docker setup — skipped entirely under --no-docker; auto-confirmed under --yes.
|
|
155
|
+
if no_docker:
|
|
156
|
+
info(
|
|
157
|
+
"Skipping Docker setup (--no-docker). Run 'odoo-dev build' later if needed."
|
|
158
|
+
)
|
|
159
|
+
elif yes or typer.confirm(
|
|
160
|
+
"\nDo you want to set up the Docker environment?", default=True
|
|
161
|
+
):
|
|
134
162
|
# Set up Docker files first
|
|
135
163
|
_setup_docker_files(cfg, community_only=community)
|
|
136
164
|
|
|
@@ -254,6 +282,22 @@ def vscode(cfg=None) -> None:
|
|
|
254
282
|
success("VSCode configuration set up successfully!")
|
|
255
283
|
|
|
256
284
|
|
|
285
|
+
def _fully_vendored(project_dir: Path) -> bool:
|
|
286
|
+
"""True if the repo is fully vendored: ``addons.lock`` present and no
|
|
287
|
+
``addons/`` symlink still points into ``.repos/``.
|
|
288
|
+
|
|
289
|
+
Such a repo needs no submodule checkout — its shared addons are committed real
|
|
290
|
+
files under ``vendored/``. A hybrid (still carries ``.repos`` symlinks) does
|
|
291
|
+
need its submodules, so this returns False there.
|
|
292
|
+
"""
|
|
293
|
+
from odoo_dev.vendor.verify import hybrid_submodule_addons
|
|
294
|
+
|
|
295
|
+
project_dir = Path(project_dir)
|
|
296
|
+
return (project_dir / "addons.lock").exists() and not hybrid_submodule_addons(
|
|
297
|
+
project_dir
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
257
301
|
def _init_submodules(cfg) -> None:
|
|
258
302
|
"""Initialize and update git submodules if present."""
|
|
259
303
|
gitmodules = cfg.project_dir / ".gitmodules"
|
|
@@ -261,6 +305,10 @@ def _init_submodules(cfg) -> None:
|
|
|
261
305
|
if not gitmodules.exists():
|
|
262
306
|
return
|
|
263
307
|
|
|
308
|
+
if _fully_vendored(cfg.project_dir):
|
|
309
|
+
info("Fully vendored (addons.lock, no .repos symlinks); skipping submodule init.")
|
|
310
|
+
return
|
|
311
|
+
|
|
264
312
|
success("Initializing git submodules...")
|
|
265
313
|
|
|
266
314
|
result = subprocess.run(
|
|
@@ -303,6 +351,7 @@ def _setup_odoo_config(cfg, community_only: bool = False) -> None:
|
|
|
303
351
|
[
|
|
304
352
|
str(cfg.project_dir / "design-themes"),
|
|
305
353
|
str(cfg.project_dir / "addons"),
|
|
354
|
+
str(cfg.project_dir / "vendored"),
|
|
306
355
|
]
|
|
307
356
|
)
|
|
308
357
|
|
|
@@ -360,7 +409,9 @@ def _setup_docker_files(cfg, community_only: bool = False) -> None:
|
|
|
360
409
|
_setup_docker_odoo_config(cfg, community_only=community_only)
|
|
361
410
|
|
|
362
411
|
|
|
363
|
-
def _generate_docker_odoo_conf(
|
|
412
|
+
def _generate_docker_odoo_conf(
|
|
413
|
+
community_only: bool = False, has_vendored: bool = False
|
|
414
|
+
) -> str:
|
|
364
415
|
"""Generate Docker-specific odoo.conf content."""
|
|
365
416
|
# Build addons path for Docker (using /opt/project paths)
|
|
366
417
|
addons_paths = [
|
|
@@ -378,6 +429,10 @@ def _generate_docker_odoo_conf(community_only: bool = False) -> str:
|
|
|
378
429
|
"/opt/project/addons",
|
|
379
430
|
]
|
|
380
431
|
)
|
|
432
|
+
# Only add vendored/ when the repo actually has it — an addons_path entry that
|
|
433
|
+
# doesn't exist makes Odoo warn on every boot.
|
|
434
|
+
if has_vendored:
|
|
435
|
+
addons_paths.append("/opt/project/vendored")
|
|
381
436
|
|
|
382
437
|
return f"""[options]
|
|
383
438
|
addons_path = {",".join(addons_paths)}
|
|
@@ -411,7 +466,12 @@ def _setup_docker_odoo_config(cfg, community_only: bool = False) -> None:
|
|
|
411
466
|
return
|
|
412
467
|
|
|
413
468
|
success("Creating Docker Odoo configuration file...")
|
|
414
|
-
|
|
469
|
+
has_vendored = (cfg.project_dir / "vendored").is_dir()
|
|
470
|
+
conf_file.write_text(
|
|
471
|
+
_generate_docker_odoo_conf(
|
|
472
|
+
community_only=community_only, has_vendored=has_vendored
|
|
473
|
+
)
|
|
474
|
+
)
|
|
415
475
|
conf_file.chmod(0o600)
|
|
416
476
|
success(f"Docker config file created at {conf_file}")
|
|
417
477
|
|