Skip to content

Repository files navigation

odoo-devkit

odoo-devkit bootstraps tenant-focused Odoo workspaces from a tracked workspace.toml manifest.

The current workspace flow:

  • assembles a long-lived but rebuildable workspace under ~/Developer/odoo-workspaces/<tenant> by default,
  • treats the active tenant checkout as the source of truth for handwritten code,
  • emits a workspace.lock.toml file with the exact assembled refs,
  • inspects combined tenant/shared-addon uv workspaces before artifact publish,
  • generates a minimal runtime config scaffold under .generated/, and
  • generates workspace-root AGENTS.md, docs/README.md, and docs/session-prompt.md so Every Code, Codex Lab, and other coding-agent consumers can use the assembled workspace without turning each tenant repo into a copy of the shared operating guide, and
  • owns the pure PyCharm Odoo-conf helper and the starter templates for thin tenant overlays, and
  • writes PyCharm-visible shared run configurations for rare-but-important commands.

For remote environments, the stable lane model is testing plus prod. Launchplane-managed PR previews are a separate control-plane concern rather than a third durable runtime lane exposed through platform runtime. odoo-devkit can publish artifact images for handoff, but remote ship, promote, gate, restore, bootstrap, update, and preview lifecycle flow belongs in launchplane, not in branch-oriented odoo-devkit commands.

Command surface

uv run platform workspace sync --manifest /path/to/workspace.toml
uv run platform workspace status --manifest /path/to/workspace.toml
uv run platform workspace status --manifest /path/to/workspace.toml --check
uv run platform workspace scaffold-tenant-overlay \
  --output-dir /path/to/repo --tenant opw
uv run platform workspace scaffold-cockpit-root \
  --output-dir /path/to/workspace-root --force
uv run platform workspace clean --manifest /path/to/workspace.toml
uv run platform workspace run --manifest /path/to/workspace.toml -- pwd
uv run platform dependencies inspect --manifest /path/to/workspace.toml
uv run platform dependencies check --manifest /path/to/workspace.toml
uv run platform dependencies normalize --manifest /path/to/workspace.toml
uv run platform runtime select --manifest /path/to/workspace.toml
uv run platform runtime build --manifest /path/to/workspace.toml --no-cache
uv run platform runtime up --manifest /path/to/workspace.toml --build
uv run platform runtime down --manifest /path/to/workspace.toml --volumes
uv run platform runtime workflow --manifest /path/to/workspace.toml --workflow update
uv run platform runtime restore --manifest /path/to/workspace.toml
uv run platform runtime inspect --manifest /path/to/workspace.toml
uv run platform runtime logs --manifest /path/to/workspace.toml --service web --no-follow
uv run platform runtime psql --manifest /path/to/workspace.toml -- -c 'select 1'
uv run platform runtime odoo-shell --manifest /path/to/workspace.toml \
  --script tmp/scripts/example.py

If --manifest is omitted, the CLI looks for workspace.toml in the current directory.

Local Runtime Input

All platform runtime commands consume runtime values through one typed input: ODOO_DEVKIT_RUNTIME_ENVIRONMENT_JSON. The JSON object must contain the exact selected context, the exact selected instance, and a non-empty environment object whose keys and values are strings:

{
  "context": "replace-me",
  "instance": "local",
  "environment": {
    "ODOO_MASTER_PASSWORD": "<operator-local-value>",
    "ODOO_DB_USER": "odoo",
    "ODOO_DB_PASSWORD": "<operator-local-value>"
  }
}

Inject the payload from an operator-owned shell, password manager, or a mode-0600 file outside the repository before running runtime select, inspect, up, or local workflows. For example:

export ODOO_DEVKIT_RUNTIME_ENVIRONMENT_JSON="$(cat ~/.config/odoo-devkit/runtime-environment.json)"
uv run platform runtime inspect --manifest /path/to/workspace.toml --instance local

Do not put the payload or its values in workspace.toml, generated workspace docs, checked-in config, .env, platform/.env, or platform/secrets.toml. Missing input, context/instance mismatches, non-string values, and legacy devkit-local env/secrets files fail closed. Non-local Launchplane artifact workflows use the same typed payload envelope but include only artifact build inputs; they do not require or transmit database, master password, or other deployment-only secrets. A stack-required key is enforced during publish only when it is itself an artifact build input. Non-local mutation remains Launchplane-owned.

Scope

This repo is intentionally small. It owns the manifest/runtime contract for tenant workspaces and the local runtime commands needed to develop against those workspaces.

  • workspace sync materializes repo-addressable shared-addons inputs from [repos.shared_addons].url + ref into sources/shared-addons when the manifest does not point at a pre-existing local path.
  • The active tenant checkout remains path-based and is still the source of truth for handwritten tenant code.
  • Local runtime assets live in odoo-devkit itself. Tenant scaffolds keep [repos.runtime] pointed at the sibling odoo-devkit checkout so the same tracked tenant manifest can target local runtime work and artifact handoff without growing a repo-local remote mutation surface.
  • Runtime repo ownership remains explicit. When [repos.runtime] is present it may be path-based or repo-addressable, workspace sync materializes repo-addressed runtime inputs into sources/runtime, and non-local runtime commands fail closed until that checkout exists.
  • Runtime Odoo core is inherited from the image/tooling chain. For IDE analysis, workspace prepare-ide attaches an explicit, pinned community source checkout to the exact tenant project; see the workspace CLI guide.
  • workspace status compares deterministic generated surfaces, the manifest hash, source materialization, and the local lock snapshot. --check exits nonzero for stale guidance or materialization while reporting ordinary source commit/dirty changes on editable path-linked sources separately as informational baseline drift. Baseline drift on managed read-only checkouts fails the check.
  • Status exposes tenant, devkit, shared-addons, and distinct runtime roles with workspace-relative entrypoints, resolved paths, materialization type, and editability. Path-linked sources are edit roots; managed checkouts are not.
  • Use an optional regular-file workspace.local.md for supplemental non-secret notes; symlinks and non-files fail the workspace check. AGENTS.override.md is reserved full-replacement input in Codex Lab and its presence fails the normal workspace status check.

