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.tomlfile 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, anddocs/session-prompt.mdso 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.
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 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.pyIf --manifest is omitted, the CLI looks for workspace.toml in the current
directory.
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 localDo 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.
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 syncmaterializes repo-addressable shared-addons inputs from[repos.shared_addons].url+refintosources/shared-addonswhen 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-devkititself. Tenant scaffolds keep[repos.runtime]pointed at the siblingodoo-devkitcheckout 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 syncmaterializes repo-addressed runtime inputs intosources/runtime, and non-local runtime commands fail closed until that checkout exists. - Odoo core is still inherited from the runtime image/tooling chain rather than materialized as a separate checkout.
workspace statuscompares deterministic generated surfaces, the manifest hash, source materialization, and the local lock snapshot.--checkexits 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.mdfor supplemental non-secret notes; symlinks and non-files fail the workspace check.AGENTS.override.mdis 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-devkitagainst the repo owned byodoo-devkititself:select,build,publish,up,down,inspect,logs,psql,odoo-shell,restore,workflow bootstrap,workflow init,workflow update, andworkflow 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 outsideplatform runtime. - Release actions such as ship, promote, and gate execution belong in
launchplane, not underplatform runtime. - non-local
workflow initandworkflow openupgraderemain local-only and fail closed with an explicit--instance localrequirement instead of falling through to an implicit remote path.
- The shared tenant compose database service stays pinned to
postgres:17while existing tenant DB volumes still use the legacy/var/lib/postgresql/datalayout. - The shared local compose contract includes the image-owned Launchplane runtime
addon root
/opt/launchplane/addonsand loadsbase,web,launchplane_runtime_healthas server-wide modules by default. Keep that addon root in the renderedODOO_ADDONS_PATHso startup scripts, generated Odoo config, and wrapper-normalized server commands agree. Startup shell phases normalize/opt/launchplane/addonsinto their generated config before running database updates so server-wide runtime health stays loadable even when downstream image layers overrideODOO_ADDONS_PATH./web/healthremains 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_nameanddbfilterto the configuredODOO_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 configuredODOO_ADMIN_LOGINis absent in a restored tenant database. This preserves boot for tenant databases that renamed or removed the defaultadminlogin while still checking active default admin passwords when matching users exist. - 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.
uv run python -m unittest discover -s testsFor 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 bootstrapNon-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.