-
Notifications
You must be signed in to change notification settings - Fork 0
0.1 Config | CLI, declarative .env, cascade
Configuration resolution: where the parameters come from and in which order of priority. It replaces the logic of the original Bash
lib/cli.sh, whose semantics (not form) it reproduces. It lives insrc/cli.rs(flags),src/config.rs(cascade,.envparser, validators),src/prompt.rs(prompts) andsrc/secret.rs(password). The result is aContextthat the steps read.
Every parameter is resolved with this precedence (highest first):
1. CLI argument → 2. interactive prompt → 3. .env file → 4. final default
In Rust the distinction between “flag not passed” and “flag passed with the default value” is modelled
with Option<T>: Some(v) = explicitly supplied (at any value), None = not supplied. That replaces
Bash's CLI_*_SET booleans, carrying the same information in a typed way.
The instance name is resolved first, because everything else is named after it: the system user,
the PostgreSQL role, the database, the install dir, the unit, the config file, the helper. Those are
derived defaults, so an explicit value in a .env beats them — which is why a configuration
file for a named instance should name as little as possible, or it hands the second instance the
first one's user and database without a word. The gevent port is derived the same way, from
--port + 3, and refused if it equals it.
Resolution (ResolvedConfig::resolve) is a pure function: no I/O, no prompts. The interactive
prompts live in prompt.rs and fill a further layer that overlays the .env. If stdin/stdout are not
a TTY the prompts are skipped (CLI → .env → default).
| Parameter | CLI flag |
.env key |
Default | Validation |
|---|---|---|---|---|
| Version | --version |
ODOO_VERSION |
18.0 |
16|17|18|19 → N.0; or 16.0..19.0
|
| OS user | --odoo-user |
ODOO_USER |
odoo |
identifier ^[A-Za-z0-9._-]+$
|
| DB user | --db-user |
DB_USER |
= odoo-user | identifier |
| Port | --port |
ODOO_PORT |
8069 |
integer 1–65535 |
| DB name | --db-name |
DB_NAME |
odoo |
identifier (non-empty) |
| Install dir | --install-dir |
ODOO_INSTALL_DIR |
/opt/odoo/odoo<N> |
absolute, under /opt/odoo
|
| Admin password | --admin-passwd |
ODOO_ADMIN_PASSWD |
admin |
non-empty; admin requires confirmation |
| Nginx | --with-nginx |
WITH_NGINX |
false |
boolean |
| Config file | --config <FILE> |
— | — | an existing .env file |
ODOO_HOME is the constant /opt/odoo: an ODOO_HOME key in the .env produces a warning and is
ignored.
If --db-user is not passed, db_user = odoo_user (including with a customised odoo_user). An
explicit CLI value, or a DB_USER in the .env that differs from the odoo default, decouples
them.
Allowed only with an explicit interactive confirmation.
| Situation | Outcome |
|---|---|
password ≠ admin
|
fine, no confirmation |
password = admin, TTY present |
y/N confirmation prompt |
password = admin, non-interactive |
error + stop (it cannot be confirmed without a TTY) |
| empty password | error |
The password never reaches the logs: it is wrapped in a Secret type whose Debug is redacted
(Secret(****)), and it does not appear in the configuration summary.
It must be /opt/odoo or below. The check uses Path::starts_with (component-based), so
/opt/odoofoo is correctly outside the scope — more robust than Bash's string-prefix comparison.
18 and 18.0 are equivalent: internally 18.0 (full) is kept and 18 (short) is derived for file
and unit names.
Bash did source "$CONFIG_FILE": it executes the file as Bash code with root privileges — a
code-execution vector. The port does not reproduce that.
parse_env_file reads KEY=VALUE line by line:
- blank lines and comments (
#) are ignored; - an
exportprefix is tolerated; - quotes wrapping the value are stripped;
-
no command expansion, no
eval; - unknown keys → warning and ignore (never a failure).
A “dangerous” value such as ODOO_ADMIN_PASSWD=$(rm -rf /) is treated as a literal string and never
executed. A dedicated test proves it with a sentinel file that has to survive the parsing.
# production.env — example
ODOO_VERSION=18
ODOO_USER=odoo
ODOO_PORT=8069
DB_NAME=odoo
WITH_NGINX=trueparse CLI
└─▶ load .env (if --config) # parsed, never executed
└─▶ interactive prompts (if TTY) # only for fields not passed on the CLI
└─▶ resolve (cascade + validation, pure)
└─▶ confirm the 'admin' password if needed
└─▶ Context → preflight checks → lock → step execution
The configuration summary is printed before anything is mutated, and with --dry-run the flow stops
right after the plan.
-
The UI sits behind a boundary:
config.rsdoes not depend onprompt.rs. The rich UI (inquirefor prompts,indicatiffor progress) lives inprompt.rs/progress.rs, and the steps never talk to it — see 2. UI + dry-run. -
Typed errors (
ConfigError,thiserror) instead of Bash'sexit 1: invalid version, port out of range, install-dir out of scope, empty db_name,adminpassword non-interactively — each is an error, never a panic. - No
.unwrap()/.expect()in non-test code. - Resolution is covered by tests: the CLI > prompt > env > default cascade, the
db_userrule, the.envparser with its no-eval proof, the validations, the password hard stop, version normalisation.
Start here
Key concepts
References
For developers
Technical detail — how it works inside
Steps:
- 1.1 PrepareOptRoot
- 1.2 CreateOdooUser
- 1.3 SetupLogDir
- 1.3b SetupCacheDir
- 1.4 AptPackages (delta)
- 1.5 InstallWkhtmltopdf
- 1.6 SetupPostgres
- 1.7 CreateDbRole
- 1.8 CreateDatabase
- 1.9 CloneOdooRepo
- 1.10 CreateVirtualenv
- 1.11 InstallPythonRequirements
- 1.12 GenerateConfig
- 1.12b SetupDataDir
- 1.13 InitializeOdooDatabase
- 1.14 SetupSystemd
- 1.15 Nginx (6 sub-steps)
- 1.16 WriteControlScript + PatchBashrc
Cross-cutting: