Jump to
↵select↑↓navigateescclose

Standards

Managed files

Every file odoo-nix's standards write, which module owns it, how each kind merges with what you wrote, the local regions, the retire rules, the checks sync reports, and each place odoo-nix changes OCA's rules.

Updated
Tags
  • standards
  • managed-files
  • sync
  • oca
  • pre-commit
On this page

odoo-nix sync --write writes the files below for the modules that are on, and sync --check reports any that differ. A module that turns off takes its files back out.

The files

FileModuleKind
flake.nixdev-shellblock inputs in the inputs set: odoo-nix.url, and standards-profile with an org profile
.envrcdev-shellwhole, local region envrc
.gitignoredev-shellblock dev-shell at the end: what the dev shell links or makes (/odoo.conf, /odools.toml, /.venv, /.devenv/, /.direnv/, /.odoo-nix/, /tools/.venv, /tools/.bin, /node_modules, /result, /result-*, __pycache__/, *.py[cod])
.editorconfigeditorconfigwhole, local region editorconfig; overridable
.ruff.tomlpython-lintwhole, local region ruff (it must open with a table header)
.pylintrc, .pylintrc-mandatorypylint-odoowhole
tools/pyproject.tomlevery module with a toolTOML merge: [project].dependencies (the locked tools of the modules that are on), [tool.uv] package = false, and [tool.coverage.run] and [tool.coverage.report] (tests)
tools/uv.lockevery module with a toollock: uv lock --project tools
package.jsonjs-xmlJSON merge: the devDependencies the hooks run
package-lock.jsonjs-xmllock: npm install --package-lock-only --ignore-scripts --no-audit --no-fund
prettier.config.cjs, eslint.config.cjsjs-xmlwhole
.vscode/settings.json, .vscode/extensions.jsonideJSONC merge (Editors)
checklog-odoo.cfgtestsseed: written once, then yours
.pre-commit-config.yamlevery module with a hookwhole, local regions hooks and exclude
.git-blame-ignore-revshygieneseed
committed.tomlcommitswhole, local region committed
.github/dependabot.ymldependabotwhole, local region updates
.github/zizmor.ymlworkflow-lintwhole; overridable
.github/workflows/ci.ymlciwhole, when the lint or the test gate is on: a lint module, or tests with an installable addon that is not untested (CI)
.github/workflows/pr-policy.ymlciwhole, only with commits or releases on (the pr-policy gate)
.github/workflows/deps.ymlciwhole
.github/workflows/release.ymlreleaseswhole, only where the checkout has a release branch
README.mdreadme with addons-tableblock addons-table at the end; overridable

"Overridable" means an org profile's templates/ may replace the template (Profiles).

The kinds

  • Whole: the file is odoo-nix's. It starts with a header comment. A file with a local region says odoo-nix:managed — generated by odoo-nix (`odoo-init --sync`); edit only inside the odoo-nix:local region., and anything you add goes in that region, between odoo-nix:local-begin <name> and odoo-nix:local-end <name>, which sync carries over byte for byte. A file without one (.pylintrc, .pylintrc-mandatory, prettier.config.cjs, eslint.config.cjs, .github/zizmor.yml, the workflow callers) says odoo-nix:managed — generated by odoo-nix (`odoo-init --sync`); do not edit. and takes no additions. Any edit outside a local region is drift.

  • Block: odoo-nix owns the lines between >>> odoo-nix: <name> (managed) >>> and <<< odoo-nix: <name> (managed) <<< (<!-- odoo-nix:begin <name> --> in Markdown) and nothing else in the file. A hand-written line the block now covers is absorbed into it on first opt-in when it is identical to the block's (a .gitignore line, an odoo-nix.url in flake.nix that already names the same URL), and a comment paragraph left empty by that goes too. A flake.nix input defined otherwise is refused instead (see First opt-in).

  • Merge (TOML, JSON, JSONC): odoo-nix owns the keys it writes and leaves every other one, its order and its comments alone. A pin you raised by hand in tools/pyproject.toml or package.json stays (a lower one is raised); a tool you added stays. JSONC files keep their indent, their inline arrays and a missing final newline.

  • Seed: written when missing, never rewritten.

  • Lock: tools/uv.lock and package-lock.json are checked against their manifests. --check reports a stale lock (exit 1); --write reruns the lock command; --write --offline names it and runs nothing. Dependabot never moves them: the series data does (Versioning).

Every hook in .pre-commit-config.yaml is language: system and runs a locked tool, never one from PATH: tools/.venv/bin/<tool> (from tools/uv.lock), node_modules/.bin/<tool> (from package-lock.json) or tools/.bin/<tool> (actionlint, zizmor, shellcheck, nixfmt, statix, deadnix, committed: the Nix tools of the odoo-nix the checkout pins). The dev shell links all three into the checkout and runs prek install -c .pre-commit-config.yaml; -c keeps prek from running a nested config of an addon.

First opt-in

A checkout often has files of its own where odoo-nix writes. For these sync refuses to replace yours on first opt-in, exits 2 and prints the diff with where your lines go:

FileWhere your lines go
flake.nixan odoo-nix or standards-profile input defined otherwise than the managed inputs block; --force replaces it with the block's
.pre-commit-config.yamlthe hooks local region; your exclude alternatives in the exclude region
.envrcthe envrc local region
.ruff.tomlpython-lint's extra-select, ignore, per-file-ignores, or the ruff region
.github/dependabot.ymlthe updates local region

Move them, then rerun with --force. A file odoo-nix's own scaffolding wrote (odoo-init's .envrc) is replaced without asking.

Retire rules

Files another tool's era left, deleted by sync while their module is on (retire-keep in the table keeps one):

FilesModuleWhy
.flake8, .isort.cfg, ruff.toml; a setup.cfg holding only the old linters' sectionspython-lintruff is configured in .ruff.toml
.prettierrc*, .eslintrc*, eslint.config.{js,mjs,ts}, prettier.config.{js,mjs}js-xmlprettier.config.cjs and eslint.config.cjs replace them
.travis.yml, .t2d.yml; an unmanaged .github/workflows/*.yml (or .yaml) that runs OCA's checks or pre-commit/action (it contains oca_run_tests, ghcr.io/oca/oca-ci or pre-commit/action); list one you keep in retire-keepcithe gates replace OCA's CI and the pre-commit run
oca_dependencies.txt, test-requirements.txtdev-shell (addons mode with externalSources)addons mode reads the external sources from flake.nix

An org profile adds its own with [[retire]].

Checks

Beside the files, sync --check (and the end of --write) reports these as drift (exit 1):

  • readme with required: an installable addon without README.md or README.rst;

  • ci in a project: a .gitmodules entry that does not follow branch = <series>;

  • packaging in addons mode: an addon without its whool pyproject.toml, or a nix/uv.lock that predates one (odoo project relock);

  • python-lint: a [tool.ruff] table in the config file, which ruff ignores once .ruff.toml exists;

  • the series sources disagreeing: .odoo-series (or flake.nix), the manifests' versions, flake.lock's ocb branch, an integration branch named <n>.0 that names another series.

Notices, which change no exit code: releases or packaging on in a project; ruff in the config file's [dependency-groups].dev (the hooks and the editor use the locked one); with ide on, python-envs.* in .vscode/settings.json (it overrides the interpreter); both shapes' signals without an explicit shape.

Refused before anything is written (exit 2): a series odoo-nix-tools has no data for; no tracked flake.nix; an addon repository without its addons-mode nix/pyproject.toml; the table in the other shape's file; addons.lockDir other than "nix"; releases with commits.convention = "odoo"; an [[tool.odoo-nix.untested]] entry naming no addon of the repository; releases under develop+series on a repository whose integration branch is itself a series branch (create develop and make it the default branch first).

Where odoo-nix changes OCA's rules

The lint and format rules are OCA's own, per series, from the series data each odoo-nix-tools release ships (odoo-nix data-path series/18.0.json). Every change odoo-nix makes to them is one of these deviations, applied in code and tested one by one:

IdAppliesChange
odoo-nix-pinsalwaysmanifestoo, checklog-odoo and manifestoo-core (no version in OCA's data), prek and coverage come from odoo-nix's own pins
drop-gen-readmealwaysno oca-gen-addon-readme: READMEs are Markdown, written by hand (OCA's own CI skips it too)
drop-excluded-addonsalwaysno oca-update-pre-commit-excluded-addons: odoo-nix renders the NOT INSTALLABLE ADDONS region from discovery
drop-external-depsalways (proposed, pending sign-off)no oca-gen-external-dependencies: nothing in odoo-nix's uv model reads the requirements.txt it writes
no-readme-fixseries 20.0oca-checks-odoo-module loses --fix (it can rename README.md to README.rst)
stage-namesseries 18.0commit/push/merge-commit become pre-commit/pre-push/pre-merge-commit
forbid-contributinghygiene.forbid-contributing = falsethe obsolete-dotfiles hook stops refusing CONTRIBUTING.md
addons-dirprojectsoca-fix-manifest-website gets --addons-dir=<addons root>
github-per-hookalways^.github/ leaves the top-level exclude; each OCA hook excludes it itself, so actionlint, zizmor and check-yaml see .github/
prettier-vscodealwaysprettier skips ^\.vscode/, so the editor's JSONC keeps its layout
system-languagealwaysevery hook is language: system and runs the locked executable; additional_dependencies, rev and default_language_version go
locks-excludedalwaysthe top-level exclude gains uv.lock, flake.lock and package-lock.json
esm-staticseries 18.0 and latereslint parses static/src/**/*.js and static/tests/**/*.js as ES modules
pylint-dedupealwaysa message listed twice in a .pylintrc* enable list is written once
required-authoraddon-manifest.author = ""no manifest-required-authors, and manifest-required-author is not enabled
node-runtimeseries 19.0 and 20.0node is the series preset's (nodejs_24), not OCA's default

With addon-manifest.website = "" the oca-fix-manifest-website hook is not rendered at all.