odoo-dev
A CLI tool for managing Odoo development environments. Handles local Python setup, Docker containers, database operations, and more.
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.
Installation
# Install with uv (recommended) — installs the published package from PyPI
uv tool install odoo-dev
# Or with pip
pip install odoo-dev
Then make sure the install location is on your PATH (uv prints the path; usually ~/.local/bin):
uv tool update-shell # or: export PATH="$HOME/.local/bin:$PATH"
Quick Start
# In your Odoo project directory
cd my-odoo-project
# Full setup: clone Odoo repos, create venv, install deps, configure VSCode
odoo-dev setup
# Or for community edition only
odoo-dev setup --community
setup reads ODOO_VERSION / PYTHON_VERSION from a .env in the project root (or
prompts for them and offers to save). It then clones the Odoo repos, builds a .venv,
installs system dependencies, and generates conf/odoo.conf. It will offer to set up
Docker at the end — answer "no" if you only want the local venv workflow.
Database setup (read this before your first run/test)
The generated conf/odoo.conf connects as PostgreSQL user odoo over the local
socket. A running PostgreSQL server is a prerequisite you provide yourself — on
every platform. setup installs only the PostgreSQL client and build dependencies
(macOS: libpq; Linux: postgresql-client + libpq-dev); it never installs, starts,
or configures a server, and never creates the odoo role. So on a fresh machine,
install a server, start it, and create the role once:
macOS (Homebrew):
brew install postgresql@18 # install a server (pick your version)
brew services start postgresql@18 # start it
createuser -s odoo # create the role odoo.conf expects
# Homebrew's versioned postgres is keg-only; add its bin to PATH if psql/createuser aren't found:
# export PATH="$(brew --prefix postgresql@18)/bin:$PATH"
Debian/Ubuntu:
sudo apt-get install postgresql # install a server if you don't already have one
sudo systemctl start postgresql
sudo -u postgres createuser -s odoo
Using a different / remote / Docker PostgreSQL: set DB_HOST, DB_PORT,
DB_USER, DB_PASSWORD in .env before running setup (it writes them into
conf/odoo.conf), or edit conf/odoo.conf directly. With no DB_HOST, odoo-dev
connects over the local socket as the odoo role.
Before launching, run/test/shell/update run a quick connection preflight: if
the server is unreachable, the role is missing, or authentication fails, you get a
specific one-line fix instead of a stack trace.
Commands
Local Development (default)
odoo-dev run # Start Odoo locally (default port 8069)
odoo-dev run -d mydb -p 8070 # Pick a database and HTTP port
odoo-dev run -d mydb -i base # Initialize module(s) on start
odoo-dev run -d mydb --dev reload # With hot reload
odoo-dev run --debug # With debugpy (VSCode attach on 5678)
odoo-dev shell mydb # Open an Odoo shell
odoo-dev update base -d mydb # Update modules
odoo-dev test my_module # Run a module's tests (coverage on by default)
odoo-dev test my_module --test-tags my_module --no-coverage
odoo-dev test # Auto-discover & test all addons in addons/ + vendored/
odoo-dev scaffold my_module # Create a new module
Note: the HTTP port flag is
-p/--port(not--http-port).
Database Operations
odoo-dev db list # List databases
odoo-dev db restore backup.zip # Restore from backup (neutralized by default)
odoo-dev db restore backup.zip mydb --no-neutralize
odoo-dev db drop mydb # Drop database
odoo-dev db neutralize mydb # Disable emails/crons
Docker (optional)
odoo-dev docker start # Start containers
odoo-dev docker stop # Stop containers
odoo-dev docker restart # Restart containers
odoo-dev docker logs # View logs
odoo-dev docker build # Rebuild image
odoo-dev docker shell mydb # Shell in container
odoo-dev docker psql # PostgreSQL shell
Setup Commands
odoo-dev setup # Full setup (interactive; offers Docker)
odoo-dev setup --community # Community edition only
odoo-dev setup --no-docker --yes # Headless/agentic: clone + venv + conf, no Docker, no prompts
odoo-dev setup-venv # Just create the venv (no repo clone)
odoo-dev vscode # Configure VSCode debugging
Vendored Addons
Shared addons can be vendored — materialized as real committed directories
under vendored/, pinned per-addon by addons.lock — instead of pulled in as
.repos/ git submodules. This makes each promotion a normal, reviewable file
diff and deploys as plain files (what Odoo.sh needs). vendored/ and addons/
are both on the addons path.
odoo-dev vendor migrate # Convert .repos submodule+symlink addons -> vendored/
odoo-dev vendor migrate --dry-run # Preview the pins without changing anything
odoo-dev vendor sync # Materialize vendored/ from addons.lock
odoo-dev vendor check # CI gate: verify vendored/ byte-matches the pins
odoo-dev vendor add fsm --source github.com/bemade/bemade-addons --version 18.0.1.3.2
odoo-dev vendor bump fsm --version 18.0.1.4.0 # Move a pin and re-materialize
odoo-dev vendor update # Pull newest upstream for all tracked addons
odoo-dev vendor update fsm --dry-run # Show what would update, change nothing
odoo-dev vendor develop fsm # Edit fsm against a live source clone
odoo-dev vendor develop fsm --stop # Leave develop mode (keeps the clone)
odoo-dev vendor status # Show vendored pins, symlinks, develop mode
Because vendored/<addon> is a materialized copy (re-sync clobbers it), you
don't edit it in place. vendor develop <addon> clones the addon's source repo
into a git-ignored .vendor-dev/, checks out a work branch at the pinned commit,
and prepends an overlay to your local conf/odoo.conf addons_path so Odoo
loads the live tree instead of the vendored copy (first path wins). vendored/
stays byte-for-byte pristine and only the local conf is touched, so nothing
dev-only can leak into a commit or into CI/prod. Edit in the clone, run/test in
this project's Odoo, commit + push upstream, then vendor bump once the new
version is tagged. --branch NAME names the work branch; --base REF bases it on
something other than the current pin.
vendor check verifies, per addon: the vendored files byte-match the pinned
commit; a version tag (if set) still resolves to that commit; every manifest
external_dependencies['python'] is named in requirements.txt; no addon
name collides between addons/ and vendored/; and no file under vendored/ is
gitignored. Python bytecode (__pycache__/, *.pyc) is excluded from the byte
comparison, so running the test suite doesn't turn the check red.
That last assertion catches a trap specific to migration: repo-wide ignore rules
(node_modules, package.json, …) are harmless while shared addons live in
submodules, but they start applying the moment those addons become real files
under vendored/ — and git add then skips the matching files without a word,
so the committed tree is incomplete while everything looks fine locally.
vendor migrate appends a !vendored/** negation to .gitignore when it
detects the situation. Pins themselves come from the gitlink the branch records,
not from whatever the submodule happens to be checked out at; migrate refuses to
run against uninitialized submodules.
vendor update is the pull side: each client owns its pins. For every addon
that tracks upstream — a version (bump to the newest <addon>/<version> tag) or
a branch (bump to its HEAD) — it moves the pin forward and re-materializes.
Addons pinned to a bare commit are left alone. Run it on a schedule (a client's own
CI, its own token, opening a vendor-bump/… MR) so shared-addon changes propagate
without any per-source push/fan-out machinery.
Project Structure
odoo-dev expects this project structure:
my-odoo-project/
├── .env # Optional: ODOO_VERSION, PYTHON_VERSION
├── addons/ # Your custom addons
├── vendored/ # Vendored shared addons (real files, pinned by addons.lock)
├── addons.lock # Per-addon pins for vendored/ (see `vendor`)
├── requirements.txt # Project-specific Python deps
├── odoo/ # Cloned by setup
├── enterprise/ # Cloned by setup (unless --community)
├── design-themes/ # Cloned by setup
├── .venv/ # Created by setup
└── conf/
└── odoo.conf # Created by setup
vendored/ and addons.lock are only present once a project adopts vendoring
(odoo-dev vendor migrate); submodule-based projects use .repos/ + symlinks
into addons/ instead.
Configuration
Create a .env file in your project root:
ODOO_VERSION=19.0
PYTHON_VERSION=3.12
# Optional — DB connection, written into conf/odoo.conf by `setup`.
# Omit DB_HOST/DB_PORT to use the local socket (the default). Set these to
# point at a remote / Docker / non-default PostgreSQL:
# DB_HOST=localhost
# DB_PORT=5432
# DB_USER=odoo
# DB_PASSWORD=odoo
Requirements
- Python 3.12+
- uv (recommended) or pip
- Git
- PostgreSQL (for local development — server + an
odoorole; see "Database setup") - Docker (optional, for containerized development)
Development
# Clone and install for development
git clone git@github.com:bemade/odoo-dev.git
cd odoo-dev
uv sync
# Run tests
uv run pytest # All tests
uv run pytest -m "not slow" # Fast tests only
# Build
uv build
License
LGPL-3. For complete license terms, visit https://www.gnu.org/licenses/lgpl-3.0.en.html
Release files for odoo-dev 1.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| odoo_dev-1.2.3.tar.gz | 80.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| odoo_dev-1.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 138.6 kB
Release files / odoo_dev-1.2.3.tar.gz
| Download URL | odoo_dev-1.2.3.tar.gz |
|---|---|
| Size | 80.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f09ec3f414df234203f4c7dd30f0a67293bd3cac1d8ce1c6c84af2748ba9823b
|
|
BLAKE2b-256 checksum How to use checksums |
e770279fd96439dc044226b293b37100acc602a8aa2e368c25327c09f8538f32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / odoo_dev-1.2.3-py3-none-any.whl
| Download URL | odoo_dev-1.2.3-py3-none-any.whl |
|---|---|
| Size | 58.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
be9a6155b57f4ad127b10e5083b874421db1a5bb2d7da5465a5985679766fafe
|
|
BLAKE2b-256 checksum How to use checksums |
7fe5a8a2e0481351f9f4bf068e704b239a211ec434e8ade2e2a1a6cd805413a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|