diff --git a/conf/nixos/erplibre.nix b/conf/nixos/erplibre.nix index 9711aaa..bbcd12d 100644 --- a/conf/nixos/erplibre.nix +++ b/conf/nixos/erplibre.nix @@ -177,4 +177,67 @@ LIBRARY_PATH = "/run/current-system/sw/lib"; PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig"; }; + + # Le service ERPLibre, DÉCLARÉ et non écrit. + # + # Sur toute autre distribution l'installation dépose l'unité par + # « tee /etc/systemd/system/erplibre.service ». Ici /etc est généré depuis + # le store et monté en lecture seule : le tee échoue sur « Read-only file + # system », et l'installation entière rend 1 à sa dernière étape, après que + # tout le reste a réussi. + # + # « wantedBy » est l'équivalent déclaratif de « systemctl enable » : + # l'activation par lien symbolique écrirait elle aussi dans /etc. + # + # L'interpréteur vient du STORE et non de /bin. /bin et /usr/bin sont ici + # un montage FUSE d'envfs, et systemd résout l'exécutable d'ExecStart + # lui-même, hors de portée de ce montage : « /bin/bash » y rend + # « 203/EXEC, Unable to locate executable ». Avec Restart=always, l'unité + # boucle alors indéfiniment. La même raison vaut pour /usr/bin/env, donc + # le shebang de run.sh ne suffirait pas davantage. + # + # La première reconstruction déclare le service AVANT qu'ERPLibre ne soit + # installé, et son démarrage échoue alors — c'est attendu, et c'est + # exactement le cas que « nixos-rebuild rend 4 » recouvre. L'installation + # le relance une fois le dépôt en place. + systemd.services.erplibre = { + description = "ERPLibre"; + requires = [ "postgresql.service" ]; + after = [ "network.target" "network-online.target" "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + # Une unité systemd ne reçoit PAS le PATH d'une session : le sien ne + # porte que coreutils, findutils, grep, sed et systemd. + # + # bash — run.sh lance odoo_bin.sh et lib_db_select.sh, dont le shebang est + # « #!/usr/bin/env bash ». env est là, bash non : « env: 'bash': No such + # file or directory », et run.sh s'arrête avant Odoo. + # + # python3 — la sonde de réveil tourne AVANT que odoo_bin.sh n'active le + # venv, donc avec le python du système. Son échec est silencieux + # (« 2>/dev/null ») : sans elle, la première page ouverte attendrait le + # chargement du registre sans que rien ne le dise. + path = with pkgs; [ bash python312 ]; + # L'unité est déclarée par le module, donc démarrée par la + # reconstruction — qui a lieu PENDANT « make install_os », alors que la + # source d'Odoo n'arrive qu'à « make install_odoo_18 ». Sans condition, + # run.sh échoue sur un odoo-bin absent et « Restart = always » le rejoue + # toutes les cinq secondes jusqu'à ce que l'installation le pose — vingt + # et un échecs mesurés sur une pose ordinaire, et un « nixos-rebuild » + # qui rend 4 parce qu'une unité n'a pas démarré. + # + # Une CONDITION, et non une dépendance : systemd saute l'unité en le + # disant une fois, sans la marquer en échec, et la démarre d'elle-même + # au prochain déclenchement une fois le fichier là. Le motif évite de + # figer la version d'Odoo ici, où elle vieillirait en silence. + unitConfig.ConditionPathExistsGlob = "@EL_DIR@/odoo*/odoo/odoo-bin"; + serviceConfig = { + Type = "simple"; + User = "@EL_USER@"; + WorkingDirectory = "@EL_DIR@"; + ExecStart = "${pkgs.bash}/bin/bash @EL_DIR@/run.sh"; + Restart = "always"; + RestartSec = 5; + StandardOutput = "journal+console"; + }; + }; } diff --git a/long_test/install_nixos.py b/long_test/install_nixos.py index 939be46..7eea0ae 100755 --- a/long_test/install_nixos.py +++ b/long_test/install_nixos.py @@ -312,10 +312,21 @@ def verifier(cible, journal, jump=""): """ resultats = {} + # Par NOM EXACT, jamais par motif. envfs est un système de fichiers qui + # RÉSOUT à la demande et n'ÉNUMÈRE pas : « ls /usr/bin » y rend un + # répertoire vide alors que « /usr/bin/python3.12 » s'ouvre. Un glob passe + # par readdir, donc ne rend rien, et le contrôle déclarait absent ce qui + # est là — il éprouvait envfs de la seule façon dont envfs ne peut pas + # répondre. + # + # La version vient du dépôt plutôt que d'être écrite ici : c'est elle que + # l'installation a suivie, et une constante vieillirait à côté. chemins = sonder( cible, "for f in /bin/bash /usr/bin/env; do [ -e $f ] || exit 1; done;" - " ls /usr/bin/python3.* >/dev/null 2>&1 || exit 1; echo ok", + " v=$(cut -d. -f1,2 git/erplibre/.python-odoo-version 2>/dev/null);" + ' [ -n "$v" ] || v=3;' + ' [ -e "/usr/bin/python$v" ] || exit 1; echo ok', jump, ) resultats["envfs"] = chemins == "ok" diff --git a/script/install/install_nixos_dependency.sh b/script/install/install_nixos_dependency.sh index 4a356e4..d98a6ac 100755 --- a/script/install/install_nixos_dependency.sh +++ b/script/install/install_nixos_dependency.sh @@ -17,6 +17,11 @@ set -e . ./env_var.sh EL_USER=${USER} +# Le répertoire du service, pris du checkout QUI POSE le module : « make +# install_os » tourne à la racine du dépôt, et c'est ce dépôt-là que le +# service doit lancer. Deviner « /home/$USER/git/erplibre » se tromperait sur +# une installation de production, qui vit sous /opt. +EL_DIR=${EL_DIR:-${PWD}} MODULE_SRC="conf/nixos/erplibre.nix" MODULE_DST="/etc/nixos/erplibre.nix" CONFIG="/etc/nixos/configuration.nix" @@ -34,8 +39,9 @@ fi echo -e "\n---- Module ERPLibre pour NixOS ----" # Le nom du compte est substitué comme le user-data cloud-init l'est : le # module déclare un rôle PostgreSQL, et un rôle porte un nom. -sed "s/@EL_USER@/${EL_USER}/g" "${MODULE_SRC}" | sudo tee "${MODULE_DST}" > /dev/null -echo " posé : ${MODULE_DST} (compte ${EL_USER})" +sed -e "s/@EL_USER@/${EL_USER}/g" -e "s#@EL_DIR@#${EL_DIR}#g" \ + "${MODULE_SRC}" | sudo tee "${MODULE_DST}" > /dev/null +echo " posé : ${MODULE_DST} (compte ${EL_USER}, dépôt ${EL_DIR})" if [ ! -f "${CONFIG}" ]; then echo "Configuration introuvable : ${CONFIG}" diff --git a/script/todo/qemu_install.py b/script/todo/qemu_install.py index 2ec60b3..be01248 100644 --- a/script/todo/qemu_install.py +++ b/script/todo/qemu_install.py @@ -205,8 +205,28 @@ class QemuInstallMixin: "sudo sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' " "/etc/selinux/config 2>/dev/null || true; fi; " ) + # NixOS est reconnu DANS la VM, et non d'après ce que l'hôte croit + # savoir : la même commande sert au déploiement, au test long et à un + # « --hote » qu'on n'a pas créé. /etc y est généré depuis le store et + # monté en lecture seule — le tee plus bas échoue sur « Read-only file + # system », et l'installation rend 1 à sa dernière étape après que + # tout le reste a réussi. L'unité vient du module déclaratif posé par + # « make install_os » ; il ne reste qu'à la relancer, une fois le + # dépôt en place. + nixos = ( + "if [ -f /etc/os-release ] && grep -q '^ID=nixos' " + "/etc/os-release; then " + "if systemctl cat erplibre.service >/dev/null 2>&1; then " + "sudo systemctl restart erplibre.service; " + "else " + 'echo "erplibre.service non déclaré : ' + '« make install_os » pose le module NixOS qui le porte." >&2; ' + "exit 1; fi; " + "else " + ) return ( - f'SVC_USER=$(whoami); SVC_GROUP=$(id -gn); SVC_DIR="{svc_dir}"; ' + nixos + + f'SVC_USER=$(whoami); SVC_GROUP=$(id -gn); SVC_DIR="{svc_dir}"; ' + pre + selinux_shell + "sudo tee /etc/systemd/system/erplibre.service >/dev/null <