Either the installation succeeds completely, or the system goes back to exactly what it was.
Run it with no arguments and it asks.
The prompts filled in — version, user, database, port, install directory, master password, nginx — and then the installation starting. Everything on screen is the installer's own output.
Install · Configuration · Rollback · Uninstall · Security · Wiki
Installer for Odoo 16 / 17 / 18 / 19 on Ubuntu ≥ 22.04, Debian ≥ 11 and Fedora ≥ 40, written in
Rust, with transactional rollback. It sets up the system user, dependencies, PostgreSQL, the
Odoo sources, a virtualenv, the config file, a systemd service, optionally Nginx, and an odoo helper
command.
The command is invok. The .deb and .rpm packages also install the short alias vok, a
symlink to the same program: vok --dry-run and invok --dry-run do exactly the same thing.
Invok — from “invoke”: to call something into being.
Independent project. Not affiliated with Odoo S.A., nor endorsed or sponsored by it. “Odoo” is a trademark of Odoo S.A. and is used here only to name the software this tool installs. The installer does not redistribute Odoo code: it downloads it at runtime from the official
odoo/odoorepository, onto the target machine.
Full technical documentation — engine, step-by-step reference, rollback model, multi-distro support — lives in the wiki.
- Surgical, verified rollback — if a step fails, the steps already executed are undone in reverse order, and resources that were already on the machine are never touched. It is proven by end-to-end tests and by a CI job that installs and uninstalls on real machines.
- One binary, no runtime — a native executable; git, the package manager, psql and venv stay external commands.
- Three families, no scattered
ifs —aptanddnfsit behind two boundaries, and no step knows which distribution it runs on. - Resumable, and never destructive with itself — an interrupted installation resumes where it
stopped; a completed one is never silently overwritten. The
.envfile is parsed declaratively, never executed as code. - Interruptible — Ctrl-C rolls back and restores the system instead of leaving it half-done.
- One flow, two modes — guided (interactive prompts) or non-interactive (
--config/flags/CI).
| Requirement | Detail |
|---|---|
| OS | Ubuntu ≥ 22.04, Debian ≥ 11 or Fedora ≥ 40 — exercised in CI up to Ubuntu 24.04, Debian 12 and Fedora 44 (full cycle: install, service up, rollback). A newer release is accepted with a warning, not refused |
| Odoo version × OS | 16, 17, 18 and 19 install on all three families. On Fedora, Odoo 16 needs an interpreter of its own — the installer picks it: see the note below |
| Python | the installer picks the interpreter: the system one when Odoo's pins cover it, otherwise the newest interpreter packaged by the distribution that they do cover. On Fedora ≥ 43 the venv is built on python3.13, installed for the occasion and removed by the rollback |
| Privileges | a normal user with sudo (not a direct root login) |
| Disk | ≥ 5 GB free (override with MIN_DISK_GB) |
| Ports | 8069 free; 80/443 too when using Nginx — unless it is Nginx itself holding them, which is not a conflict |
Odoo 16 on Fedora builds its virtualenv on Python 3.12, and the reason is upstream rather than here. Odoo 16's newest
geventpin is written forpython_version >= '3.12'with nothing above it, so past 3.12 pip keeps selectinggevent==24.2.1, whose newest prebuilt wheel iscp312: it would have to compile, and the C generated for an older CPython does not survive 3.13's headers. Odoo 17, 18 and 19 pingevent==24.11.1for>= '3.13', which ships a wheel for the interpreter every current Fedora uses.So the ceiling the installer applies is per Odoo version, not one number for all of them: 3.12 for 16, 3.13 for the rest. On Fedora it installs
python3.12for the occasion — packaged by 41 through 44 — builds the venv on it, and the rollback takes it back with the rest of the delta. On Ubuntu and Debian nothing changes: their system Python is already below both ceilings.If a combination ever falls outside what the pins cover, the installer does not pretend otherwise: the failure names the interpreter and prints the lines that Odoo version declares, instead of leaving you with the compiler's output alone.
ODOO_HOME is the constant /opt/odoo and cannot be overridden.
Same program in every case, and in every case Odoo itself is installed at runtime, when you run the command. The difference that matters is who compiles: A, B and C give you a ready-made static musl binary with no dependencies; D and E build it on your machine.
| For | ||
|---|---|---|
| A | any distro, nothing to install | .tar.gz |
| B | Ubuntu / Debian, command in PATH + vok alias |
.deb |
| C | Fedora, same | .rpm |
| D | you already have Rust | cargo install |
| E | from a clone of this repository | cargo build |
Updates come from the Releases page: every version is there with its
sha256, and you update by downloading again. There is noapt/dnfrepository to add to the machine's sources.
The commands below point at v3.3.0, the release this README describes. If a newer one exists, find it on Releases and change the version in the URLs and file names.
Two Linux x86_64 variants: …-musl.tar.gz is static and runs anywhere (recommended);
…-gnu.tar.gz is dynamic, for systems with a recent glibc. Each archive ships a .sha256.
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok-x86_64-unknown-linux-musl.tar.gz
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok-x86_64-unknown-linux-musl.tar.gz.sha256
sha256sum -c invok-x86_64-unknown-linux-musl.tar.gz.sha256 # must say: OK
tar xzf invok-x86_64-unknown-linux-musl.tar.gz
./invok -V # which version is this
sudo ./invok # guided (interactive)
sudo ./invok --config production.env --with-nginx # or non-interactivePuts invok in PATH, removable with apt remove invok. It ships only the CLI binary: no
services, no system changes.
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok_3.3.0-1_amd64.deb
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok_3.3.0-1_amd64.deb.sha256
sha256sum -c invok_3.3.0-1_amd64.deb.sha256 # must say: OK
sudo apt install ./invok_3.3.0-1_amd64.deb
invok -V # which version is installed
sudo invok # now on PATH — `sudo vok` is the same programThe package creates /usr/bin/vok as a link to /usr/bin/invok. If a /usr/bin/vok already exists
and is not a link, the alias is skipped and the installation says so: someone else's file does not
get overwritten.
The same binary in the other wrapper. Removable with sudo dnf remove invok.
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok-3.3.0-1.x86_64.rpm
curl -fsSL -O https://github.com/Omisen/invok/releases/download/v3.3.0/invok-3.3.0-1.x86_64.rpm.sha256
sha256sum -c invok-3.3.0-1.x86_64.rpm.sha256 # must say: OK
sudo dnf install ./invok-3.3.0-1.x86_64.rpm
invok -V
sudo invokD — Any distro with Rust: cargo install
The crate is on crates.io.
cargo install invok
sudo "$(command -v invok)" # cargo installs into ~/.cargo/bin, which root has no PATH entry forThis is the path people get wrong, so: cargo install compiles from source (options A, B and C
hand you the same executable ready-made), the binary lands in ~/.cargo/bin/ which is not on root's
PATH — a bare sudo invok answers command not found — and there is no vok alias, which only B
and C create. Update with cargo install invok --force.
E — Any distro: build from source
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Rust toolchain, once
source "$HOME/.cargo/env"
git clone https://github.com/Omisen/invok.git && cd invok
cargo build --release # → target/release/invok
sudo ./target/release/invokRun it via sudo from a normal user: SUDO_USER becomes the owner of the odoo helper command.
Parameters resolve in this order: CLI → interactive prompt → .env → default. The prompt sits
above the file on purpose: a value in the .env is offered as the suggested default, so whoever is
at the terminal sees it and has the last word. Only a value passed on the CLI is never asked for
at all — and with no TTY there are no prompts, so the cascade is CLI → .env → default.
| Flag | Value | Default |
|---|---|---|
--version |
16 | 17 | 18 | 19 (or NN.0) |
18.0 |
--instance |
name of this instance — see below | none (the historical names) |
--odoo-user |
system user | odoo |
--db-user |
PostgreSQL role | = --odoo-user |
--db-password |
password for the DB role | empty → peer authentication |
--port |
HTTP port (1–65535) | 8069 |
--gevent-port |
longpolling/websocket port | --port + 3 (so 8072) |
--db-name |
database name | odoo |
--install-dir |
install directory (must live under /opt/odoo) |
/opt/odoo/odoo<N> |
--admin-passwd |
Odoo master password | admin (discouraged) |
--with-nginx |
Nginx reverse proxy | off |
--server-name |
server_name of the Nginx vhost |
_ (catch-all) |
--config <FILE> |
load a .env file (declarative) |
— |
--dry-run |
print the plan without changing anything | off |
--force |
install even if a manifest exists, archiving it instead of overwriting | off |
Also: --install-dir, --logfile, --open-https-port (opens 443 on the firewall ahead of TLS; it
does not configure TLS — legacy alias --enable-ssl), --aggressive-rollback, and
-V/--installer-version for the installer version (--version is Odoo's). --help and the
wiki have them all. Subcommands: invok rollback (alias uninstall) and invok list, below; with no
subcommand, the command installs.
sudo invok --version 17 --with-nginx
sudo invok --with-nginx --server-name '[your-domain.example.com]'
sudo invok --version 18 --db-name odoo --port 8069 --admin-passwd '[YOUR_ADMIN_PASSWORD]'By default the installer names its artifacts after the Odoo version: the unit is odoo18, the
sources live in /opt/odoo/odoo18, the system user and the database are both odoo. That is what
every release so far has written on disk, and passing no --instance keeps every one of those names
exactly as it is.
Run with no arguments and the form asks for it first, with an empty default:
── Odoo installation settings ──
? Instance name (empty = the historical instance)
a second Odoo beside an existing one needs a name; leave empty for the first
Leaving it empty is a real answer and changes nothing. Naming one moves the suggestions that follow
with it — the system user, the database and the install directory are proposed as odoo-cliente-x
rather than odoo — which is why it is asked first: a name given at the end would arrive after
those answers had already been given, and the second instance would take the first one's user,
database and port.
--instance <name> does the same from the command line, and then the question is not asked. With
--instance cliente-x:
Without --instance |
--instance cliente-x |
|
|---|---|---|
| systemd unit | odoo18 |
odoo-cliente-x |
| sources and virtualenv | /opt/odoo/odoo18 |
/opt/odoo/odoo-cliente-x |
| system user and PostgreSQL role | odoo |
odoo-cliente-x |
| database | odoo |
odoo-cliente-x |
| home, filestore, cache | /opt/odoo/... |
/opt/odoo/odoo-cliente-x/... |
| helper command | odoo |
odoo-cliente-x |
The name must be lowercase letters, digits, - and _, start with a letter and stay within 26
characters — the intersection of what a systemd unit, a path, a PostgreSQL identifier, an Nginx
server_name and a Unix user name each accept. It is checked before anything on the machine is
touched.
The system user and the PostgreSQL role are always given the same name, and that is not a
convention: Odoo connects over the local Unix socket, where pg_hba.conf usually says peer — which
authenticates by operating-system user and ignores the password. A role named differently from the
user would simply be refused. If you decouple them explicitly, the installer says so and lets you
proceed: your pg_hba.conf may well be configured for it.
Each instance gets its own uninstall manifest: /var/lib/invok/instances/<name>.json for a named
one, and /var/lib/invok/state.json — where it has always been — for the unnamed one. So a second
instance can be installed alongside the first, and each is undone on its own.
Each instance takes two ports: the HTTP one and the longpolling one Odoo's gevent worker binds
at startup. --port moves both — 8169 gives 8172 — so a second instance normally needs nothing
else; --gevent-port is there for the machine that already has something on that number. The
preflight refuses a port another instance has recorded, even when nothing is listening on it
right now: an instance that is merely stopped holds no socket, and the collision would otherwise
surface at the first simultaneous start, naming neither of them.
sudo invok # the form asks for the name first
sudo invok --instance cliente-x --port 8169 # a second instance (HTTP 8169, longpolling 8172)
sudo invok list # what this machine carries
sudo invok rollback --instance cliente-x # undo just that one
sudo invok rollback --all # undo them all, shared artifacts lastinvok list needs root, because the manifests are 0600 root — a listing that silently came up
empty for lack of privileges would be worse than a refusal. For the same reason a manifest that
exists but cannot be read is reported as a problem rather than skipped.
rollback with no --instance works when there is exactly one instance; with several it lists them
and stops, rather than choose one for you. The unnamed instance is called default when you have to
type it, and for that reason default is not accepted as an instance name.
/opt/odoo, the system packages, the PostgreSQL cluster, wkhtmltopdf and the Nginx installation
belong to whichever instance created them. Removing that instance while another is installed would
take the ground out from under a running system, so those artifacts are left in place and the
rollback says so:
Shared with the instances still installed (default): left in place.
- prepare-opt-root
- install-system-dependencies
- setup-postgres
The instance's own artifacts — sources, virtualenv, database, role, config, unit, vhost, helper and,
for a named instance, its system user and home — are removed as always. Its manifest is kept: it
is the record of who owns what the others are still using, and invok list shows it as shared only.
To take everything off, in the right order:
sudo invok rollback --allTwo passes: each instance's own artifacts first, then what they had in common, once nothing is using
it. That ordering is the whole reason --all exists as its own flag rather than a loop you write
yourself.
With --config <FILE> the parameters come from a KEY=VALUE file. Unlike the old Bash version (which
sourced the file — code execution as root), parsing here is declarative: KEY=VALUE lines, #
comments and blank lines ignored, nothing executed (a value like $(...) stays a literal string).
Unknown keys produce a warning and are ignored.
Recognised keys: ODOO_VERSION, ODOO_INSTANCE, ODOO_USER, DB_USER, DB_PASSWORD, ODOO_PORT,
ODOO_GEVENT_PORT, DB_NAME,
ODOO_INSTALL_DIR, ODOO_ADMIN_PASSWD, ODOO_LOGFILE, WITH_NGINX, NGINX_SERVER_NAME,
NGINX_OPEN_HTTPS_PORT (legacy alias NGINX_ENABLE_SSL). ODOO_HOME is constant and ignored.
# production.env
ODOO_VERSION=18
ODOO_USER=odoo
ODOO_PORT=8069
DB_NAME=odoo
WITH_NGINX=true
# ODOO_ADMIN_PASSWD=... # do NOT use 'admin' in productionYou write this file. The repository ships no
production.env: a.envholds the admin and database passwords, soconfigs/*.envis git-ignored by design. The only exceptions are the CI presets —configs/ci.envandconfigs/ci-nginx.env, throwaway files for ephemeral runners — which serve as a complete example to copy.
Environment variables (network tuning)
Not installation parameters, but knobs for difficult networks, read from the process environment.
| Variable | Default | What it does |
|---|---|---|
ODOO_NETWORK_TIMEOUT_SECS |
300 |
Timeout for network operations (Odoo clone, fallback tarball, wkhtmltopdf .deb download). On expiry the command is interrupted with a clear error instead of hanging. 0 disables it |
GIT_CLONE_RETRIES |
3 |
git clone attempts before falling back to the tarball (a timeout consumes one attempt) |
PACKAGE_INSTALL_ATTEMPTS |
3 |
attempts at installing the packages when the mirror is what failed (Failed to fetch, a connection reset). A package that does not exist is not retried: it would answer the same way every time |
GIT_DEPTH |
5 |
Shallow clone depth |
apt-get and long local operations (DB init, pip install, venv creation) have no timeout:
cutting them short does more damage than waiting.
--dry-run runs the snapshots only (read-only) and prints the plan, telling "would act" apart from
"no-op (already present)". Nothing is changed and no state is persisted. It works with or without
sudo, but not identically: snapshots interrogate the system, and some do it through sudo
(PostgreSQL state, installed packages). Without privileges those steps show up as “snapshot
unavailable” and the plan, though true, is incomplete — the installer says so before printing it.
sudo invok --config production.env --dry-run # the plan, before anything happens
systemctl status odoo18 # N = short version, e.g. 18
journalctl -u odoo18 -n 50 --no-pager
odoo status # helper command; after: source ~/.bashrc
# start | stop | restart | list
# status [NAME] | logs [NAME] [N] | dev [NAME]Each instance gets its own helper — odoo, odoo-cliente-x — and every verb that starts or
stops acts on that instance alone: on a machine with two customers, a helper that could stop the
other one would be a way to take a customer offline by accident. Reading is not touching, though, so
list (and status, which prints it after its own state) answers from any helper: what is
installed here, what is up, and which command drives each.
Odoo services on this machine:
odoo-cliente-x.service inactive
-> odoo18.service active (this one: odoo)
logs follows a journal (logs 500 for more scrollback); Ctrl-C stops reading and leaves the
service running — worth saying, because dev is the verb right next to it and that one does stop
it. dev opens a shell as the instance's user to run odoo-bin by hand; when you leave,
it puts the service back the way it found it. You are asked first — and the answer you get by just
pressing Enter is always "as it was", so an instance that was serving comes back up and one you had
deliberately switched off stays off. If there is no terminal to ask — you closed the window, or the
session was killed — the same rule is applied without asking, which is the case the behaviour exists
for.
The three verbs that only look can name another instance, so you can go from list straight to
what you meant without first remembering somebody else's helper name:
odoo list # what is installed here, what is up, what drives each
odoo status cliente-x # that instance's state
odoo logs cliente-x 500 # its journal, last 500 lines
odoo dev cliente-x # a shell as its user, to reach its filesName it cliente-x, odoo-cliente-x or odoo-cliente-x.service — whatever list printed — or
default for the unnamed installation. An unknown name is refused with the listing; a name matching
two installations is refused rather than guessed.
start, stop and restart never take an instance. They drive this helper's service and no
other, which is what keeps one customer from going offline while somebody fixes another's problem —
to act on a named instance, use its own helper, which list tells you. For the same reason dev cliente-x only opens a shell: it does not stop that service. Its home is 0750 and its config
0640 on purpose — the database password and the customer's attachments are in there — so the way
in is sudo, not a loosened permission: whoever can sudo can already become any user. If that
instance is running, odoo-bin on its port will fail, and dev says so on the way in rather than
letting you find out.
sudo cat /var/log/invok.log # installer log (post-mortem; survives rollback, by design)
The instance is running, then invok rollback undoes all 25 steps in reverse:
no leftovers, the manifest is consumed, and /opt/odoo, the user and the service are
gone. The installation happened before the recording; nothing else is edited.
Before mutating anything, every step records whether what it is about to create already existed. If a step fails, the previous ones are undone in reverse order (best-effort, idempotent). The key guarantee is about pre-existing resources, which a rollback never touches:
- an existing database with the same name is never dropped (it may hold real data);
- an already-installed PostgreSQL stays (stop/disable by default, never purged without the flag);
- an existing
/opt/odoostays; - the user's
~/.bashrccomes back byte for byte (only our line is removed).
Two refusals, before anything is touched: an installation already registered — with the three ways on — and a port another manifest claims, while nothing is listening on it.
Re-running the installer. A registered installation is never silently overwritten. If the previous
one was complete, the installer stops and tells you the three ways on — --instance <name> --port <free port> to add a second instance beside it, invok rollback to remove it, or
--force to install over it, which archives the old manifest rather than deleting it. Note that
plainly re-running does not add an instance: an instance is created by naming it, on the command
line or with ODOO_INSTANCE in a .env. If it was
interrupted (Ctrl-C, crash, power loss), it resumes where it stopped: steps already executed are
not redone, and the record that those artifacts are ours is preserved — which is what lets the
rollback remove them months later. Resuming needs the same parameters: with a different database
name the installer stops and says which field does not match.
Ctrl-C. A Ctrl-C (or a kill/systemctl stop) no longer kills the installer: the installation
rolls itself back. The interruption takes effect between steps — the one in progress is carried
to completion, because stopping an apt halfway would leave dpkg inconsistent. For most commands
the wait is short: the signal reaches the whole process group, so the one running ends by itself. The
three that talk to the network (the clone, the tarball, the .deb) run in a process group of their
own — so that a timeout can kill the worker and not just the sudo in front of it — and are
therefore finished rather than interrupted; the log says so when it happens, and the wait is bounded
by ODOO_NETWORK_TIMEOUT_SECS. A second Ctrl-C exits immediately with code 130, leaving the
system half-done by your choice; clean it up with sudo invok rollback.
From a script, signal the installer only. “Two Ctrl-C” means two signals received. A
sudo pkill -INT -f invokhits two processes — thesudoand the installer — and so counts as the second press: immediate exit, no rollback. Usesudo pkill -INT -x invok(-x= exact process name). From a terminal the problem does not arise.
sudo invok rollback --dry-run # what would be removed, touching nothing
sudo invok rollback # remove for real (asks for confirmation)uninstall is an alias of the same command. It reads the state left by the installation, rebuilds the
steps with the snapshot taken back then and runs their undos in reverse order. The same guarantee
holds: only what the installer created is removed. A database that already existed stays where it is —
because the saved snapshot says so, not because of an inspection made at rollback time, which by then
could no longer tell the two cases apart.
| Flag | Value | Default |
|---|---|---|
--instance <NAME> |
which instance to undo; default is the unnamed one |
the only one installed, or a refusal listing them |
--all |
every instance, own artifacts first and shared ones last | off |
--state <FILE> |
state file to consume | /var/lib/invok/state.json (falls back to the historical /opt/odoo/.installer-state.json) |
--dry-run |
list without mutating (no sudo needed) |
off |
--aggressive-rollback |
also purge PostgreSQL/Nginx installed by us, and the common utilities | off |
--yes / -y |
skip the confirmation (required with no terminal) | off |
With more than one instance installed, rollback on its own lists them and stops: it will not
guess which one you meant when the choice is destructive. --instance and --all are the two ways
to say it out loud, and --state cannot be combined with --instance — one names a file, the other
names an instance.
On a successful installation the state file stays on disk: it is the uninstall manifest, the only
record of which artifacts that installation created and which it found already there. Without it
invok rollback could not tell the two apart, and could not remove the instance. Do not delete it by
hand. It is removed only after a complete rollback; if something could not be cleaned up the file
stays and the command can be re-run (undos are idempotent).
Three files live outside /opt/odoo, which is the perimeter the rollback must be able to remove
whole: the manifest /var/lib/invok/state.json, the lock /run/invok.lock (prevents two concurrent
installations, gone after a reboot) and the log /var/log/invok.log. A named instance keeps its
manifest in /var/lib/invok/instances/<name>.json, for the same reason: one file per instance, so
each is undone on its own.
-
Admin password
admin: discouraged. Interactively it needs an explicit confirmation; in non-interactive mode withadmin_passwd=adminthe installer stops. The password never reaches the logs or the summary. -
wkhtmltopdf checksum (TOFU): the SHA-256 of the package is verified before installing. Upstream publishes no checksums or signatures, so the pins are manual, trust-on-first-use and preloaded in the source. A download that does not match its pin — or a variant with no pin — is fail-closed refused, with no bypass.
-
The
odoohelper is not global: installed for the installing user only (~/.local/bin). -
TLS is not configured by the installer, deliberately. The generated Nginx vhost listens on port 80 only. For HTTPS use
certbot --nginx, which obtains the certificates and rewrites the vhost itself, adding the 443 block and the redirect;--open-https-portonly opens 443 on the firewall ahead of that step.sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d odoo.example.com
Two behaviours worth knowing before you deploy — how the Python interpreter is chosen, and the one
visible difference between the two Nginx layouts (on Fedora the default site lives inside
nginx.conf and is deliberately left alone) — are in the
wiki. Vulnerability reports: SECURITY.md.
cargo build
cargo test # runs without root: the system is behind a mock
cargo clippy --all-targets -- -D warnings
cargo fmt --all -- --checkThe tests cover every step (snapshot→run→undo round trip), the coordination between steps and the
end-to-end rollback (injected failure → final state == initial state; pre-existing resources
untouched). .github/workflows/test.yml runs them on every push. They run against a mock of the
system, so they prove the logic, not the integration with a real package manager, PostgreSQL and
systemd. That is .github/workflows/integration.yml, which really installs Odoo on ephemeral runners
and containers — Ubuntu, Debian, Fedora, with and without Nginx, with a pre-existing odoo user, and
with a real SIGINT mid-installation — then checks that invok rollback leaves the system clean,
package by package. It runs on demand and on main/dev. The matrix and its declared limits are in
the wiki.
MODE=full bash scripts/ci/integration-test.sh # runnable by hand — DESTRUCTIVE: throwaway VMs onlyContributions welcome — CONTRIBUTING.md has the full guide: the checklist for a new
step, how the divergence between distributions is kept out of the steps, and the commit and PR
conventions. In short: run the four commands above before opening a PR, branch off dev, and keep to
three rules this project takes seriously:
- Every mutation is reversible. A new step carries
snapshot/run/undoand aPreState; if it creates something the rollback cannot remove, it is not finished. TheSteptrait is not to be modified in order to add a step. - No
.unwrap()/.expect()in production code: every failure is aResultthat says what and where. - A test that cannot fail is not a test. Check that your red case really goes red first.
If the change affects behaviour on one distribution, say so in the PR: the integration CI is where that shows up.
Participation in issues, pull requests and reviews is governed by the Code of Conduct; vulnerabilities go to SECURITY.md, never to a public issue.
The installer was originally written in Bash; those versions are archived at tags
v1.0.0 and v1.2.0. The current version is
a complete rewrite in Rust with transactional rollback, and is the only one in this repository.
MIT — see LICENSE. The published .tar.gz, .deb and .rpm contain only the invok
binary and a short plain-text guide (PACKAGE-README, installed as
/usr/share/doc/invok/README): no third-party code is redistributed. Odoo and wkhtmltopdf (both
LGPLv3) are downloaded at runtime from their official sources and remain subject to their own
licences.
This project is independent and is not affiliated with Odoo S.A., nor endorsed by it; “Odoo” is a trademark of Odoo S.A.
Engine and the Step trait · step-by-step reference · rollback model ·
multi-distribution support · security
Made for Ubuntu, Debian and Fedora. Independent project — not affiliated with Odoo S.A.