odoo-dev 1.0.0__tar.gz → 1.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.
- odoo_dev-1.2.0/.release-please-manifest.json +3 -0
- odoo_dev-1.2.0/CHANGELOG.md +50 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/PKG-INFO +55 -3
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/README.md +54 -2
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/pyproject.toml +1 -1
- odoo_dev-1.2.0/src/odoo_dev/__init__.py +3 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/cli.py +6 -0
- odoo_dev-1.2.0/src/odoo_dev/commands/bump.py +88 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/commands/run.py +16 -4
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/commands/setup.py +80 -20
- odoo_dev-1.2.0/src/odoo_dev/commands/vendor.py +278 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/config.py +10 -0
- odoo_dev-1.2.0/src/odoo_dev/utils/manifest.py +81 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/__init__.py +10 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/develop.py +306 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/edit.py +84 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/lock.py +86 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/materialize.py +130 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/migrate.py +209 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/sources.py +146 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/sync.py +33 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/update.py +105 -0
- odoo_dev-1.2.0/src/odoo_dev/vendor/verify.py +189 -0
- odoo_dev-1.2.0/tests/test_bump.py +61 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_cli.py +5 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_setup.py +125 -7
- odoo_dev-1.2.0/tests/test_vendor_develop.py +189 -0
- odoo_dev-1.2.0/tests/test_vendor_edit.py +106 -0
- odoo_dev-1.2.0/tests/test_vendor_lock.py +108 -0
- odoo_dev-1.2.0/tests/test_vendor_materialize.py +145 -0
- odoo_dev-1.2.0/tests/test_vendor_migrate.py +128 -0
- odoo_dev-1.2.0/tests/test_vendor_update.py +123 -0
- odoo_dev-1.2.0/tests/test_vendor_verify.py +180 -0
- odoo_dev-1.0.0/.release-please-manifest.json +0 -3
- odoo_dev-1.0.0/CHANGELOG.md +0 -19
- odoo_dev-1.0.0/src/odoo_dev/__init__.py +0 -3
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/.github/workflows/release-please.yml +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/.gitignore +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/.gitmodules +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/.python-version +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/release-please-config.json +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/commands/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/commands/db.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/commands/docker.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/preflight.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/docker/Dockerfile +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/docker/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/docker/docker-entrypoint.sh +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/vscode/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/vscode/launch.json +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/vscode/settings.json +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/templates/vscode/tasks.json +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/utils/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/src/odoo_dev/utils/console.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/fixtures/__init__.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_config.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_db.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_db_config.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_drop_database.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_integration.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_preflight.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_run.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/tests/test_setup_db.py +0 -0
- {odoo_dev-1.0.0 → odoo_dev-1.2.0}/uv.lock +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.2.0](https://github.com/bemade/odoo-dev/compare/v1.1.0...v1.2.0) (2026-07-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add 'vendor' command for per-addon vendoring ([7dfe8cb](https://github.com/bemade/odoo-dev/commit/7dfe8cbbf20031b012bc3326485f9aa3b634146c))
|
|
9
|
+
* **setup:** add --no-docker and --yes for headless agentic setup ([daa62c4](https://github.com/bemade/odoo-dev/commit/daa62c496ceb923152305091c5f94958014f1a51))
|
|
10
|
+
* **setup:** skip submodule init for fully-vendored repos ([83b7818](https://github.com/bemade/odoo-dev/commit/83b78186a0a23b3d33eb12a5364d312794315158))
|
|
11
|
+
* **vendor:** add `vendor develop` local dev-loop for vendored addons ([89325f8](https://github.com/bemade/odoo-dev/commit/89325f88a9a51bfc4d4cb4ea755381c55e62989a))
|
|
12
|
+
* **vendor:** add `vendor update` — the pull side of vendoring ([fb88c1e](https://github.com/bemade/odoo-dev/commit/fb88c1e3b62604f59932811b772cd5e420eb5152))
|
|
13
|
+
* **vendor:** add vendored/ to the addons_path when present ([0fdc512](https://github.com/bemade/odoo-dev/commit/0fdc512a9c9df2541028a8743d1afb33240ac6de))
|
|
14
|
+
* **vendor:** assert-no-hybrid guard in `vendor check --no-hybrid` ([d424eca](https://github.com/bemade/odoo-dev/commit/d424eca69fac72f27a44e24ea3ca68fe9e7b37af))
|
|
15
|
+
* **vendor:** test and cover vendored/ addons alongside local ones ([a989d17](https://github.com/bemade/odoo-dev/commit/a989d174cc6cc84da5cae7fad327c017cbaf8030))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **vendor:** compare python-dep package NAMES, not full spec strings ([6903fa3](https://github.com/bemade/odoo-dev/commit/6903fa307dfab977cb40b9d7dfee2d017f67a0ff))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
* document the vendor command + agentic setup flag ([523ac05](https://github.com/bemade/odoo-dev/commit/523ac050bcd103d18b99abc8cc3a50ab0c6886c4))
|
|
26
|
+
|
|
27
|
+
## [1.1.0](https://github.com/bemade/odoo-dev/compare/v1.0.0...v1.1.0) (2026-06-26)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
* 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))
|
|
33
|
+
|
|
34
|
+
## [1.0.0](https://github.com/bemade/odoo-dev/compare/v0.4.0...v1.0.0) (2026-06-11)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### ⚠ BREAKING CHANGES
|
|
38
|
+
|
|
39
|
+
* 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).
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
|
|
43
|
+
* 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))
|
|
44
|
+
|
|
45
|
+
## [0.4.0](https://github.com/bemade/odoo-dev/compare/v0.3.3...v0.4.0) (2026-06-11)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Features
|
|
49
|
+
|
|
50
|
+
* 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.
|
|
3
|
+
Version: 1.2.0
|
|
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:
|
|
@@ -5,7 +5,9 @@ from typing import Optional
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
from odoo_dev import __version__
|
|
8
|
+
from odoo_dev.commands import bump as bump_cmd
|
|
8
9
|
from odoo_dev.commands import db, docker, run, setup
|
|
10
|
+
from odoo_dev.commands import vendor as vendor_cmd
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def _version_callback(value: bool) -> None:
|
|
@@ -39,6 +41,7 @@ def _main(
|
|
|
39
41
|
# Command groups
|
|
40
42
|
app.add_typer(db.app, name="db")
|
|
41
43
|
app.add_typer(docker.app, name="docker")
|
|
44
|
+
app.add_typer(vendor_cmd.app, name="vendor")
|
|
42
45
|
|
|
43
46
|
# Setup commands
|
|
44
47
|
app.command()(setup.setup)
|
|
@@ -52,6 +55,9 @@ app.command()(run.update)
|
|
|
52
55
|
app.command()(run.test)
|
|
53
56
|
app.command()(run.scaffold)
|
|
54
57
|
|
|
58
|
+
# Versioning
|
|
59
|
+
app.command()(bump_cmd.bump)
|
|
60
|
+
|
|
55
61
|
|
|
56
62
|
def main() -> None:
|
|
57
63
|
"""Entry point for the CLI."""
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Bump an Odoo module's manifest version."""
|
|
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, info, success
|
|
11
|
+
from odoo_dev.utils.manifest import (
|
|
12
|
+
bump_version_string,
|
|
13
|
+
find_module_root,
|
|
14
|
+
manifest_path,
|
|
15
|
+
read_version,
|
|
16
|
+
set_version,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def bump(
|
|
21
|
+
module: Annotated[
|
|
22
|
+
str,
|
|
23
|
+
typer.Argument(help="Module name (under addons/) or path to a module dir"),
|
|
24
|
+
],
|
|
25
|
+
level: Annotated[
|
|
26
|
+
str, typer.Argument(help="Bump level: patch | minor | major")
|
|
27
|
+
] = "patch",
|
|
28
|
+
no_stage: Annotated[
|
|
29
|
+
bool, typer.Option("--no-stage", help="Do not git add the changed manifest")
|
|
30
|
+
] = False,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Bump a module's __manifest__.py version (patch | minor | major).
|
|
33
|
+
|
|
34
|
+
Series-agnostic: only the trailing major.minor.patch segments move, never the
|
|
35
|
+
Odoo series prefix. Edits the manifest in place and stages it, so the bump
|
|
36
|
+
lands in the commit you're about to make (which the version-bump check
|
|
37
|
+
requires for any module you've changed).
|
|
38
|
+
"""
|
|
39
|
+
if level not in ("patch", "minor", "major"):
|
|
40
|
+
error(f"level must be one of patch|minor|major (got {level!r})")
|
|
41
|
+
raise typer.Exit(2)
|
|
42
|
+
|
|
43
|
+
# Resolve the module: an explicit path, else a name under addons/.
|
|
44
|
+
candidate = Path(module)
|
|
45
|
+
root = find_module_root(candidate) if candidate.exists() else None
|
|
46
|
+
if root is None and candidate.is_dir():
|
|
47
|
+
root = candidate
|
|
48
|
+
if root is None:
|
|
49
|
+
cfg = load_config()
|
|
50
|
+
under_addons = cfg.addons_dir / module
|
|
51
|
+
if under_addons.exists():
|
|
52
|
+
root = under_addons
|
|
53
|
+
else:
|
|
54
|
+
error(
|
|
55
|
+
f"No module found for {module!r} "
|
|
56
|
+
f"(checked the path and {cfg.addons_dir}/{module})"
|
|
57
|
+
)
|
|
58
|
+
raise typer.Exit(1)
|
|
59
|
+
|
|
60
|
+
mf = manifest_path(root)
|
|
61
|
+
if mf is None:
|
|
62
|
+
error(f"No __manifest__.py in {root}")
|
|
63
|
+
raise typer.Exit(1)
|
|
64
|
+
|
|
65
|
+
# Resolve through any addons/ -> .repos/ symlink so we edit and stage the
|
|
66
|
+
# real file in whichever repo (parent or submodule) actually owns it.
|
|
67
|
+
real_mf = mf.resolve()
|
|
68
|
+
text = real_mf.read_text()
|
|
69
|
+
current = read_version(text)
|
|
70
|
+
if current is None:
|
|
71
|
+
error(f"No version string in {real_mf}")
|
|
72
|
+
raise typer.Exit(1)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
new_version = bump_version_string(current, level)
|
|
76
|
+
except ValueError as exc:
|
|
77
|
+
error(str(exc))
|
|
78
|
+
raise typer.Exit(1)
|
|
79
|
+
|
|
80
|
+
real_mf.write_text(set_version(text, new_version))
|
|
81
|
+
success(f"{root.name}: {current} -> {new_version}")
|
|
82
|
+
|
|
83
|
+
if not no_stage:
|
|
84
|
+
result = subprocess.run(
|
|
85
|
+
["git", "add", real_mf.name], cwd=real_mf.parent, check=False
|
|
86
|
+
)
|
|
87
|
+
if result.returncode != 0:
|
|
88
|
+
info(f"(could not git add {real_mf}; stage it yourself)")
|
|
@@ -268,7 +268,9 @@ def test(
|
|
|
268
268
|
|
|
269
269
|
# Determine modules to test
|
|
270
270
|
if modules is None:
|
|
271
|
-
# Use manifestoo to list addons
|
|
271
|
+
# Use manifestoo to list addons across addons/ and vendored/, so a no-args
|
|
272
|
+
# run tests all custom AND vendored addons together (catches vendored
|
|
273
|
+
# addons that break when combined with local addons or with each other).
|
|
272
274
|
manifestoo_cmd = [
|
|
273
275
|
str(venv_python),
|
|
274
276
|
"-m",
|
|
@@ -276,6 +278,8 @@ def test(
|
|
|
276
278
|
"--select-addons-dir",
|
|
277
279
|
str(cfg.addons_dir),
|
|
278
280
|
]
|
|
281
|
+
if cfg.vendored_dir.is_dir():
|
|
282
|
+
manifestoo_cmd.extend(["--select-addons-dir", str(cfg.vendored_dir)])
|
|
279
283
|
if exclude:
|
|
280
284
|
manifestoo_cmd.extend(["--select-exclude", exclude])
|
|
281
285
|
manifestoo_cmd.extend(["list", "--separator=,"])
|
|
@@ -287,7 +291,7 @@ def test(
|
|
|
287
291
|
)
|
|
288
292
|
modules = result.stdout.strip()
|
|
289
293
|
if not modules:
|
|
290
|
-
error(f"No addons found in {cfg.addons_dir}")
|
|
294
|
+
error(f"No addons found in {cfg.addons_dir} or {cfg.vendored_dir}")
|
|
291
295
|
raise typer.Exit(1)
|
|
292
296
|
|
|
293
297
|
# Clone external repo dependencies declared in repo_deps.yaml and symlink into addons_dir
|
|
@@ -387,9 +391,9 @@ def test(
|
|
|
387
391
|
success(f"Running tests: {modules}")
|
|
388
392
|
|
|
389
393
|
if coverage:
|
|
390
|
-
# Build coverage source paths
|
|
394
|
+
# Build coverage source paths (a module may live in addons/ or vendored/)
|
|
391
395
|
coverage_source = ",".join(
|
|
392
|
-
str(cfg
|
|
396
|
+
str(_module_dir(cfg, m)) for m in modules.split(",")
|
|
393
397
|
)
|
|
394
398
|
|
|
395
399
|
test_cmd = [
|
|
@@ -575,6 +579,14 @@ def _terminate_connections(db_name: str, db_config: dict[str, str]) -> None:
|
|
|
575
579
|
)
|
|
576
580
|
|
|
577
581
|
|
|
582
|
+
def _module_dir(cfg, module: str) -> Path:
|
|
583
|
+
"""Return the directory of ``module`` — under addons/ or vendored/."""
|
|
584
|
+
for base in (cfg.addons_dir, cfg.vendored_dir):
|
|
585
|
+
if (base / module).is_dir():
|
|
586
|
+
return base / module
|
|
587
|
+
return cfg.addons_dir / module
|
|
588
|
+
|
|
589
|
+
|
|
578
590
|
def _get_addons_path(config_file) -> str:
|
|
579
591
|
"""Extract addons_path from odoo.conf."""
|
|
580
592
|
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
|
|