From aa3f99e746e3a28861a771772657fbaa5cd072ae Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Fri, 25 Sep 2026 01:29:53 -0400 Subject: [PATCH] [IMP] install.sh: ask y/N before installing the tooling venv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a system too old to read TODO, install.sh ran the install without asking, and the install DELETES an existing .venv.erplibre. It now runs only on a yes typed in a terminal: o, oui, y or yes, any case. Enter alone means no, and with no terminal no question is asked and nothing is installed. « aucun python3 » finally shows when the probe is silent: the || fallback never fired behind a pipe. Checked: 16 tests, each answer on a pseudo-terminal; install.sh run in an isolated copy exits 1 on « n » and installs on « o ». --- FR --- [IMP] install.sh : demander o/N avant d'installer le venv d'outillage Sur un système trop ancien pour lire TODO, install.sh lançait l'installation sans rien demander, et celle-ci SUPPRIME un .venv.erplibre existant. Elle ne part plus que sur un oui tapé au terminal : o, oui, y ou yes, toute casse. Entrée seule vaut non, et sans terminal aucune question n'est posée ni installation lancée. « aucun python3 » s'affiche enfin quand la sonde est muette : le repli par || ne se déclenchait jamais derrière un tube. Vérifié : 16 tests, dont chaque réponse sur un pseudo-terminal ; install.sh lancé dans une copie isolée sort en 1 sur « n » et installe sur « o ». Assisted-by: Claude Opus 5.5 --- CHANGELOG.base.md | 4 +- CHANGELOG.fr.md | 2 +- CHANGELOG.md | 2 +- README.base.md | 12 +++-- README.fr.md | 6 ++- README.md | 6 ++- install.sh | 28 ++++++++++- test/test_install_sh_confirm.py | 83 +++++++++++++++++++++++++++++++++ 8 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 test/test_install_sh_confirm.py diff --git a/CHANGELOG.base.md b/CHANGELOG.base.md index 78a346e..e001a9e 100644 --- a/CHANGELOG.base.md +++ b/CHANGELOG.base.md @@ -162,7 +162,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere - The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3` - `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted -- `make` and `make todo` run `./todo.sh`, a launcher whose name says a menu opens and not an installation; it hands every argument to `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal +- `make` and `make todo` run `./todo.sh`, a launcher whose name says a menu opens and not an installation; it hands every argument to `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install, which runs only on a yes typed in a terminal (`o`, `oui`, `y`, `yes`) since it can delete `.venv.erplibre`. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal - The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/` - Debian 11 is dropped from the deployment catalogue: its LTS ended, and its security suite is neither served nor archived — the index it still publishes names packages whose pool no longer holds the file, so apt stops before installing git. Debian 13 takes its place, its cloud image always being the latest point release - Every container image is built on bookworm, whatever the Odoo version. The base is `python:-slim-`: the interpreter comes from the official image, never from Debian, and the bookworm variants exist down to 3.7.17. The wkhtmltopdf build follows the suite — bullseye's requires libssl1.1, absent from bookworm @@ -187,7 +187,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs - L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système - `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé -- `make` et `make todo` lancent `./todo.sh`, dont le nom annonce un menu et non une installation ; il passe chaque argument à `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal +- `make` et `make todo` lancent `./todo.sh`, dont le nom annonce un menu et non une installation ; il passe chaque argument à `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation, qui ne part que sur un oui tapé au terminal (`o`, `oui`, `y`, `yes`) puisqu'elle peut supprimer `.venv.erplibre`. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal - L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/` - Debian 11 quitte le catalogue de déploiement : son LTS est terminé, et sa suite de sécurité n'est ni servie ni archivée — l'index qu'elle publie encore nomme des paquets dont le pool ne porte plus le fichier, et apt s'arrête avant d'installer git. Debian 13 la remplace, son image cloud étant toujours le dernier point de version - Toutes les images de conteneur reposent sur bookworm, quelle que soit la version d'Odoo. La base est `python:-slim-` : l'interpréteur vient de l'image officielle, jamais de Debian, et les variantes bookworm existent jusqu'à 3.7.17. Le build de wkhtmltopdf suit la version — celui de bullseye réclame libssl1.1, absente de bookworm diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 9a78560..41bb6e0 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -82,7 +82,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs - L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système - `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé -- `make` et `make todo` lancent `./todo.sh`, dont le nom annonce un menu et non une installation ; il passe chaque argument à `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal +- `make` et `make todo` lancent `./todo.sh`, dont le nom annonce un menu et non une installation ; il passe chaque argument à `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation, qui ne part que sur un oui tapé au terminal (`o`, `oui`, `y`, `yes`) puisqu'elle peut supprimer `.venv.erplibre`. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal - L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/` - Debian 11 quitte le catalogue de déploiement : son LTS est terminé, et sa suite de sécurité n'est ni servie ni archivée — l'index qu'elle publie encore nomme des paquets dont le pool ne porte plus le fichier, et apt s'arrête avant d'installer git. Debian 13 la remplace, son image cloud étant toujours le dernier point de version - Toutes les images de conteneur reposent sur bookworm, quelle que soit la version d'Odoo. La base est `python:-slim-` : l'interpréteur vient de l'image officielle, jamais de Debian, et les variantes bookworm existent jusqu'à 3.7.17. Le build de wkhtmltopdf suit la version — celui de bullseye réclame libssl1.1, absente de bookworm diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c9ba0..6ff6830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,7 +82,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere - The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3` - `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted -- `make` and `make todo` run `./todo.sh`, a launcher whose name says a menu opens and not an installation; it hands every argument to `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal +- `make` and `make todo` run `./todo.sh`, a launcher whose name says a menu opens and not an installation; it hands every argument to `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install, which runs only on a yes typed in a terminal (`o`, `oui`, `y`, `yes`) since it can delete `.venv.erplibre`. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal - The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/` - Debian 11 is dropped from the deployment catalogue: its LTS ended, and its security suite is neither served nor archived — the index it still publishes names packages whose pool no longer holds the file, so apt stops before installing git. Debian 13 takes its place, its cloud image always being the latest point release - Every container image is built on bookworm, whatever the Odoo version. The base is `python:-slim-`: the interpreter comes from the official image, never from Debian, and the bookworm variants exist down to 3.7.17. The wkhtmltopdf build follows the suite — bullseye's requires libssl1.1, absent from bookworm diff --git a/README.base.md b/README.base.md index 6f1e80e..cd970a4 100644 --- a/README.base.md +++ b/README.base.md @@ -257,8 +257,10 @@ make `make` and `./install.sh` start TODO through the interpreter that can read it: `.venv.erplibre` when it carries the right version, otherwise the system `python3` when it is recent enough, otherwise the install itself — a system -older than `conf/python-erplibre-version` cannot parse the code, so it goes -straight to the install rather than stopping on a syntax error. When the +older than `conf/python-erplibre-version` cannot parse the code, so it offers +the install rather than stopping on a syntax error. The install runs only on a +yes typed in a terminal (`o`, `oui`, `y` or `yes`); Enter alone, or no +terminal, means no. When the environment is missing but the system Python suffices, TODO offers to run `./script/install/install_erplibre.sh` (in a terminal) or prints that command. The install builds the environment through `EL_PYTHON_PROVIDER` (mise or pyenv); an existing `.venv.erplibre` on @@ -270,8 +272,10 @@ by hand goes with it. `make` et `./install.sh` lancent TODO par l'interpréteur capable de le lire : `.venv.erplibre` quand il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation elle-même — un système plus ancien -que `conf/python-erplibre-version` ne sait pas analyser le code, donc on va -droit à l'installation plutôt que de s'arrêter sur une erreur de syntaxe. Quand +que `conf/python-erplibre-version` ne sait pas analyser le code, donc +l'installation est proposée plutôt que de s'arrêter sur une erreur de syntaxe. +Elle ne part que sur un oui tapé au terminal (`o`, `oui`, `y` ou `yes`) ; +Entrée seule, ou l'absence de terminal, vaut non. Quand l'environnement manque mais que le Python du système suffit, TODO propose de lancer `./script/install/install_erplibre.sh` (dans un terminal) ou affiche cette commande. L'installation bâtit l'environnement par diff --git a/README.fr.md b/README.fr.md index 0bdb9d3..56b40d5 100644 --- a/README.fr.md +++ b/README.fr.md @@ -129,8 +129,10 @@ make `make` et `./install.sh` lancent TODO par l'interpréteur capable de le lire : `.venv.erplibre` quand il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation elle-même — un système plus ancien -que `conf/python-erplibre-version` ne sait pas analyser le code, donc on va -droit à l'installation plutôt que de s'arrêter sur une erreur de syntaxe. Quand +que `conf/python-erplibre-version` ne sait pas analyser le code, donc +l'installation est proposée plutôt que de s'arrêter sur une erreur de syntaxe. +Elle ne part que sur un oui tapé au terminal (`o`, `oui`, `y` ou `yes`) ; +Entrée seule, ou l'absence de terminal, vaut non. Quand l'environnement manque mais que le Python du système suffit, TODO propose de lancer `./script/install/install_erplibre.sh` (dans un terminal) ou affiche cette commande. L'installation bâtit l'environnement par diff --git a/README.md b/README.md index 1b63d4b..8083d66 100644 --- a/README.md +++ b/README.md @@ -127,8 +127,10 @@ make `make` and `./install.sh` start TODO through the interpreter that can read it: `.venv.erplibre` when it carries the right version, otherwise the system `python3` when it is recent enough, otherwise the install itself — a system -older than `conf/python-erplibre-version` cannot parse the code, so it goes -straight to the install rather than stopping on a syntax error. When the +older than `conf/python-erplibre-version` cannot parse the code, so it offers +the install rather than stopping on a syntax error. The install runs only on a +yes typed in a terminal (`o`, `oui`, `y` or `yes`); Enter alone, or no +terminal, means no. When the environment is missing but the system Python suffices, TODO offers to run `./script/install/install_erplibre.sh` (in a terminal) or prints that command. The install builds the environment through `EL_PYTHON_PROVIDER` (mise or pyenv); an existing `.venv.erplibre` on diff --git a/install.sh b/install.sh index 005d0c0..8917786 100755 --- a/install.sh +++ b/install.sh @@ -42,6 +42,20 @@ el_assez_recent() { = "${ATTENDUE}" ] } +# Vrai sur une réponse affirmative lue au terminal, en français ou en anglais : +# o, oui, y, yes, sans égard à la casse. Le défaut est NON : une frappe sur +# Entrée ne lance pas une installation qui peut détruire un venv. Sans +# terminal sur l'entrée, rien ne peut consentir, et la réponse est non. +el_confirmer() { + [ -t 0 ] || return 1 + printf '%s [o/N] ' "$1" + read -r reponse || return 1 + case "$(printf '%s' "${reponse}" | tr '[:upper:]' '[:lower:]')" in + o | oui | y | yes) return 0 ;; + *) return 1 ;; + esac +} + PYTHON_VENV="./${VENV}/bin/python" if [ -n "${VENV}" ] && [ -x "${PYTHON_VENV}" ] \ && el_assez_recent "$(el_mineure "${PYTHON_VENV}")"; then @@ -64,7 +78,17 @@ if command -v python3 > /dev/null 2>&1 \ exec ./script/todo/todo.py "$@" fi +SYSTEME="$(el_mineure python3)" echo "Python ${VOULUE:-du depot} est requis pour lire le code de TODO ;" -echo " ce systeme livre $(el_mineure python3 2> /dev/null || echo 'aucun python3')." -echo " Installation du venv d'outillage, qui le pose :" +echo " ce systeme livre ${SYSTEME:-aucun python3 utilisable}." +echo " L'installation du venv d'outillage le pose :" +echo " ./script/install/install_erplibre.sh" +if [ -d "./${VENV}" ]; then + echo " ${VENV} existant sera SUPPRIME puis rebati, avec ce qui y a ete" + echo " pose a la main." +fi +if ! el_confirmer "Lancer l'installation ?"; then + echo "Installation non lancee : TODO ne demarre pas." + exit 1 +fi exec ./script/todo/source_todo.sh "$@" diff --git a/test/test_install_sh_confirm.py b/test/test_install_sh_confirm.py new file mode 100644 index 0000000..5eac5f7 --- /dev/null +++ b/test/test_install_sh_confirm.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# © 2026 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +"""La question qu'install.sh pose avant l'installation du venv d'outillage. + +L'installation peut SUPPRIMER un .venv.erplibre existant : elle ne part que +sur un oui explicite. La réponse se donne en français ou en anglais (o, oui, +y, yes, toute casse) ; Entrée seule vaut non. Sans terminal sur l'entrée, +personne ne peut consentir, et la réponse est non sans question posée. +""" + +import os +import select +import subprocess +import unittest +from pathlib import Path + +RACINE = Path(__file__).resolve().parent.parent +SCRIPT = ( + "source <(sed -n '/^el_confirmer()/,/^}/p' install.sh); " + 'el_confirmer "Lancer ?"' +) + + +def repondre_au_terminal(reponse): + """Lance el_confirmer sur un pseudo-terminal, y tape la réponse. + + Rend le code de sortie et ce que le terminal a affiché. + """ + maitre, esclave = os.openpty() + proc = subprocess.Popen( + ["bash", "-c", SCRIPT], + cwd=RACINE, + stdin=esclave, + stdout=esclave, + stderr=esclave, + ) + os.close(esclave) + os.write(maitre, (reponse + "\n").encode()) + code = proc.wait(timeout=10) + lu = b"" + while select.select([maitre], [], [], 0.2)[0]: + try: + morceau = os.read(maitre, 4096) + except OSError: + break + if not morceau: + break + lu += morceau + os.close(maitre) + return code, lu.decode(errors="replace") + + +class TestConfirmer(unittest.TestCase): + def test_oui_en_francais_et_en_anglais(self): + for reponse in ("o", "O", "oui", "Oui", "y", "Y", "yes", "YES"): + with self.subTest(reponse=reponse): + self.assertEqual(repondre_au_terminal(reponse)[0], 0) + + def test_entree_seule_et_non_refusent(self): + for reponse in ("", "n", "non", "no", "ouais", "x"): + with self.subTest(reponse=reponse): + self.assertEqual(repondre_au_terminal(reponse)[0], 1) + + def test_la_question_annonce_le_defaut(self): + self.assertIn("Lancer ? [o/N]", repondre_au_terminal("n")[1]) + + def test_sans_terminal_la_reponse_est_non(self): + proc = subprocess.run( + ["bash", "-c", SCRIPT], + cwd=RACINE, + input="o\n", + capture_output=True, + text=True, + check=False, + ) + self.assertEqual(proc.returncode, 1) + self.assertNotIn("[o/N]", proc.stdout) + + +if __name__ == "__main__": + unittest.main()