[ADD] todo: todo.sh launcher, shown by make instead of install.sh

make prints the recipe it runs, and « ./install.sh » offered an installation
to read where one opens a menu. The launcher adds nothing but a name and
passes every argument to install.sh, which alone keeps picking an interpreter
able to READ the code and stays the entry point of a fresh machine.
A test caps todo.sh at six lines of code and refuses it python3, bin/python
and sort -V: two copies of that choice would drift. The changelog names the
launcher.
Checked: chain run through to the menu, exit 0; 9 tests.

--- FR ---

[ADD] todo : lanceur todo.sh, que make affiche à la place d'install.sh

make affiche la recette qu'il exécute, et « ./install.sh » y donnait à lire
une installation là où l'on ouvre un menu. Le lanceur n'ajoute qu'un nom et
passe chaque argument à install.sh, qui garde seul le choix d'un interpréteur
capable de LIRE le code et reste le point d'entrée d'une machine neuve.
Un test plafonne todo.sh à six lignes de code et lui refuse python3,
bin/python et sort -V : deux copies de ce choix dériveraient. Le changelog
nomme le lanceur.
Vérifié : chaîne lancée jusqu'au menu, sortie 0 ; 9 tests.

Assisted-by: Claude Opus 5
Assisted-by: Claude Opus 5.5
This commit is contained in:
Mathieu Benoit 2026-09-25 01:29:53 -04:00
parent 0f0088e7ae
commit a8f7512130
6 changed files with 127 additions and 7 deletions

View file

@ -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` go through `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. 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:<version>-slim-<suite>`: 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` passent par `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. 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:<version>-slim-<version Debian>` : 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

View file

@ -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` passent par `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. 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:<version>-slim-<version Debian>` : 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

View file

@ -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` go through `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. 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:<version>-slim-<suite>`: 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

View file

@ -2,8 +2,11 @@
# TODO #
########
# Par install.sh, et non todo.py directement : lui seul choisit un interpréteur
# capable de LIRE le code avant de le lancer, et pose le venv s'il manque.
# Par todo.sh, et non todo.py directement : la chaîne passe par install.sh,
# qui choisit un interpréteur capable de LIRE le code avant de le lancer et
# pose le venv s'il manque. Le nom dit ce qui se passe : make affiche la
# recette, et « ./install.sh » y donnait à lire une installation là où l'on
# ouvre un menu.
.PHONY: todo
todo:
./install.sh
./todo.sh

View file

@ -0,0 +1,99 @@
#!/usr/bin/env python3
# © 2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
"""La chaîne de lancement de TODO tient-elle en un seul endroit ?
« make todo » passe par todo.sh, qui passe par install.sh, qui choisit un
interpréteur capable de LIRE le code avant de le lui donner. Trois fichiers,
mais UNE seule décision : elle vit dans install.sh, qui est aussi le point
d'entrée d'une machine neuve.
Ce que ces tests gardent :
- todo.sh reste un NOM, et non une seconde copie du choix d'interpréteur.
Deux copies dérivent, et celle qui dérive est toujours celle qu'on ne lit
pas — ici, le chemin qu'emprunte « make » ;
- les arguments traversent. Un lanceur qui les avale rend « ./todo.sh » et
« ./install.sh » différents sans le dire ;
- todo.sh se place dans son propre répertoire : « make -C » et un appel par
chemin absolu partent d'ailleurs, et les chemins relatifs d'install.sh
n'y survivraient pas.
"""
import re
import subprocess
import unittest
from pathlib import Path
RACINE = Path(__file__).resolve().parents[1]
TODO_SH = RACINE / "todo.sh"
INSTALL_SH = RACINE / "install.sh"
MAKEFILE = (RACINE / "conf/make.todo.Makefile").read_text(encoding="utf-8")
SOURCE = TODO_SH.read_text(encoding="utf-8")
class TestCible(unittest.TestCase):
def test_make_todo_lance_todo_sh(self):
"""La recette s'affiche : elle doit nommer ce qu'elle fait."""
recette = re.search(r"^todo:\n\t(.+)$", MAKEFILE, re.MULTILINE)
self.assertIsNotNone(recette, MAKEFILE)
self.assertEqual("./todo.sh", recette.group(1).strip())
def test_make_n_rend_la_meme_chose(self):
sortie = subprocess.run(
["make", "-n", "todo"],
cwd=RACINE,
capture_output=True,
text=True,
)
self.assertEqual(0, sortie.returncode, sortie.stderr)
self.assertIn("./todo.sh", sortie.stdout)
class TestLanceur(unittest.TestCase):
def test_il_est_executable(self):
self.assertTrue(TODO_SH.exists())
self.assertTrue(TODO_SH.stat().st_mode & 0o111)
def test_il_delegue_a_install_sh(self):
self.assertTrue(INSTALL_SH.exists())
self.assertIn("./install.sh", SOURCE)
def test_les_arguments_traversent(self):
self.assertIn('"$@"', SOURCE)
def test_il_se_place_dans_son_repertoire(self):
self.assertIn('cd "$(dirname "$0")"', SOURCE)
def test_il_ne_choisit_pas_l_interprete_lui_meme(self):
"""Le choix vit dans install.sh. Le recopier ici en ferait deux, et
celle qui dérive serait celle que « make » emprunte."""
for marqueur in ("python3", "pyvenv.cfg", "sort -V", "bin/python"):
with self.subTest(marqueur=marqueur):
self.assertNotIn(marqueur, SOURCE)
def test_il_reste_court(self):
"""Au-delà d'une poignée de lignes, ce n'est plus un nom mais une
seconde implémentation."""
code = [
ligne
for ligne in SOURCE.splitlines()
if ligne.strip() and not ligne.lstrip().startswith("#")
]
self.assertLessEqual(len(code), 6, code)
class TestSyntaxe(unittest.TestCase):
def test_les_deux_scripts_se_lisent(self):
for script in (TODO_SH, INSTALL_SH):
with self.subTest(script=script.name):
sortie = subprocess.run(
["bash", "-n", str(script)],
capture_output=True,
text=True,
)
self.assertEqual(0, sortie.returncode, sortie.stderr)
if __name__ == "__main__":
unittest.main()

18
todo.sh Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
#
# Lancer TODO.
#
# Le travail est dans install.sh, qui choisit un interpréteur capable de LIRE
# le code avant de le lui donner. Ce fichier n'ajoute qu'un NOM, et c'est sa
# seule raison d'être : make affiche la recette qu'il exécute, et « ./install.sh »
# y donnait à lire une installation là où l'on ouvre un menu. Deux gestes
# distincts méritent deux noms, même quand ils mènent au même endroit.
#
# Les arguments passent tels quels : « ./todo.sh --help » vaut « ./install.sh
# --help ».
cd "$(dirname "$0")" || exit 1
exec ./install.sh "$@"