-
Notifications
You must be signed in to change notification settings - Fork 0
Multi distribution support
How the same installer works on Ubuntu, Debian and Fedora without the steps knowing which distribution they are running on. If you are looking for “what do I install”, you want the Quick start; this page explains the how, and it is the page to read before adding a family or touching a package name.
| Family | Minimum | Exercised in CI, full cycle |
|---|---|---|
| Ubuntu | ≥ 22.04 | 22.04, 24.04 (native runners) |
| Debian | ≥ 11 | 11 and 12 (containers — a probe: without systemd as PID 1 it stops at setup-postgres) |
| Fedora | ≥ 40 | 41 and 44 (privileged container with systemd as PID 1) |
“Exercised” has a precise meaning here: the integration CI really installs, checks that Odoo answers
over HTTP, then runs invok rollback and checks that nothing is left. The thresholds are open
upwards: a newer release is accepted with a warning, never refused — a refusal without evidence
blocks the good case, and a blocked installation is a certain harm while the avoided one was
hypothetical.
The divergence between families is not one thing but two, and keeping them apart is what prevents an abstraction that abstracts two different things.
| Boundary | Answers | Lives in |
|---|---|---|
PackageManager |
which commands install and remove, and what the packages are called here |
src/packaging/ (apt.rs, dnf.rs) |
Distro |
where the files live, which concepts exist, which tool governs the firewall |
src/distro/ (debian.rs, fedora.rs) |
Neither is a second door onto the system: both are obtained from SystemOps (ops.packages(),
ops.distro()), so what touches the machine stays in one place and the tests still mock only one
thing.
Steps never match on the distribution. They ask a boundary, and the boundary answers for the family
being run on.
OsFamily is persisted in the manifest and re-read by the rollback command. It is not re-detected
when the rollback starts, and it is not inferred from “which package manager is installed”: the
manifest might describe an installation made elsewhere, and a machine with both managers would answer
wrongly in silence. It is the same rule that governs artifact ownership — who created what is
re-read from the manifest, not re-derived from the system.
| Aspect | Debian / Ubuntu | Fedora |
|---|---|---|
| Manager |
apt-get / dpkg
|
dnf / rpm
|
| PostgreSQL marker | postgresql |
postgresql-server (the client is not enough to say the server is there) |
| PostgreSQL cluster | created by the package's postinst |
does not exist until you create it: needs postgresql-setup --initdb
|
| wkhtmltopdf |
.deb per codename (jammy, bullseye, bookworm) |
the fedora37 .rpm, the only one built for this family in the pinned release |
| Nginx vhost |
sites-available + symlink in sites-enabled
|
conf.d/*.conf, no symlink
|
| Nginx default site | the sites-enabled/default file/symlink, removed and restored by the rollback |
not a file: a block inside nginx.conf, and it is left alone |
| SELinux | not applicable |
httpd_can_network_connect must be turned on, or the proxy answers 502 with a valid nginx -t
|
| Firewall | ufw |
firewalld |
Two consequences worth reading in full, because they are choices and not details:
-
The Nginx default site on Fedora is not removed. There the default server is a
server { listen 80 default_server; … }block inside/etc/nginx/nginx.conf: removing it would mean rewriting the main configuration of a customer's service. The practical effect, declared: on a Fedora with a freshly installed Nginx, a request to a hostname that does not matchNGINX_SERVER_NAMEstill gets the welcome page instead of Odoo. -
The PostgreSQL cluster is a state axis of its own (the fourth in
setup-postgres): if we initialised it, the rollback can remove it — but only with--aggressive-rollbackand only if it hosts no other databases. An empty data directory is an inert leftover; somebody else's data is not.
One need (“we want the libjpeg headers”) has different names on different distributions, and sometimes different names on different releases of the same distribution. So each family's catalogue is not a list of names but a list of alternative groups, resolved at runtime in this order:
- an alternative that is already installed wins (we do not add
libtiff5-devto a machine that haslibtiff-dev, and the delta stays honest); - otherwise the first one with a real candidate;
- otherwise the first one that is installable anyway — a purely virtual name, used only as a fallback, because a virtual name cannot be removed and a delta containing one would lie to the rollback.
If no alternative is available the error arrives in the snapshot, before mutating, with the unresolved group in the message. And if the package index cannot be queried, the message says so rather than sending you off to look for the rename of a package that is sitting perfectly where it always was: blindness is not absence.
A test ties the two catalogues together: every declared need must be covered by both families. Without it, a dependency gets added to one only and the gap surfaces when a VM stops compiling — the most expensive place possible.
Odoo pins gevent and greenlet per Python version. On an interpreter newer than its pins there is
no prebuilt wheel: pip tries to build from source and the generated C does not survive a newer CPython's
headers. That is the case on Fedora ≥ 43, where the system python3 is 3.14.
The installer's rule, in one line:
the system
python3if Odoo's pins cover it, otherwise the newest interpreter packaged by the distribution that they do cover.
On Fedora 44 that means python3.13 (with its headers), installed for the occasion and removed by
the rollback like any other package in the delta — it is brought in by
install-system-dependencies, whose undo purges, and not by bootstrap-prerequisites, which leaves
what it adds installed.
On Debian and Ubuntu nothing changes: the base repositories have one Python and it is inside the pins. If one day it were not, the installer does not refuse: it warns at preflight, and if pip fails to build it says the cause is the Python version — not the build environment, not a missing package.
Everything downstream of the virtualenv knows nothing about this choice: the systemd unit, the control
script and the database initialisation all go through <venv>/bin/python3, which the venv creates under
that name whatever the base interpreter was.
The integration CI matrix is designed so that every diverging branch is executed by someone:
| Job | What it exercises that the others do not |
|---|---|
native (Ubuntu 22.04 / 24.04) |
full life cycle on a non-pristine machine (preinstalled packages that must survive) |
container (Debian 11 / 12) |
package names and the wkhtmltopdf pin on genuinely minimal images |
fedora:41 |
the rpm family with a system Python covered by the pins |
fedora:44 |
the rpm family with the alternative interpreter (the other branch of the choice) |
fedora:41 / nginx |
firewalld and SELinux — the only place they really run |
nginx (Ubuntu) |
the two natures of the default site, restored byte for byte |
preexisting-user |
an odoo user already present, with and without a home |
interrupt |
a real SIGINT during the installation |
What is not covered by a real run is declared where it lives rather than hidden: currently the precondition on a divergent PGDATA (a machine with a drop-in that moves the cluster) lives only in the mock tests.
-
OsFamily(a new variant) and the threshold inchecks::validate_os; - a
PackageManagerbackend with its catalogue — every need, or the parity test fails; - a
Distrobackend with the Nginx layout, firewall, SELinux and PostgreSQL cluster; - the TOFU wkhtmltopdf pins for that family's packages (fail-closed: no pin, no install);
- an entry in the
integration.ymlmatrix, because a family nobody runs is a promise, not a feature.
The Step trait is not touched: if it seems necessary, that is the signal that the divergence has
ended up in the wrong place.
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: