Unified CLI tool for native Odoo development environment management
Project description
odoodev — Unified Odoo Development CLI
Deutsche Dokumentation
Projektübersicht
odoodev ist ein einheitliches CLI-Tool für die Verwaltung nativer Odoo-Entwicklungsumgebungen über mehrere Versionen hinweg (v16–v19). Es ersetzt eine Vielzahl manueller Skripte, Shell-Funktionen und Konfigurationsdateien durch ein konsistentes Werkzeug mit vollständigem Lifecycle-Management.
Hauptfunktionen:
- Multi-Version Support (v16, v17, v18, v19)
- Automatische Versionserkennung aus dem aktuellen Verzeichnis
- Interaktiver Setup-Wizard für die Ersteinrichtung
- Native Entwicklung mit UV Virtual Environments
- Repository-Management (Odoo, OCA, Enterprise, Custom)
- Datenbank-Backup-Wiederherstellung (ZIP, 7z, tar, SQL)
- Docker-Service-Verwaltung (PostgreSQL, Mailpit)
- Shell-Integration (Fish, Bash, Zsh)
- Odoo-Konfigurationsgenerierung mit Template-System
Schnelleinrichtung
1. Installation
Option A: Dauerhaft als CLI-Tool installieren (empfohlen)
# Dauerhaft als globales CLI-Tool installieren
uv tool install odoodev-equitania
# Oder direkt von GitLab/GitHub installieren (neueste Version)
uv tool install git+https://github.com/equitania/odoo-dev.git
# Update auf neueste Version
uv tool upgrade odoodev-equitania
# Einmalig ausfuehren ohne Installation
uvx odoodev-equitania --help
Nach uv tool install ist odoodev systemweit verfuegbar — kein Virtual Environment noetig.
Option B: Entwicklungsinstallation (fuer Beitragende)
# Repository klonen
git clone https://github.com/equitania/odoo-dev.git
cd odoo-dev
# Virtual Environment erstellen und aktivieren
uv venv && source .venv/bin/activate.fish # Fish
# oder: source .venv/bin/activate # Bash/Zsh
# Paket im Entwicklungsmodus installieren
uv pip install -e ".[dev]"
2. Setup-Wizard
Beim ersten Aufruf eines beliebigen odoodev-Befehls erscheint automatisch ein Hinweis:
[INFO] Keine Konfiguration gefunden. Tipp: 'odoodev setup' (verwende Defaults)
Starte den interaktiven Wizard:
# Interaktiver Setup-Wizard (empfohlen für Erstbenutzer)
odoodev setup
Der Wizard führt durch 4 Schritte:
| Schritt | Frage | Standard |
|---|---|---|
| 1 | Basisverzeichnis für Odoo-Projekte | ~/gitbase |
| 2 | Aktive Odoo-Versionen (Mehrfachauswahl) | v16, v17, v18, v19 |
| 3 | PostgreSQL-Benutzer und -Passwort | ownerp / CHANGE_AT_FIRST |
| 4 | Zusammenfassung und Bestätigung | — |
Die Konfiguration wird in ~/.config/odoodev/config.yaml gespeichert:
# Generated by: odoodev setup
base_dir: ~/projects/odoo # Eigener Basispfad
database:
user: ownerp
password: CHANGE_AT_FIRST
active_versions:
- '18'
- '19'
Weitere Setup-Optionen:
# Mit Standardwerten ohne Interaktion (z.B. für CI/Skripte)
odoodev setup --non-interactive
# Konfiguration auf Standardwerte zurücksetzen
odoodev setup --reset
# Aktuelle Konfiguration anzeigen
odoodev config show
Was base_dir bewirkt: Alle Versionspfade aus versions.yaml werden automatisch umgebogen. Wenn base_dir auf ~/projects/odoo gesetzt ist, wird aus ~/gitbase/v18 automatisch ~/projects/odoo/v18. Die automatische Versionserkennung (odoodev start ohne Versionsangabe) funktioniert dann ebenfalls im neuen Basisverzeichnis.
Rückwärtskompatibilität:
- Ohne
config.yamlverwendet odoodev die bisherigen Standardwerte (~/gitbase) - Bestehende
versions-override.yamlfunktioniert unverändert - Pfade mit expliziten Overrides werden nicht von
base_dirumgebogen
3. Umgebung initialisieren und starten
# Neue Odoo 18 Umgebung initialisieren
odoodev init 18
# Odoo im Entwicklungsmodus starten
odoodev start 18 --dev
# Shell-Integration installieren
odoodev shell-setup
Voraussetzungen und Dateien
Systemvoraussetzungen im Detail
| Tool | Prüfung | Pflicht | Anmerkung |
|---|---|---|---|
| UV | uv --version |
Ja | Python-Paketmanager — ersetzt pip |
| Docker | docker info |
Ja | Für PostgreSQL- und Mailpit-Container |
| Docker Compose V2 | docker compose version |
Ja | Service-Orchestrierung |
| wkhtmltopdf | Pfad-Detection (plattformspezifisch) | Ja | macOS: /usr/local/bin, /opt/homebrew/bin; Linux: /usr/local/bin, /usr/bin |
| PostgreSQL-Tools | pg_dump, psql |
Für DB-Operationen | macOS: /opt/homebrew/opt/libpq/bin |
| Git | SSH-Zugang | Ja | Für Repository-Klonen |
| Python-Pakete | babel, psycopg2, lxml, PIL, werkzeug, dateutil | Ja | In .venv via requirements.txt |
Installation der Systemvoraussetzungen:
# macOS
brew install uv
brew install libpq && brew link libpq --force
# wkhtmltopdf: "patched qt"-Version von https://wkhtmltopdf.org/downloads.html herunterladen
# Hinweis: brew install wkhtmltopdf enthält kein gepatchtes Qt — Odoo-PDF-Rendering funktioniert ggf. nicht korrekt
# Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
sudo apt-get install -y postgresql-client
# wkhtmltopdf: "patched qt"-Version von https://wkhtmltopdf.org/downloads.html herunterladen
# Hinweis: apt-get install wkhtmltopdf enthält kein gepatchtes Qt — Odoo-PDF-Rendering funktioniert ggf. nicht korrekt
Vom Benutzer bereitzustellende Dateien
| Datei | Pfad | Zweck |
|---|---|---|
repos.yaml |
vXX-dev/scripts/repos.yaml |
Definiert alle zu klonenden Repositories |
requirements.txt |
vXX-dev/devXX_native/requirements.txt |
Python-Abhängigkeiten für Odoo |
odoo_template.conf |
vXX-dev/conf/odooXX_template.conf |
Template für Odoo-Konfigurationsgenerierung |
| SSH-Keys | ~/.ssh/id_rsa (oder via repos.yaml konfigurierbar) |
Zugang zu privaten Git-Repositories |
Diese Dateien sind projektspezifisch und werden nicht von odoodev generiert. Sie müssen im jeweiligen Versions-Repository vorhanden sein.
Von odoodev generierte Dateien
| Datei | Befehl | Zweck |
|---|---|---|
config.yaml |
odoodev setup |
Globale Konfiguration (Basispfad, DB-Credentials) |
.env |
odoodev env setup |
Umgebungsvariablen (Ports, Credentials, Pfade) |
docker-compose.yml |
odoodev init |
Docker-Services (PostgreSQL, Mailpit) |
.venv/ |
odoodev venv setup |
Python Virtual Environment via UV |
odoo_YYMMDD.conf |
odoodev repos |
Odoo-Konfiguration mit addons_path |
odoodev-activate.fish/.sh |
odoodev shell-setup |
Shell-Integration |
Verzeichnisstruktur im Detail
Vollständige Darstellung aller Verzeichnisse und Dateien mit Markierung, was odoodev generiert vs. was vorhanden sein muss:
~/.config/odoodev/
├── config.yaml # [GENERATED] odoodev setup — Globale Konfiguration
└── versions-override.yaml # [MANUELL] Optionale Versions-Overrides
~/gitbase/vXX/ # (oder eigener base_dir aus config.yaml)
├── vXX-server/ # [REPOS] Git-Clone des Odoo-Servers
│ ├── odoo-bin # Odoo-Executable (von odoodev start erwartet)
│ ├── odoo/addons/ # Core-Addons
│ └── addons/ # Standard-Addons
├── vXX-dev/
│ ├── devXX_native/ # [INIT] Erstellt von odoodev
│ │ ├── .env # [GENERATED] odoodev env setup
│ │ ├── docker-compose.yml # [GENERATED] odoodev init
│ │ ├── .venv/ # [GENERATED] odoodev venv setup
│ │ └── requirements.txt # [MANUELL] Muss vorhanden sein!
│ ├── conf/
│ │ └── odooXX_template.conf # [MANUELL] Template fuer Config-Generierung
│ └── scripts/
│ └── repos.yaml # [MANUELL] Repository-Definitionen
├── myconfs/ # [GENERATED] odoodev repos
│ └── odoo_YYMMDD.conf # Generierte Odoo-Konfiguration
├── vXX-addons/ # [REPOS] Geklont von odoodev repos
├── vXX-oca/ # [REPOS] Geklont von odoodev repos
└── ...weitere Addon-Repos... # [REPOS] Gemaess repos.yaml
Legende:
[GENERATED]— Wird von odoodev automatisch erzeugt[REPOS]— Wird vonodoodev reposper git clone erstellt[INIT]— Verzeichnis wird vonodoodev initangelegt[MANUELL]— Muss vom Benutzer bereitgestellt werden
Datenfluss
Der komplette Workflow von der Ersteinrichtung bis zum Server-Start:
odoodev setup (einmalig)
├── Basispfad festlegen (Standard: ~/gitbase)
├── Aktive Versionen waehlen
├── DB-Credentials konfigurieren
└── config.yaml speichern → ~/.config/odoodev/config.yaml
odoodev init
├── Verzeichnisse anlegen (devXX_native/)
├── .env generieren (aus Jinja2-Template + versions.yaml + config.yaml)
├── docker-compose.yml generieren (PostgreSQL + Mailpit)
├── .venv erstellen (UV + requirements.txt)
└── repos ausfuehren (optional, --skip-repos ueberspringt)
│
├── repos.yaml laden
├── SSH-Zugang pruefen
├── git clone/pull fuer alle Repositories
└── odoo_YYMMDD.conf generieren
│
├── Template lesen (odooXX_template.conf)
├── addons_path aus Repo-Pfaden zusammenbauen
│ (gruppiert nach: Odoo, OCA, Enterprise,
│ Syscoon, 3rd-party, Equitania, Customer, Other)
└── Config in myconfs/ speichern
odoodev start
├── .env laden (DB_PORT, PGUSER, ODOO_PORT...)
├── Voraussetzungen pruefen:
│ ├── .venv vorhanden?
│ ├── odoo-bin vorhanden?
│ ├── odoo_*.conf vorhanden?
│ └── PostgreSQL-Port erreichbar?
├── requirements.txt Freshness pruefen (SHA256)
└── odoo-bin starten (mit odoo_YYMMDD.conf)
Verwendung
Befehle im Überblick
| Befehl | Beschreibung |
|---|---|
odoodev setup |
Interaktiver Setup-Wizard für die Ersteinrichtung |
odoodev init [VERSION] |
Neue Entwicklungsumgebung initialisieren |
odoodev start [VERSION] |
Odoo-Server starten |
odoodev stop [VERSION] |
Odoo-Server und Docker-Services stoppen |
odoodev repos [VERSION] |
Repositories klonen/aktualisieren |
odoodev pull [VERSION] |
Schneller git pull aller vorhandenen Repos |
odoodev db [SUBCOMMAND] [VERSION] |
Datenbankoperationen (backup, restore, list, drop) |
odoodev env [SUBCOMMAND] [VERSION] |
.env-Dateiverwaltung |
odoodev venv [SUBCOMMAND] [VERSION] |
Virtual Environment verwalten |
odoodev docker [SUBCOMMAND] [VERSION] |
Docker-Services steuern |
odoodev config [SUBCOMMAND] |
Konfiguration und Versionen |
odoodev run [PLAYBOOK] [OPTIONS] |
YAML-Playbook oder Inline-Steps ausführen |
odoodev shell-setup |
Shell-Wrapper installieren |
Setup-Wizard
# Interaktiver Wizard (empfohlen)
odoodev setup
# Standardwerte ohne Interaktion speichern
odoodev setup --non-interactive
# Konfiguration auf Defaults zurücksetzen
odoodev setup --reset
Umgebung initialisieren
# Interaktiv (mit Bestätigungsdialogen)
odoodev init 18
# Nicht-interaktiv (mit Standardwerten)
odoodev init 18 --non-interactive
# Ohne Repository-Klonen
odoodev init 18 --skip-repos
# Ohne Docker-Services
odoodev init 18 --skip-docker
Server starten
# Entwicklungsmodus (Hot-Reload)
odoodev start 18 --dev
# Interaktive Shell
odoodev start 18 --shell
# Tests ausführen
odoodev start 18 --test
# Venv aktivieren ohne Server zu starten
odoodev start 18 --prepare
# Zusätzliche Odoo-Argumente übergeben
odoodev start 18 -- -d mydb -u my_module
Start-Modi im Überblick:
| Modus | Flag | Beschreibung |
|---|---|---|
| Normal | (kein Flag) | Produktionsnaher Start. Views werden aus der Datenbank geladen, kein Auto-Reload. |
| Development | --dev |
Entwicklungsmodus (--dev=all): Views aus XML-Dateien laden, Auto-Reload bei Code-Änderungen, pdb-Debugger bei Exceptions. Nur für Entwicklung! |
| Shell | --shell |
Interaktive Odoo-Python-Shell mit vollem Zugriff auf die ORM-API. |
| Test | --test |
Startet Odoo mit --test-enable --stop-after-init — führt Unit-Tests aus und beendet sich. |
| Prepare | --prepare |
Aktiviert nur die virtuelle Umgebung und öffnet eine Shell, ohne Odoo zu starten. |
Hinweis:
--dev=allaktiviert alle Entwickler-Features (XML-Reload, Python Auto-Reload, pdb-Debugger). Einzelne Features können kommagetrennt gewählt werden, z.B.--dev=reload,xml. Niemals in Produktion verwenden!
Server stoppen
# Odoo-Prozess und Docker-Services stoppen
odoodev stop 18
# Nur Odoo-Prozess stoppen (Docker weiter laufen lassen)
odoodev stop 18 --keep-docker
# Sofortiger Kill ohne graceful Shutdown
odoodev stop 18 --force
Der stop-Befehl erkennt den laufenden Odoo-Prozess anhand des konfigurierten Ports (via lsof) und beendet ihn zunächst mit SIGTERM, dann bei Bedarf mit SIGKILL.
Repository-Verwaltung
# Alle Repositories klonen/aktualisieren
odoodev repos 18
# Nur Odoo-Server verarbeiten
odoodev repos 18 --server-only
# Nur Odoo-Konfiguration generieren
odoodev repos 18 --config-only
# Custom repos.yaml verwenden
odoodev repos 18 -c /pfad/zu/repos.yaml
Schneller Pull aller Repos
# Alle vorhandenen Repos aktualisieren (kein Clone, kein Access-Check)
odoodev pull 18
# Mit Custom repos.yaml
odoodev pull 18 -c /pfad/zu/repos.yaml
Datenbankoperationen
# Datenbanken auflisten
odoodev db list 18
# Backup erstellen (interaktiv)
odoodev db backup 18
# Backup als SQL-Dump
odoodev db backup 18 -n v18_exam -t sql -o /tmp
# Backup als ZIP mit Filestore
odoodev db backup 18 -n v18_exam -t zip -o /tmp
# Backup wiederherstellen
odoodev db restore 18 -n v18_test -z backup.zip
# Datenbank löschen
odoodev db drop 18 -n v18_test
# Datenbank löschen ohne Bestätigungsprompt
odoodev db drop 18 -n v18_test --yes
Playbook-Automation (run)
# YAML-Playbook ausführen
odoodev run playbook.yaml
# Dry-Run — Schritte anzeigen ohne auszuführen
odoodev run playbook.yaml --dry-run
# JSON-Output (NDJSON) für maschinelle Verarbeitung
odoodev run playbook.yaml --output json
# Inline-Steps ohne YAML-Datei
odoodev run --step docker.up --step pull -V 18
# Version überschreiben
odoodev run playbook.yaml -V 19
Playbook-Format:
version: "18"
on_error: stop # stop | continue
steps:
- name: "Start Docker"
command: docker.up
- name: "Pull code"
command: pull
- name: "Generate config"
command: repos
args:
config-only: true
Verfügbare Commands: docker.up, docker.down, docker.status, pull, repos, start, stop, db.list, db.backup, db.restore, db.drop, env.check, venv.check, venv.setup
Beispiel-Playbooks unter odoodev/data/examples/playbooks/: daily-update.yaml, start-dev.yaml, full-refresh.yaml, restore-db.yaml
Docker-Services
# Services starten
odoodev docker up 18
# Services stoppen
odoodev docker down 18
# Status anzeigen
odoodev docker status 18
# Logs anzeigen
odoodev docker logs 18 -f
repos.yaml Format
Die Datei repos.yaml steuert, welche Repositories geklont und wie sie in der Odoo-Konfiguration organisiert werden. Erwartet unter vXX-dev/scripts/repos.yaml:
version: "18"
branch: "develop"
ssh_key: "~/.ssh/id_rsa"
paths:
base: ~/gitbase/v18
template: ~/gitbase/v18/v18-dev/conf/odoo18_template.conf
config_dir: ~/gitbase/v18/myconfs
base_addons:
- $HOME/gitbase/v18/v18-server/odoo/addons
- $HOME/gitbase/v18/v18-server/addons
addons:
- key: eq_module
path: v18-addons
git_url: git@gitlab.ownerp.io:v18/v18-addons.git
section: Equitania # Odoo|OCA|Enterprise|Syscoon|3rd-party|Equitania|Customer|Other
commented: false # false=aktiv, true=auskommentiert in odoo.conf
suffix: "" # Unterverzeichnis fuer Module (z.B. /modules)
customers:
- key: v18-customer
path: v18-customer
git_url: git@gitlab.ownerp.io:customer/v18-customer.git
section: Customer
commented: true # Auskommentiert in odoo.conf bis aktiviert
Felder-Referenz:
| Feld | Pflicht | Beschreibung |
|---|---|---|
version |
Ja | Odoo-Version (z.B. "18") |
branch |
Ja | Git-Branch fuer alle Repositories |
ssh_key |
Nein | Pfad zum SSH-Key (Standard: System-Default) |
paths.base |
Ja | Basis-Verzeichnis der Odoo-Version |
paths.template |
Ja | Pfad zum odoo_template.conf |
paths.config_dir |
Ja | Zielverzeichnis fuer generierte Configs |
base_addons |
Ja | Odoo Core- und Standard-Addon-Pfade |
addons[].key |
Ja | Eindeutiger Identifier fuer das Repository |
addons[].path |
Ja | Zielverzeichnis (relativ zu paths.base) |
addons[].git_url |
Ja | Git-Repository-URL |
addons[].section |
Nein | Gruppierung in odoo.conf (Standard: "Other") |
addons[].commented |
Nein | true = als Kommentar in odoo.conf (Standard: false) |
addons[].suffix |
Nein | Unterverzeichnis-Suffix fuer addons_path |
Sektionen im addons_path (Reihenfolge in generierter odoo.conf):
- Odoo (Core)
- OCA
- Enterprise
- Syscoon
- 3rd-party
- Equitania
- Customer
- Other
Repository-Abschnitte in repos.yaml: addons, additional, special, customers — alle werden identisch verarbeitet.
Unterstützte Versionen
| Version | Python | PostgreSQL | DB Port | Odoo Port | Gevent | Mailpit | SMTP |
|---|---|---|---|---|---|---|---|
| v16 | 3.12 | 16.11 | 16432 | 16069 | 16072 | 16025 | 11025 |
| v17 | 3.12 | 16.11 | 17432 | 17069 | 17072 | 17025 | 11725 |
| v18 | 3.13 | 16.11 | 18432 | 18069 | 18072 | 18025 | 1025 |
| v19 | 3.13 | 17.4 | 19432 | 19069 | 19072 | 19025 | 1925 |
Port-Schema: {version}{service} — z.B. v18: DB=18432, Odoo=18069, Gevent=18072
Konfiguration
Globale Konfiguration (odoodev setup)
Die globale Konfiguration wird in ~/.config/odoodev/config.yaml gespeichert und steuert:
| Einstellung | Standard | Beschreibung |
|---|---|---|
base_dir |
~/gitbase |
Basisverzeichnis für alle Odoo-Versionen |
database.user |
ownerp |
Standard-PostgreSQL-Benutzer |
database.password |
CHANGE_AT_FIRST |
Standard-PostgreSQL-Passwort |
active_versions |
16, 17, 18, 19 |
Aktive Odoo-Versionen |
Die DB-Credentials aus config.yaml werden automatisch in .env-Dateien und Datenbankoperationen verwendet.
Versionsspezifische Overrides (versions-override.yaml)
Benutzer können versionsspezifische Einstellungen ueberschreiben, ohne versions.yaml im Paket zu ändern:
Datei: ~/.config/odoodev/versions-override.yaml
versions:
"18":
ports:
db: 15432 # Eigener PostgreSQL-Port
odoo: 8069 # Standard-Odoo-Port statt 18069
paths:
base: "~/projects/odoo18" # Anderer Basispfad
git:
branch: "main" # Anderer Default-Branch
Die Override-Datei wird beim Laden der Versionsregistry automatisch zusammengeführt. Nur angegebene Felder werden ueberschrieben — alle anderen behalten ihre Standardwerte.
Priorität:
versions-override.yaml(höchste — versionsspezifische Pfade werden nicht vonbase_dirumgebogen)config.yaml(globale Einstellungen wiebase_dir)versions.yamlim Paket (Standardwerte)
Obsolete Komponenten
odoodev ersetzt folgende Artefakte aus den bestehenden vXX-dev Repositories:
| Obsolete Datei/Komponente | Ersetzt durch | Anmerkung |
|---|---|---|
start-native.sh |
odoodev start |
Alle Startmodi abgedeckt (dev, shell, test, prepare) |
.env.template |
odoodev env setup |
Template ist im Tool integriert (Jinja2) |
docker-compose.yml (manuell) |
odoodev init |
Generiert aus internem Template |
Fish-Funktionen (odoo-env, odoo-start, odoo-stop) |
odoodev CLI |
Alle Befehle konsolidiert |
Fish-Aliase (dev16, dev18) |
odoodev-activate |
Shell-Integration via odoodev shell-setup |
Manuelle git clone pro Repo |
odoodev repos |
Gesteuert ueber repos.yaml |
| Manuelle Config-Generierung | odoodev repos --config-only |
Automatisch aus repos.yaml + Template |
docker-compose-arm64.yml |
odoodev init |
Plattform wird automatisch erkannt |
Weiterhin benötigt (nicht ersetzt):
| Datei | Grund |
|---|---|
repos.yaml |
Projektspezifisch — definiert welche Repos geklont werden |
requirements.txt |
Versionsspezifisch — Odoo-Dependencies + Custom-Pakete |
odooXX_template.conf |
Versionsspezifisch — Basis fuer Konfigurationsgenerierung |
| SSH-Keys | Zugang zu privaten Git-Repositories |
Filestore-Verwaltung
Bei der Datenbank-Wiederherstellung verwaltet odoodev auch den Odoo-Filestore:
Filestore-Pfad: ~/odoo-share/filestore/{db_name}/
Dieser Pfad ist fest im Tool hinterlegt und wird bei odoodev db restore automatisch verwendet:
- Backup wird extrahiert (ZIP, 7z, tar, gz, SQL)
- SQL-Dump wird in neue Datenbank eingespielt
- Filestore wird nach
~/odoo-share/filestore/{db_name}/kopiert - Post-Restore Deaktivierungen:
- Cron-Jobs (
ir_cron.active = false) - Mail-Server (
ir_mail_server.active = false) - Fetchmail-Server (
fetchmail_server.active = false) - Nextcloud-Integration (Config-Parameter geleert)
- Office365-Integration (Config-Parameter geleert)
- Cron-Jobs (
Architektur
odoodev/
├── cli.py # CLI-Einstiegspunkt (Click) + First-Run-Hint
├── output.py # Rich-Konsolenausgabe
├── commands/
│ ├── setup_cmd.py # Setup-Wizard (questionary)
│ ├── init_cmd.py # Umgebungsinitialisierung
│ ├── start.py # Server-Start
│ ├── repos.py # Repository-Verwaltung
│ ├── db.py # Datenbankoperationen
│ ├── env.py # .env-Verwaltung
│ ├── venv.py # Virtual Environment
│ ├── docker.py # Docker-Services
│ ├── config.py # Konfiguration
│ ├── run.py # Playbook-Automation (run)
│ └── shell_setup.py # Shell-Integration
├── core/
│ ├── global_config.py # Globale Konfiguration (config.yaml)
│ ├── version_registry.py # Versionsmanagement + base_dir Rebasing
│ ├── environment.py # Plattformerkennung
│ ├── git_ops.py # Git-Operationen
│ ├── database.py # PostgreSQL-Operationen
│ ├── odoo_config.py # Konfigurationsgenerierung
│ ├── venv_manager.py # UV-Venv-Verwaltung
│ ├── docker_compose.py # Docker-Compose-Operationen
│ ├── prerequisites.py # Voraussetzungsprüfungen
│ ├── shell_integration.py# Shell-Aktivierung
│ ├── playbook.py # Playbook-Engine (Dataclasses, Runner)
│ └── automation.py # Non-Interactive Command-Handler
├── templates/ # Jinja2-Templates
│ ├── docker-compose.yml.j2
│ ├── env.template.j2
│ └── shell/ # Shell-Aktivierungsskripte
└── data/
├── versions.yaml # Versionsregistry
└── examples/playbooks/ # Beispiel-Playbooks (daily-update, start-dev, ...)
Entwicklung
# Entwicklungsumgebung einrichten
uv venv && source .venv/bin/activate.fish
uv pip install -e ".[dev]"
# Tests ausführen
pytest
# Linting
ruff check . && ruff format --check .
# Formatierung anwenden
ruff check --fix . && ruff format .
# Typ-Prüfung
mypy odoodev
# Paket bauen
uv build
Lizenz
Dieses Projekt ist unter der AGPL-3.0-or-later Lizenz lizenziert.
Kontakt
- Firma: Equitania Software GmbH
- E-Mail: info@equitania.de
- Website: https://www.equitania.de
English Documentation
Project Overview
odoodev is a unified CLI tool for native Odoo development environment management across versions (v16–v19). It replaces a variety of manual scripts, shell functions, and configuration files with a consistent tool providing complete lifecycle management.
Key Features:
- Multi-version support (v16, v17, v18, v19)
- Automatic version detection from current directory
- Interactive setup wizard for first-time configuration
- Native development with UV virtual environments
- Repository management (Odoo, OCA, Enterprise, Custom)
- Database backup restoration (ZIP, 7z, tar, SQL)
- Docker service management (PostgreSQL, Mailpit)
- Shell integration (Fish, Bash, Zsh)
- Odoo configuration generation with template system
Quick Setup
1. Installation
Option A: Install as permanent CLI tool (recommended)
# Install as global CLI tool permanently
uv tool install odoodev-equitania
# Or install directly from GitLab/GitHub (latest version)
uv tool install git+https://github.com/equitania/odoo-dev.git
# Update to latest version
uv tool upgrade odoodev-equitania
# Run once without installing
uvx odoodev-equitania --help
After uv tool install, odoodev is available system-wide — no virtual environment needed.
Option B: Development installation (for contributors)
# Clone repository
git clone https://github.com/equitania/odoo-dev.git
cd odoo-dev
# Create and activate virtual environment
uv venv && source .venv/bin/activate.fish # Fish
# or: source .venv/bin/activate # Bash/Zsh
# Install package in development mode
uv pip install -e ".[dev]"
2. Setup Wizard
On the first invocation of any odoodev command, a hint is shown automatically:
[INFO] No configuration found. Tip: run 'odoodev setup' (using defaults)
Start the interactive wizard:
# Interactive setup wizard (recommended for first-time users)
odoodev setup
The wizard guides you through 4 steps:
| Step | Question | Default |
|---|---|---|
| 1 | Base directory for Odoo projects | ~/gitbase |
| 2 | Active Odoo versions (multi-select) | v16, v17, v18, v19 |
| 3 | PostgreSQL user and password | ownerp / CHANGE_AT_FIRST |
| 4 | Summary and confirmation | — |
The configuration is saved to ~/.config/odoodev/config.yaml:
# Generated by: odoodev setup
base_dir: ~/projects/odoo # Custom base path
database:
user: ownerp
password: CHANGE_AT_FIRST
active_versions:
- '18'
- '19'
Additional setup options:
# Save defaults without interaction (e.g. for CI/scripts)
odoodev setup --non-interactive
# Reset configuration to defaults
odoodev setup --reset
# Show current configuration
odoodev config show
What base_dir does: All version paths from versions.yaml are automatically rebased. If base_dir is set to ~/projects/odoo, then ~/gitbase/v18 becomes ~/projects/odoo/v18. Automatic version detection (odoodev start without version argument) also works in the new base directory.
Backwards compatibility:
- Without
config.yaml, odoodev uses the original defaults (~/gitbase) - Existing
versions-override.yamlcontinues to work unchanged - Paths with explicit overrides are not rebased by
base_dir
3. Initialize and Start
# Initialize new Odoo 18 environment
odoodev init 18
# Start Odoo in development mode
odoodev start 18 --dev
# Install shell integration
odoodev shell-setup
Prerequisites and Required Files
System Prerequisites in Detail
| Tool | Check | Required | Note |
|---|---|---|---|
| UV | uv --version |
Yes | Python package manager — replaces pip |
| Docker | docker info |
Yes | For PostgreSQL and Mailpit containers |
| Docker Compose V2 | docker compose version |
Yes | Service orchestration |
| wkhtmltopdf | Path detection (platform-specific) | Yes | macOS: /usr/local/bin, /opt/homebrew/bin; Linux: /usr/local/bin, /usr/bin |
| PostgreSQL tools | pg_dump, psql |
For DB operations | macOS: /opt/homebrew/opt/libpq/bin |
| Git | SSH access | Yes | For repository cloning |
| Python packages | babel, psycopg2, lxml, PIL, werkzeug, dateutil | Yes | In .venv via requirements.txt |
Installing system prerequisites:
# macOS
brew install uv
brew install libpq && brew link libpq --force
# wkhtmltopdf: download 'patched qt' version from https://wkhtmltopdf.org/downloads.html
# Note: brew install wkhtmltopdf lacks patched Qt — Odoo PDF rendering may not work correctly
# Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
sudo apt-get install -y postgresql-client
# wkhtmltopdf: download 'patched qt' version from https://wkhtmltopdf.org/downloads.html
# Note: apt-get install wkhtmltopdf lacks patched Qt — Odoo PDF rendering may not work correctly
User-Provided Files
| File | Path | Purpose |
|---|---|---|
repos.yaml |
vXX-dev/scripts/repos.yaml |
Defines all repositories to clone |
requirements.txt |
vXX-dev/devXX_native/requirements.txt |
Python dependencies for Odoo |
odoo_template.conf |
vXX-dev/conf/odooXX_template.conf |
Template for Odoo configuration generation |
| SSH keys | ~/.ssh/id_rsa (or configurable via repos.yaml) |
Access to private Git repositories |
These files are project-specific and are not generated by odoodev. They must exist in the respective version repository.
Files Generated by odoodev
| File | Command | Purpose |
|---|---|---|
config.yaml |
odoodev setup |
Global configuration (base path, DB credentials) |
.env |
odoodev env setup |
Environment variables (ports, credentials, paths) |
docker-compose.yml |
odoodev init |
Docker services (PostgreSQL, Mailpit) |
.venv/ |
odoodev venv setup |
Python virtual environment via UV |
odoo_YYMMDD.conf |
odoodev repos |
Odoo configuration with addons_path |
odoodev-activate.fish/.sh |
odoodev shell-setup |
Shell integration |
Directory Structure in Detail
Complete view of all directories and files, showing what odoodev generates vs. what must be provided:
~/.config/odoodev/
├── config.yaml # [GENERATED] odoodev setup — Global configuration
└── versions-override.yaml # [MANUAL] Optional version overrides
~/gitbase/vXX/ # (or custom base_dir from config.yaml)
├── vXX-server/ # [REPOS] Git clone of Odoo server
│ ├── odoo-bin # Odoo executable (expected by odoodev start)
│ ├── odoo/addons/ # Core addons
│ └── addons/ # Standard addons
├── vXX-dev/
│ ├── devXX_native/ # [INIT] Created by odoodev
│ │ ├── .env # [GENERATED] odoodev env setup
│ │ ├── docker-compose.yml # [GENERATED] odoodev init
│ │ ├── .venv/ # [GENERATED] odoodev venv setup
│ │ └── requirements.txt # [MANUAL] Must be provided!
│ ├── conf/
│ │ └── odooXX_template.conf # [MANUAL] Template for config generation
│ └── scripts/
│ └── repos.yaml # [MANUAL] Repository definitions
├── myconfs/ # [GENERATED] odoodev repos
│ └── odoo_YYMMDD.conf # Generated Odoo configuration
├── vXX-addons/ # [REPOS] Cloned by odoodev repos
├── vXX-oca/ # [REPOS] Cloned by odoodev repos
└── ...additional addon repos... # [REPOS] According to repos.yaml
Legend:
[GENERATED]— Automatically created by odoodev[REPOS]— Created byodoodev reposvia git clone[INIT]— Directory created byodoodev init[MANUAL]— Must be provided by the user
Data Flow
The complete workflow from first-time setup to server start:
odoodev setup (one-time)
├── Set base directory (default: ~/gitbase)
├── Select active versions
├── Configure DB credentials
└── Save config.yaml → ~/.config/odoodev/config.yaml
odoodev init
├── Create directories (devXX_native/)
├── Generate .env (from Jinja2 template + versions.yaml + config.yaml)
├── Generate docker-compose.yml (PostgreSQL + Mailpit)
├── Create .venv (UV + requirements.txt)
└── Run repos (optional, --skip-repos skips this)
│
├── Load repos.yaml
├── Verify SSH access
├── git clone/pull for all repositories
└── Generate odoo_YYMMDD.conf
│
├── Read template (odooXX_template.conf)
├── Build addons_path from repo paths
│ (grouped by: Odoo, OCA, Enterprise,
│ Syscoon, 3rd-party, Equitania, Customer, Other)
└── Save config to myconfs/
odoodev start
├── Load .env (DB_PORT, PGUSER, ODOO_PORT...)
├── Check prerequisites:
│ ├── .venv exists?
│ ├── odoo-bin exists?
│ ├── odoo_*.conf exists?
│ └── PostgreSQL port reachable?
├── Check requirements.txt freshness (SHA256)
└── Start odoo-bin (with odoo_YYMMDD.conf)
Usage
Command Reference
| Command | Description |
|---|---|
odoodev setup |
Interactive setup wizard for first-time configuration |
odoodev init [VERSION] |
Initialize new development environment |
odoodev start [VERSION] |
Start Odoo server |
odoodev stop [VERSION] |
Stop Odoo server and Docker services |
odoodev repos [VERSION] |
Clone/update repositories |
odoodev pull [VERSION] |
Quick git pull across all existing repos |
odoodev db [SUBCOMMAND] [VERSION] |
Database operations (backup, restore, list, drop) |
odoodev env [SUBCOMMAND] [VERSION] |
.env file management |
odoodev venv [SUBCOMMAND] [VERSION] |
Virtual environment management |
odoodev docker [SUBCOMMAND] [VERSION] |
Docker service control |
odoodev config [SUBCOMMAND] |
Configuration and versions |
odoodev run [PLAYBOOK] [OPTIONS] |
Run YAML playbook or inline steps |
odoodev shell-setup |
Install shell wrapper functions |
Setup Wizard
# Interactive wizard (recommended)
odoodev setup
# Save defaults without interaction
odoodev setup --non-interactive
# Reset configuration to defaults
odoodev setup --reset
Initialize Environment
# Interactive (with confirmation prompts)
odoodev init 18
# Non-interactive (with defaults)
odoodev init 18 --non-interactive
# Skip repository cloning
odoodev init 18 --skip-repos
# Skip Docker services
odoodev init 18 --skip-docker
Start Server
# Development mode (hot-reload)
odoodev start 18 --dev
# Interactive shell
odoodev start 18 --shell
# Run tests
odoodev start 18 --test
# Activate venv without starting server
odoodev start 18 --prepare
# Pass additional Odoo arguments
odoodev start 18 -- -d mydb -u my_module
Start Modes Overview:
| Mode | Flag | Description |
|---|---|---|
| Normal | (no flag) | Production-like start. Views are loaded from the database, no auto-reload. |
| Development | --dev |
Development mode (--dev=all): load views from XML files, auto-reload on code changes, pdb debugger on exceptions. Development only! |
| Shell | --shell |
Interactive Odoo Python shell with full ORM API access. |
| Test | --test |
Starts Odoo with --test-enable --stop-after-init — runs unit tests and exits. |
| Prepare | --prepare |
Only activates the virtual environment and opens a shell without starting Odoo. |
Note:
--dev=allenables all developer features (XML reload, Python auto-reload, pdb debugger). Individual features can be selected comma-separated, e.g.--dev=reload,xml. Never use in production!
Stop Server
# Stop Odoo process and Docker services
odoodev stop 18
# Stop only Odoo process (keep Docker running)
odoodev stop 18 --keep-docker
# Immediate kill without graceful shutdown
odoodev stop 18 --force
The stop command discovers the running Odoo process by configured port (via lsof) and terminates it with SIGTERM first, then SIGKILL if needed.
Repository Management
# Clone/update all repositories
odoodev repos 18
# Process only Odoo server
odoodev repos 18 --server-only
# Generate Odoo config only
odoodev repos 18 --config-only
# Use custom repos.yaml
odoodev repos 18 -c /path/to/repos.yaml
Quick Pull All Repos
# Update all existing repos (no clone, no access check)
odoodev pull 18
# With custom repos.yaml
odoodev pull 18 -c /path/to/repos.yaml
Database Operations
# List databases
odoodev db list 18
# Create backup (interactive)
odoodev db backup 18
# Backup as SQL dump
odoodev db backup 18 -n v18_exam -t sql -o /tmp
# Backup as ZIP with filestore
odoodev db backup 18 -n v18_exam -t zip -o /tmp
# Restore backup
odoodev db restore 18 -n v18_test -z backup.zip
# Drop database
odoodev db drop 18 -n v18_test
# Drop database without confirmation prompt
odoodev db drop 18 -n v18_test --yes
Playbook Automation (run)
# Execute YAML playbook
odoodev run playbook.yaml
# Dry-run — show steps without executing
odoodev run playbook.yaml --dry-run
# JSON output (NDJSON) for machine processing
odoodev run playbook.yaml --output json
# Inline steps without YAML file
odoodev run --step docker.up --step pull -V 18
# Override version
odoodev run playbook.yaml -V 19
Playbook format:
version: "18"
on_error: stop # stop | continue
steps:
- name: "Start Docker"
command: docker.up
- name: "Pull code"
command: pull
- name: "Generate config"
command: repos
args:
config-only: true
Available commands: docker.up, docker.down, docker.status, pull, repos, start, stop, db.list, db.backup, db.restore, db.drop, env.check, venv.check, venv.setup
Example playbooks in odoodev/data/examples/playbooks/: daily-update.yaml, start-dev.yaml, full-refresh.yaml, restore-db.yaml
Docker Services
# Start services
odoodev docker up 18
# Stop services
odoodev docker down 18
# Show status
odoodev docker status 18
# View logs
odoodev docker logs 18 -f
repos.yaml Format
The file repos.yaml controls which repositories are cloned and how they are organized in the Odoo configuration. Expected at vXX-dev/scripts/repos.yaml:
version: "18"
branch: "develop"
ssh_key: "~/.ssh/id_rsa"
paths:
base: ~/gitbase/v18
template: ~/gitbase/v18/v18-dev/conf/odoo18_template.conf
config_dir: ~/gitbase/v18/myconfs
base_addons:
- $HOME/gitbase/v18/v18-server/odoo/addons
- $HOME/gitbase/v18/v18-server/addons
addons:
- key: eq_module
path: v18-addons
git_url: git@gitlab.ownerp.io:v18/v18-addons.git
section: Equitania # Odoo|OCA|Enterprise|Syscoon|3rd-party|Equitania|Customer|Other
commented: false # false=active, true=commented out in odoo.conf
suffix: "" # Subdirectory for modules (e.g. /modules)
customers:
- key: v18-customer
path: v18-customer
git_url: git@gitlab.ownerp.io:customer/v18-customer.git
section: Customer
commented: true # Commented out in odoo.conf until activated
Field Reference:
| Field | Required | Description |
|---|---|---|
version |
Yes | Odoo version (e.g. "18") |
branch |
Yes | Git branch for all repositories |
ssh_key |
No | Path to SSH key (default: system default) |
paths.base |
Yes | Base directory of the Odoo version |
paths.template |
Yes | Path to odoo_template.conf |
paths.config_dir |
Yes | Target directory for generated configs |
base_addons |
Yes | Odoo core and standard addon paths |
addons[].key |
Yes | Unique identifier for the repository |
addons[].path |
Yes | Target directory (relative to paths.base) |
addons[].git_url |
Yes | Git repository URL |
addons[].section |
No | Grouping in odoo.conf (default: "Other") |
addons[].commented |
No | true = as comment in odoo.conf (default: false) |
addons[].suffix |
No | Subdirectory suffix for addons_path |
Sections in addons_path (order in generated odoo.conf):
- Odoo (Core)
- OCA
- Enterprise
- Syscoon
- 3rd-party
- Equitania
- Customer
- Other
Repository sections in repos.yaml: addons, additional, special, customers — all processed identically.
Supported Versions
| Version | Python | PostgreSQL | DB Port | Odoo Port | Gevent | Mailpit | SMTP |
|---|---|---|---|---|---|---|---|
| v16 | 3.12 | 16.11 | 16432 | 16069 | 16072 | 16025 | 11025 |
| v17 | 3.12 | 16.11 | 17432 | 17069 | 17072 | 17025 | 11725 |
| v18 | 3.13 | 16.11 | 18432 | 18069 | 18072 | 18025 | 1025 |
| v19 | 3.13 | 17.4 | 19432 | 19069 | 19072 | 19025 | 1925 |
Port schema: {version}{service} — e.g. v18: DB=18432, Odoo=18069, Gevent=18072
Configuration
Global Configuration (odoodev setup)
The global configuration is stored in ~/.config/odoodev/config.yaml and controls:
| Setting | Default | Description |
|---|---|---|
base_dir |
~/gitbase |
Base directory for all Odoo versions |
database.user |
ownerp |
Default PostgreSQL user |
database.password |
CHANGE_AT_FIRST |
Default PostgreSQL password |
active_versions |
16, 17, 18, 19 |
Active Odoo versions |
DB credentials from config.yaml are automatically used in .env files and database operations.
Version-Specific Overrides (versions-override.yaml)
Users can override version-specific settings without modifying versions.yaml in the package:
File: ~/.config/odoodev/versions-override.yaml
versions:
"18":
ports:
db: 15432 # Custom PostgreSQL port
odoo: 8069 # Standard Odoo port instead of 18069
paths:
base: "~/projects/odoo18" # Different base path
git:
branch: "main" # Different default branch
The override file is automatically merged when loading the version registry. Only specified fields are overridden — all others retain their default values.
Priority:
versions-override.yaml(highest — version-specific paths are not rebased bybase_dir)config.yaml(global settings likebase_dir)versions.yamlin the package (defaults)
Obsolete Components
odoodev replaces the following artifacts from existing vXX-dev repositories:
| Obsolete File/Component | Replaced by | Note |
|---|---|---|
start-native.sh |
odoodev start |
All start modes covered (dev, shell, test, prepare) |
.env.template |
odoodev env setup |
Template is built into the tool (Jinja2) |
docker-compose.yml (manual) |
odoodev init |
Generated from internal template |
Fish functions (odoo-env, odoo-start, odoo-stop) |
odoodev CLI |
All commands consolidated |
Fish aliases (dev16, dev18) |
odoodev-activate |
Shell integration via odoodev shell-setup |
Manual git clone per repo |
odoodev repos |
Controlled via repos.yaml |
| Manual config generation | odoodev repos --config-only |
Automatic from repos.yaml + template |
docker-compose-arm64.yml |
odoodev init |
Platform is automatically detected |
Still required (not replaced):
| File | Reason |
|---|---|
repos.yaml |
Project-specific — defines which repos to clone |
requirements.txt |
Version-specific — Odoo dependencies + custom packages |
odooXX_template.conf |
Version-specific — base for configuration generation |
| SSH keys | Access to private Git repositories |
Filestore Management
During database restoration, odoodev also manages the Odoo filestore:
Filestore path: ~/odoo-share/filestore/{db_name}/
This path is hardcoded in the tool and automatically used by odoodev db restore:
- Backup is extracted (ZIP, 7z, tar, gz, SQL)
- SQL dump is imported into new database
- Filestore is copied to
~/odoo-share/filestore/{db_name}/ - Post-restore deactivations:
- Cron jobs (
ir_cron.active = false) - Mail servers (
ir_mail_server.active = false) - Fetchmail servers (
fetchmail_server.active = false) - Nextcloud integration (config parameters cleared)
- Office365 integration (config parameters cleared)
- Cron jobs (
Architecture
odoodev/
├── cli.py # CLI entry point (Click) + first-run hint
├── output.py # Rich console output
├── commands/
│ ├── setup_cmd.py # Setup wizard (questionary)
│ ├── init_cmd.py # Environment initialization
│ ├── start.py # Server startup
│ ├── repos.py # Repository management
│ ├── db.py # Database operations
│ ├── env.py # .env management
│ ├── venv.py # Virtual environment
│ ├── docker.py # Docker services
│ ├── config.py # Configuration
│ └── shell_setup.py # Shell integration
├── core/
│ ├── global_config.py # Global configuration (config.yaml)
│ ├── version_registry.py # Version management + base_dir rebasing
│ ├── environment.py # Platform detection
│ ├── git_ops.py # Git operations
│ ├── database.py # PostgreSQL operations
│ ├── odoo_config.py # Config generation
│ ├── venv_manager.py # UV venv management
│ ├── docker_compose.py # Docker Compose operations
│ ├── prerequisites.py # Prerequisite checks
│ └── shell_integration.py# Shell activation
├── templates/ # Jinja2 templates
│ ├── docker-compose.yml.j2
│ ├── env.template.j2
│ └── shell/ # Shell activation scripts
└── data/
└── versions.yaml # Version registry
Development
# Set up development environment
uv venv && source .venv/bin/activate.fish
uv pip install -e ".[dev]"
# Run tests
pytest
# Linting
ruff check . && ruff format --check .
# Apply formatting
ruff check --fix . && ruff format .
# Type checking
mypy odoodev
# Build package
uv build
License
This project is licensed under AGPL-3.0-or-later.
Contact
- Company: Equitania Software GmbH
- Email: info@equitania.de
- Website: https://www.equitania.de
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file odoodev_equitania-0.4.7.tar.gz.
File metadata
- Download URL: odoodev_equitania-0.4.7.tar.gz
- Upload date:
- Size: 95.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fea910e911e71f4d08fc9bcbf24654593816519f1047bff8959b8ff422133f94
|
|
| MD5 |
5bbbb254a838db27407544095d27c965
|
|
| BLAKE2b-256 |
e21b6583e5e223d98dfa38ac1ce70b8f1adb9b9a34e9243af5c872cead00969c
|
File details
Details for the file odoodev_equitania-0.4.7-py3-none-any.whl.
File metadata
- Download URL: odoodev_equitania-0.4.7-py3-none-any.whl
- Upload date:
- Size: 96.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ed1be9acf29f199069b92e60b5e5b003df89f9257bd401a93ca240b00945ef6
|
|
| MD5 |
b9fd7cc5a4ca02124ec2b66cad648dda
|
|
| BLAKE2b-256 |
3e8a26bad3ecf4fbd78d4a794b188484a293d1ed50523d7b5d96993c69d2882b
|