Jump to
↵select↑↓navigateescclose

Scaffolding

A repository of addons

Develop a repository of Odoo addons on its own, OCA style, with OCB and every repository it depends on as flake inputs, a generated uv lock and a dev shell that installs all of its addons.

Updated
Tags
  • flake
  • addons
  • oca
  • externalSources
On this page

A repository of addons is what OCA publishes: a flat set of addon folders at the root, one branch per Odoo series, mounted by the projects that deploy them. Addons mode gives such a repository a development shell of its own. OCB and the repositories its addons depend on are flake inputs, the uv project is generated into nix/, and odoo db provision installs every addon in the repository.

The layout

.
├── flake.nix, flake.lock    OCB and the dependencies as inputs; pins
├── .odoo-series             the series this branch targets, e.g. 18.0
├── nix/
│   ├── pyproject.toml       generated by the relock; commit it
│   └── uv.lock              generated by the relock; commit it
├── my_addon/                your addons, at the root
├── my_other_addon/
└── .odoo-nix/               links the dev shell makes (gitignored)
    ├── odoo -> /nix/store/…-source
    └── modules/<name> -> /nix/store/…-source

The shell also links odoo.conf, odools.toml and .venv at the root, as in a project. templates/addons/ in odoo-nix has the flake.nix, .envrc, .gitignore and nix/pyproject.toml to start from, and nix run github:Avunu/odoo-nix -- --addons writes them into the repository (where they are missing), with .odoo-series, then runs the first relock. List the repositories the addons depend on in the flake before that relock, or run it again after (nix run .#relock). See Scaffolder.

The flake

{
  inputs = {
    odoo-nix.url = "github:Avunu/odoo-nix/release-2";
    nixpkgs.follows = "odoo-nix/nixpkgs";
    ocb = { url = "github:OCA/OCB/18.0"; flake = false; };
    oca-edi = { url = "github:OCA/edi/18.0"; flake = false; };
    oca-queue = { url = "github:OCA/queue/18.0"; flake = false; };
  };

  outputs = { odoo-nix, ... }@inputs:
    odoo-nix.lib.mkFlake { inherit inputs; } ({ ... }: {
      imports = [ odoo-nix.flakeModules.default ];
      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
      perSystem = { lib, ... }: {
        odoo-nix = {
          enable = true;
          name = "edi-addons";
          workspaceRoot = ./.;
          odooSeries = lib.fileContents ./.odoo-series;
          addons.enable = true;
          coreSource = inputs.ocb;
          externalSources = [
            { name = "edi"; src = inputs.oca-edi; }
            { name = "queue"; src = inputs.oca-queue; }
          ];
        };
      };
    });
}

List every repository the addons' depends reach, through other repositories too: a dependency that is in none of them is taken to be part of Odoo. Other repositories of addons are inputs the same way as OCA's, on their series branch.

First lock and daily use

nix flake lock
nix run .#relock            # writes nix/pyproject.toml and nix/uv.lock
git add flake.lock nix/
direnv allow                # or: nix develop --no-pure-eval
devenv up                   # PostgreSQL, Odoo, Mailpit
odoo db provision           # every installable addon here, and what it depends on
odoo test my_addon

The relock works in a clone whose shell has never started. It links OCB and the external sources under .odoo-nix/, seeds nix/pyproject.toml if there is none, and locks. Run odoo project relock again after any of these:

  • an input moves (nix flake update);

  • an addon gains a depends on a packaged module;

  • an addon gains an external_dependencies.python entry.

Nothing detects a forgotten relock: nix/pyproject.toml only changes when the relock runs, so the stale-lock audit has nothing to compare against, and a dependency missing from the lock fails when the addon is installed.

What addons mode changes

A projectAddons mode
layoutodoo, modules, custom.odoo-nix/odoo, .odoo-nix/modules, .
OCBa submodule, or coreSourcecoreSource, required
Module repositoriessubmodules under modules/externalSources, linked under .odoo-nix/modules/
pyproject.toml, uv.lockat the root, partly hand-writtenin nix/, generated
Install setmodules.txtevery installable addon at the root
odoo module add, add-bundle, remove, odoo project updatemanage submodules and modules.txtrefused, with what to do instead

Packaged modules of the external sources (an OCA addon's pyproject.toml) are built once as wheels from the store; your addons stay live on addons_path. Unpackaged ones, yours included, contribute their external_dependencies.python to the lock and are walked for their depends. Store paths are read-only, so editing an external repository means overriding its input, for example nix develop --override-input oca-edi path:../edi, which takes a snapshot each time the shell is entered.

externalSources without addons mode

externalSources works in a project too. It mounts a repository from a flake input instead of a submodule, so its pin lives in flake.lock. Keep the name the submodule had: the lock's paths stay the same. The shell links it as modules/<name>, a link into the store, so add /modules/<name> (no trailing slash) to .gitignore. Two roots must not both provide an installable module. If they do, evaluation fails and names the module and both roots; add the module to exclude on the entry it should not come from.