Current runtime ownership is intentionally narrow and explicit:

  • local runtime targets run natively inside odoo-devkit against the repo owned by odoo-devkit itself: select, build, publish, up, down, inspect, logs, psql, odoo-shell, restore, workflow bootstrap, workflow init, workflow update, and workflow openupgrade.
  • Non-local runtime mutation is not a devkit command surface. Stable remote lanes (testing, prod) route through Launchplane service APIs, operator UI, or reusable Launchplane workflows. PR preview lifecycle also stays outside platform runtime.
  • Release actions such as ship, promote, and gate execution belong in launchplane, not under platform runtime.
  • non-local workflow init and workflow openupgrade remain local-only and fail closed with an explicit --instance local requirement instead of falling through to an implicit remote path.

Runtime Contract Notes

  • The startup wrapper maps explicitly supplied ODOO_SMTP_SERVER, ODOO_SMTP_PORT, ODOO_SMTP_USER, ODOO_SMTP_PASSWORD, ODOO_SMTP_SSL, ODOO_EMAIL_FROM, and ODOO_FROM_FILTER to Odoo's native mail options. ODOO_SMTP_SSL=True selects STARTTLS; it is not implicit TLS on port 465. Launchplane owns hosted values and secret bindings. Empty supplied values clear inherited mail options; omitted values preserve the base config. The generated config containing credentials is readable only by its owner. Existing Odoo outgoing-server records take precedence over the config fallback. Restored databases that undergo sanitization get an active dummy outgoing server, following Odoo's neutralization behavior, so even configured SMTP fallback cannot send copied customer mail. Copied SMTP usernames/passwords are cleared. Fresh bootstrap does not insert that dummy server, so an empty new database can use the operator's explicitly supplied mail configuration.
  • An optional company_email in Launchplane's website-bootstrap payload sets the selected website company's sender address and verifies it was saved. Omission preserves the existing company email. This fixes the company sender used by native website contact forms without changing form submissions or sending email during bootstrap.
  • The shared tenant compose database service stays pinned to postgres:17 while existing tenant DB volumes still use the legacy /var/lib/postgresql/data layout.
  • The shared local compose contract includes the image-owned Launchplane runtime addon root /opt/launchplane/addons and loads base,web,launchplane_runtime_health as server-wide modules by default. Keep that addon root in the rendered ODOO_ADDONS_PATH so startup scripts, generated Odoo config, and wrapper-normalized server commands agree. Startup shell phases normalize /opt/launchplane/addons into their generated config before running database updates so server-wide runtime health stays loadable even when downstream image layers override ODOO_ADDONS_PATH. /web/health remains the local container liveness check; Launchplane runtime identity evidence is exposed by the base image at /launchplane/health.
  • Public single-database runtimes pin both db_name and dbfilter to the configured ODOO_DB_NAME, and keep database listing disabled. This keeps normal website requests on the public hostname bound to the tenant database instead of falling through to Odoo's database selector.
  • Public runtimes require ODOO_ADMIN_PASSWORD, but startup skips admin hardening when the configured ODOO_ADMIN_LOGIN is absent in a restored tenant database. This preserves boot for tenant databases that renamed or removed the default admin login while still checking active default admin passwords when matching users exist.
  • Startup verifies an existing administrator password before writing it. A matching configured password is left unchanged, avoiding password-change emails on ordinary restarts. Actual configured password changes still use Odoo's normal write path and security notifications. Quotes and backslashes in configured passwords are preserved when passed into the startup shell.
  • A Postgres major-version bump is not a routine dependency refresh on this surface. Treat it as explicit migration work with a documented upgrade path for existing tenant data volumes.
  • Dependabot should not propose Postgres major upgrades automatically for the root docker-compose.yml; those changes should be intentional operator work.

Testing

uv run python -m unittest discover -s tests

For tenant repos that keep instance = "local" in the tracked manifest, --instance testing or --instance prod is not a shortcut for remote mutation. Release and non-local data actions should run through launchplane. Local workflow example:

uv --directory ../odoo-devkit run platform runtime workflow \
  --manifest ./workspace.toml \
  --workflow bootstrap

Non-local artifact publishing runs through Launchplane's reusable workflow, which supplies the authoritative runtime-environment payload and records the resulting artifact.

platform runtime publish stages the tenant addons plus shared addons into a real build context, requires clean git worktrees for the repos it captures, pushes the resulting image, resolves the pushed digest, and emits a control-plane-compatible artifact manifest JSON file. When a repo-owned artifact-inputs.toml exists beside workspace.toml, runtime and publish treat that file as the repo-owned source-input contract. Runtime and publish no longer fall back to stack.toml source selector fields.

About

Shared DX and runtime contract for Odoo workspaces

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages