Commit graph

354 commits

Author SHA1 Message Date
0f0088e7ae [FIX] install.sh: probe the version by running code, not with -V
"python -V" answers before loading the standard library. A venv whose
interpreter cannot find it, such as a checkout mounted from another machine,
still reported its version and passed as healthy. install.sh then handed TODO
to it, which died on "No module named 'encodings'", and the "venv built
elsewhere" guard never printed. The probe now runs "import sys": an
interpreter that cannot start returns nothing.
Checked: a fake python that answers -V but fails otherwise makes the test
fail on the old code and pass with the fix.

--- FR ---

[FIX] install.sh : sonder la version en exécutant du code, non par -V

« python -V » répond avant de charger la bibliothèque standard. Un venv dont
l'interpréteur ne la trouve pas, comme un checkout monté depuis une autre
machine, rendait donc sa version et passait pour sain. install.sh lui passait
alors TODO, qui mourait sur « No module named 'encodings' », et le garde
« venv bâti ailleurs » ne s'affichait jamais. La sonde exécute maintenant
« import sys » : un interpréteur qui ne démarre pas ne rend rien.
Vérifié : un faux python sain à -V mais mort ailleurs fait échouer le test
sur l'ancien code et le réussit avec la correction.

Assisted-by: Claude Opus 5.5
2026-09-25 01:29:53 -04:00
cfbdd9406e [REF] format : passer l'outillage et les tests sous ruff
Le formateur de ce dépôt est ruff depuis qu'il remplace black, qui ne connaît
aucune cible au-delà de py313 ; ce passage applique sa norme à l'arbre entier,
d'un coup, pour qu'aucun commit de fond n'ait à porter du style. L'écart tient
presque entièrement aux chaînes coupées à la main que ruff recolle quand elles
tiennent sur une ligne, et aux « with » multiples qu'il regroupe : aucune
valeur ne change, et les clés de traduction non plus.
Vérifié : la suite unitaire reste verte après le passage, et le contrôle de
syntaxe ne signale rien.

--- EN ---

This repository's formatter is ruff since it replaced black, which knows no
target beyond py313; this pass applies its standard to the whole tree at once,
so that no substantive commit has to carry style. The difference is almost
entirely the hand-split strings ruff joins back when they fit on one line, and
the multiple "with" it merges: no value changes, nor do the translation keys.
Checked: the unit suite stays green after the pass, and the syntax check
reports nothing.

Assisted-by: Claude Opus 5
2026-09-24 14:40:38 -04:00
a1f1065962 [UPD] docker : une seule base, bookworm, pour toutes les versions d'Odoo
Odoo 13, 14 et 15 bâtissaient encore sur bullseye, dont le dépôt de sécurité
est démantelé. Rien ne les y obligeait : la base est
« python:<version>-slim-<version Debian> », donc l'interpréteur vient de
l'image officielle et non de Debian — les variantes bookworm existent jusqu'à
3.7.17. L'aiguillage portait en outre une branche « buster » INATTEIGNABLE,
sa condition étant la négation de celle qui la précédait ; elle part avec le
reste. Le build de wkhtmltopdf suit la version : celui de bullseye réclame
libssl1.1, absente de bookworm.
Vérifié : les images 3.7.17 et 3.8.20 slim-bookworm existent, le .deb bookworm
rend l'empreinte déclarée, et ses quinze dépendances sont dans bookworm.

--- EN ---

Odoo 13, 14 and 15 still built on bullseye, whose security repository is
dismantled. Nothing required it: the base is
"python:<version>-slim-<suite>", so the interpreter comes from the official
image and not from Debian — the bookworm variants exist down to 3.7.17. The
switch also carried an UNREACHABLE "buster" branch, its condition being the
negation of the one before it; it goes with the rest. The wkhtmltopdf build
follows the suite: bullseye's requires libssl1.1, absent from bookworm.
Checked: the 3.7.17 and 3.8.20 slim-bookworm images exist, the bookworm .deb
matches the declared checksum, and its fifteen dependencies are in bookworm.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00
44bc1b093c [FIX] qemu : reprendre un téléchargement d'image coupé
Une image de VM pèse un demi-gigaoctet : une coupure y est bien plus probable
que sur une page. Le contrôle de complétude existait — sans lui un .part
tronqué passait pour une image, donnant un qcow2 valide mais VIDE — mais il
jetait les octets reçus et renvoyait l'utilisateur à un « curl -C - » tapé à
la main. Le transfert se reprend désormais sur le même miroir, trois essais,
par un « Range » qui demande la suite ; on ne change de miroir qu'ensuite. Un
serveur qui ignore le Range rend le fichier entier, et l'on repart alors de
zéro plutôt que de doubler les octets déjà là.
Vérifié : 5 tests contre un serveur qui coupe au tiers, image reconstituée à
l'octet près, et le .part tronqué part quand les reprises s'épuisent.

--- EN ---

A VM image weighs half a gigabyte: an interruption is far likelier there than
on a page. The completeness check existed — without it a truncated .part
passed for an image, giving a valid but EMPTY qcow2 — but it threw away the
bytes received and sent the user back to a hand-typed "curl -C -". The
transfer now resumes on the same mirror, three attempts, through a "Range"
asking for the rest; only then does it change mirror. A server ignoring the
Range returns the whole file, and we restart from zero rather than doubling
the bytes already there.
Checked: 5 tests against a server cutting at one third, image reassembled byte
for byte, and the truncated .part goes when the retries run out.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00
5411b102a9 [ADD] déploiement : un proxy SOCKS par SSH, et son mode d'emploi
« -L » relaie UN service ; « -D » ouvre un relais SOCKS, par lequel le
navigateur atteint n'importe quelle destination depuis la machine distante —
une interface qui n'écoute que sur sa boucle locale, un hôte de son réseau sans
route depuis ici. Le port par défaut est 1080, modifiable, et contrôlé libre
avant d'ouvrir. La saisie de l'adresse est celle de sshfs, désormais partagée :
l'alias de ~/.ssh/config part tel quel à ssh, faute de quoi son ProxyJump se
perdrait et une VM imbriquée deviendrait injoignable. Le réglage de Firefox
s'affiche AVANT le lancement, la commande ne rendant la main qu'au Ctrl+C.
Vérifié : 9 tests neufs, et la numérotation du menu que deux autres gardent.

--- EN ---

"-L" relays ONE service; "-D" opens a SOCKS relay, through which the browser
reaches any destination from the remote machine — an interface listening only
on its loopback, a host of its network with no route from here. The default
port is 1080, changeable, and checked free before opening. The address prompt
is sshfs's, now shared: the ~/.ssh/config alias goes to ssh as is, failing
which its ProxyJump would be lost and a nested VM become unreachable. The
Firefox settings print BEFORE the launch, the command only returning on
Ctrl+C.
Checked: 9 new tests, and the menu numbering two others guard.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00
42e10bcced [UPD] install : le venv d'outillage passe en Python 3.14.7
install_erplibre.sh bâtissait .venv.erplibre avec le python3 du système, quelle
que soit sa version : un 3.10 y donnait un venv que l'outillage ne sait pas
charger. Il délègue à install_venv.sh, qui obtient la version par mise, pyenv
ou la distribution, et rebâtit un venv hors service. Le PATCH ne borne que le
venv d'Odoo, dont le pyproject exige « >=3.12.10,<3.13 » : l'exiger ailleurs
écartait un Python de distribution d'un cran en retard — NixOS 25.11 livre
3.14.2 — et faisait compiler CPython pour rien. Le module NixOS déclare
désormais les DEUX Python, substitués depuis les fichiers de version.
Vérifié : 15 tests sur des venvs factices, conservés, rebâtis ou refusés selon
leur état, et un chemin sans pyvenv.cfg garde son contenu.

--- EN ---

install_erplibre.sh built .venv.erplibre with the system python3, whatever its
version: a 3.10 gave a venv the tooling cannot load. It delegates to
install_venv.sh, which obtains the version through mise, pyenv or the
distribution, and rebuilds a venv out of service. The PATCH bounds only Odoo's
venv, whose pyproject requires ">=3.12.10,<3.13": requiring it elsewhere turned
away a distribution Python one step behind — NixOS 25.11 ships 3.14.2 — and
compiled CPython for nothing. The NixOS module now declares BOTH Pythons,
substituted from the version files.
Checked: 15 tests on stub venvs, kept, rebuilt or refused by their state, and a
path without pyvenv.cfg keeps its content.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00
7cdcd8a07a [ADD] garde-fou : signaler au commit un source trop récent pour le dépôt
Une syntaxe plus récente que le Python du dépôt ne casse qu'au chargement, et
aucun outil en place ne la voyait : la cible de black borne ce qu'il ÉCRIT,
jamais ce qu'il accepte, et flake8 analyse avec l'interpréteur courant. Le
contrôle compile les fichiers indexés avec un vrai interpréteur de la version
déclarée, trouvé sans réseau par mise, pyenv ou le PATH, et se prend lui-même
quand il y tourne déjà. Sans interpréteur, il DIT qu'il n'a pas vérifié plutôt
que de laisser croire le contraire. Le hook relaie, sans bloquer le commit, et
écarte les dépôts rapatriés dont ce dépôt ne répond pas.
Vérifié : les f-strings PEP 701 de la révision précédente sont signalées, rien
ne l'est sur l'arbre courant, et l'absence du fichier de version se dit.

--- EN ---

A syntax newer than the repository's Python only breaks at load, and no tool in
place saw it: black's target bounds what it WRITES, never what it accepts, and
flake8 parses with the running interpreter. The check compiles staged files
with a real interpreter of the declared version, found without network through
mise, pyenv or the PATH, and takes itself when it already runs under it.
Without one, it SAYS it did not check rather than implying it did. The hook
relays, without blocking the commit, and skips the checked-out repositories
this one does not answer for.
Checked: the PEP 701 f-strings of the previous revision are reported, nothing
is on the current tree, and a missing version file is announced.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00
416032fe7b [FIX] test long cache : ne compter vue qu'une URL vraiment livrée
Un même paquet vit sous deux chemins selon le miroir, et la clé du magasin
porte le chemin : la première VM peut recevoir « 504 » sur l'un — amont jugé
muet — puis être servie du disque par l'autre. Rien n'entre alors au magasin
sous le premier chemin, et le téléchargement honnête de la seconde y était
compté en faute, faisant échouer une mesure où le cache avait tout servi. Une
URL n'est « déjà vue » que si la première VM en a obtenu les octets ; un
statut absent vaut livré, les journaux d'avant ne l'écrivant pas toujours.
Vérifié : trois tests, deux mutations attrapées.

--- EN ---

One package lives under two paths depending on the mirror, and the store key
carries the path: the first VM can get « 504 » on one — upstream judged mute —
then be served from disk by the other. Nothing enters the store under the
first path, and the second VM's honest download was counted a fault there,
failing a measurement where the cache had served everything. A URL is « already
seen » only if the first VM obtained its bytes; a missing status counts as
delivered, older logs not always writing it. Checked: three tests, two
mutations caught.

Assisted-by: Claude Opus 5
2026-09-21 05:39:38 -04:00
96ae77485c [FIX] déploiement qemu : reprendre une image périmée avant d'abandonner
Une image gardée vieillit : le répertoire « latest » d'une distribution avance
à chaque version mineure, et la somme publiée cesse de décrire celle du
disque. La vérification la supprimait puis sortait en erreur — une campagne
entière, trois VM, perdue pour une péremption qu'un seul téléchargement
répare. Un écart sur une image DÉJÀ présente la fait désormais reprendre une
fois ; un second écart porte sur des octets fraîchement téléchargés, et arrête
tout. Sans liste de miroirs, et pour des sommes injoignables, rien ne change.
Vérifié : quatre tests de comportement, deux mutations attrapées.

--- EN ---

A kept image ages: a distribution's « latest » directory moves with each point
release, and the published sum stops describing the one on disk. The check
deleted it and exited — a whole campaign, three VMs, lost to a staleness one
download repairs. A mismatch on an image ALREADY present now makes it fetched
once more; a second mismatch is on freshly downloaded bytes, and stops
everything. With no mirror list, and for unreachable sums, nothing changes.
Checked: four behaviour tests, two mutations caught.

Assisted-by: Claude Opus 5
2026-09-21 05:39:38 -04:00
ce1bf5f4d1 [FIX] motd : replier la glose au lieu de déborder de 80 colonnes
Le guide tenait dans 80 colonnes nu, et rendait 103 dès qu'une VM portait
ses outils et un bureau. Le cadre débordait alors, le terminal repliait où
il voulait, et l'alignement en deux colonnes — seule chose qui rend un
guide lisible d'un coup d'œil — disparaissait.
La mise en page porte désormais la règle, plutôt que la longueur des textes
qui n'aurait tenu que jusqu'au prochain outil : la glose se replie sous sa
colonne, et passe sous sa commande quand celle-ci ne laisse plus de quoi
écrire. L'épreuve de largeur ne couvrait que le guide nu ; elle couvre le
guide équipé, qui est celui où le débordement vivait.

--- EN ---

The guide fitted in 80 columns bare, and rendered 103 as soon as a VM
carried its tools and a desktop. The frame then overflowed, the terminal
wrapped wherever it liked, and the two-column alignment — the only thing
making a guide readable at a glance — was gone.
The layout now carries the rule, rather than the length of texts which would
have held only until the next tool: a gloss wraps under its column, and
moves below its command when that command leaves no room. The width check
covered only the bare guide; it now covers the equipped one, which is where
the overflow lived.

Assisted-by: Claude Opus 5
2026-09-17 09:31:44 -04:00
270aedd790 [ADD] i18n : refuser une clé de traduction écrite deux fois
Python bâtit un dictionnaire littéral de haut en bas : la dernière
occurrence gagne. Une clé posée deux fois fait donc qu'un menu affiche la
traduction d'un autre, et rien ne le signale — ni au chargement, ni à
l'exécution, ni au test qui exerce le premier menu.
Le contrôle lit le fichier par son ARBRE et non par une expression
régulière, laquelle n'attrapait que la forme multiligne quand la collision
peut s'écrire sur une seule. Vert sur l'état actuel, donc aucune clé en
double aujourd'hui ; éprouvé sur les deux formes, qu'il refuse.

--- EN ---

Python builds a literal dictionary top to bottom: the last occurrence wins.
A key written twice therefore makes one menu show another's translation, and
nothing reports it — not at load, not at run, not in the test exercising the
first menu.
The check reads the file by its TREE rather than by a regular expression,
which only caught the multi-line form when the collision can be written on
one. Green on the current state, so no duplicate key today; exercised on
both forms, which it refuses.

Assisted-by: Claude Opus 5
2026-09-17 09:31:44 -04:00
36fa84b037 [FIX] qemu plan : retirer l'avertissement que NixOS ne s'installe pas
Le dernier écran avant de créer portait « ERPLibre ne s'installe pas encore
sur NixOS : la VM est créée, l'installation échoue ». Il s'y installe, de
bout en bout : clone, make install_os, make install_odoo_18, Odoo enregistré
en service et servant 8069.
Un avertissement périmé à cet endroit ne fait pas qu'induire en erreur, il
décourage d'essayer ce qui marche — et c'est la seule ligne de l'écran qui
parle d'un échec. Sa traduction part avec lui, une entrée que personne
n'appelle vieillissant sans qu'on le sache.

--- EN ---

The last screen before creating carried « ERPLibre does not install on NixOS
yet: the VM is created, the install fails ». It does install, end to end:
clone, make install_os, make install_odoo_18, Odoo registered as a service
and serving 8069.
A stale warning there does not merely mislead, it discourages trying what
works — and it is the screen's only line that speaks of a failure. Its
translation goes with it, an entry nobody calls ageing unnoticed.

Assisted-by: Claude Opus 5
2026-09-17 04:04:25 -04:00
8533690e0e [FIX] proxmox : porter la décision du miroir apt au journal de la VM
Les décisions que l'hôte prend avant de lancer sont gardées pour le journal
de la VM, celui qu'on rouvre le lendemain quand l'installation a échoué.
Celle du miroir apt restait sur la console, qui a défilé depuis.
C'est pourtant là qu'on la cherche : le magasin range ses index sous l'HÔTE
demandé, et une VM qui en réclame un autre ne retrouve rien de ce qui est
gardé — hors ligne, l'installation échoue plus bas sur des dépendances
introuvables, un message qui accuse le dépôt et jamais le miroir. Le nom du
miroir posé, ou le code de l'échec, rejoignent donc les autres notes.

--- EN ---

The decisions the host takes before launching are kept for the VM's log, the
one reopened the next day when the install has failed. The apt mirror's
stayed on the console, which has scrolled away since.
Yet that is where it is looked for: the store files its indexes under the
HOST asked for, and a VM asking for another finds nothing of what is kept —
offline, the install fails further down on unresolvable dependencies, a
message that blames the repository and never the mirror. The pinned mirror's
name, or the failure's code, therefore join the other notes.

Assisted-by: Claude Opus 5
2026-09-17 03:48:09 -04:00
34ac3e775d [FIX] proxmox : donner l'URL au remède qui retire l'entrée du cache
Quand la somme d'une image ne correspond pas, le message nomme le geste à
faire EN PREMIER, cet hôte pouvant être lui-même derrière le cache. Il
rendait « printf %s | … --oublie » : une f-string dont le marqueur était
tombé, et un printf sans opérande. Recopiée, la commande lit un flux vide,
n'efface rien et sort à 0 — on croit avoir purgé, on efface l'image, on
relance, et le magasin ressert les mêmes octets indéfiniment. Un remède qui
réussit sans rien faire est pire que pas de remède.
La voie libvirt donnait déjà l'URL ; celle-ci la donne aussi. Le test la
joue dans un vrai shell, la ligne traversant deux quotages.

--- EN ---

When an image's checksum does not match, the message names the step to take
FIRST, this host possibly being behind the cache itself. It printed
« printf %s | … --oublie »: an f-string whose placeholder had been lost, and
a printf with no operand. Copied, the command reads an empty stream, erases
nothing and exits 0 — you believe you purged, you erase the image, you
retry, and the store serves the same bytes for ever. A remedy that succeeds
without doing anything is worse than none.
The libvirt path already gave the URL; this one now does too. The test plays
it in a real shell, the line crossing two levels of quoting.

Assisted-by: Claude Opus 5
2026-09-17 03:48:09 -04:00
7c860ff047 [ADD] cache qemu : NixOS apprend l'autorité, et le hors ligne s'ouvre
NixOS était SOUSTRAIT du cache faute d'ancre de confiance par fichier, ce
qui lui fermait le hors ligne : le magasin est alors la seule source, et
l'exception ne laisse rien. Une déclaration arriverait trop tard, la
première reconstruction étant le premier téléchargement.
L'autorité est donc POINTÉE, consommateur par consommateur, et
l'environnement se perd à trois frontières : nix-daemon, activé par
socket, qu'un fragment sous /run/systemd/system atteint ; sudo, que
« env_keep » traverse — le nix de root parle droit au magasin local et
télécharge lui-même ; et la session ssh, ouverte une seconde avant que
cloud-init n'écrive le faisceau. Vérifié : installation complète, 0 refus.

--- EN ---

NixOS was EXEMPTED from the cache for want of a per-file trust anchor,
which closed offline deployment to it: the store is then the only source,
and an exemption leaves nothing. A declaration would come too late, the
first rebuild being the first download.
The authority is therefore POINTED AT, consumer by consumer, and the
environment is lost at three boundaries: nix-daemon, socket-activated,
reached by a drop-in under /run/systemd/system; sudo, crossed by
« env_keep » — root's nix talks straight to the local store and downloads
itself; and the ssh session, opened one second before cloud-init writes
the bundle. Checked: a complete install, 0 refusals.

Assisted-by: Claude Opus 5
2026-09-16 21:33:10 -04:00
0105cf16e5 [FIX] cache et guide : suivre la traduction du binaire et le hors ligne
Le rebase apporte trois choses auxquelles ce travail devait s'accorder.
Les messages du binaire se traduisent désormais : ceux du refus de place
et de l'aide de --oublie passent par T(), avec leur entrée au catalogue —
deux garde-fous refusent sinon un message sans traduction ou l'inverse.

Soustraire l'hôte au cache est REFUSÉ hors ligne : le magasin est alors la
seule source, et l'excepter ne le ferait pas télécharger en direct, cela
le priverait de tout, ses propres paquets compris.

Et le guide de connexion annonce les outils sur la voie Proxmox aussi ; il
n'y était plus écrit du tout, l'argument manquant étant avalé.

--- EN ---

The rebase brings three things this work had to align with. The binary's
messages are now translatable: the out-of-space refusal and the --oublie
help go through T(), with their catalogue entry — two guard rails
otherwise refuse a message without a translation, or the reverse.

Exempting the host from the cache is REFUSED offline: the store is then
the only source, and exempting it would not make it download directly, it
would leave it with nothing, its own packages included.

And the connection guide announces the tools on the Proxmox path too; it
was no longer written there at all, the missing argument being swallowed.

Assisted-by: Claude Opus 5
2026-09-16 21:32:22 -04:00
1f5bdceb2a [ADD] guide de connexion : les outils posés, et ce que NixOS change
On entrait en ssh sans savoir que nix, PyCharm ou la forge étaient là, ni
par quelle commande s'en servir : le guide ne connaissait que la
distribution, ERPLibre et la présence d'un bureau. La liste vient du menu
FILTRÉE par la machine — Android Studio n'existe qu'en x86_64, PyCharm
veut un bureau — et chaque ligne est confrontée au code qui l'installe.
Le bloc NixOS porte la racine du dépôt, seule ligne qui n'était ni absolue
ni commande, et trois lignes qu'on ne devine pas : « --rollback », qui
défait une déclaration fautive ; « nixos-version » ; et « ls /bin », vide
alors que /bin/bash s'exécute. Une recherche de paquet a été essayée puis
écartée, muette sur cette image.

--- EN ---

One logged in over ssh with no way to know nix, PyCharm or the forge were
there, nor which command to use: the guide knew only the distribution,
ERPLibre and whether a desktop was installed. The list comes from the menu
FILTERED by the machine — Android Studio is x86_64 only, PyCharm wants a
desktop — and every line is checked against the code that installs it.
The NixOS block carries the checkout root, its only line neither absolute
nor a command, and three lines nobody guesses: « --rollback », which
undoes a faulty declaration; « nixos-version »; and « ls /bin », empty
while /bin/bash runs. A package search was tried then dropped, silent on
this image.

Assisted-by: Claude Opus 5
2026-09-16 21:32:22 -04:00
3d0ed8915f [FIX] qemu images : vérifier les sommes publiées, partout et par défaut
La vérification existait sous un drapeau, et pour Ubuntu SEULEMENT : les
autres images arrivaient sans que rien ne les regarde, alors que leurs
éditeurs publient tous une somme. Six y entrent, relevées sur les dépôts
plutôt que devinées — Debian est en sha512, les familles RHEL nomment
« CHECKSUM », Rocky l'écrit en forme BSD, Arch et openSUSE posent une
somme par image. Fedora en reste dehors, son fichier portant un numéro de
construction que l'URL de l'image ne donne pas.

Injoignable n'arrête plus rien, un écart si. Le menu du cache sait aussi
oublier une URL, « --detient » faisant l'aperçu.

--- EN ---

Verification existed behind a flag, and for Ubuntu ONLY: the other images
arrived with nothing looking at them, though every publisher ships a sum.
Six now enter, read off the repositories rather than guessed — Debian is
sha512, the RHEL families name it "CHECKSUM", Rocky writes the BSD form,
Arch and openSUSE ship a sum per image. Fedora stays out, its file
carrying a build number the image URL does not give.

Unreachable no longer stops anything, a mismatch does. The cache menu can
also forget one URL, "--detient" serving as the preview.

Assisted-by: Claude Opus 5
2026-09-16 21:32:03 -04:00
dc878e30d0 [FIX] nixos : afficher le guide de connexion, appliquer la locale
Le déploiement écrit /etc/motd partout et compte sur pam_motd pour le
montrer — vrai des quatre images cloud, faux ici : sshd rend « printmotd
no » et le PAM n'en contient aucun. Le guide était écrit, complet, et
personne ne le lisait. Il gagne un bloc propre à NixOS, dont le piège
qu'il existe pour dire — /etc/nixos/erplibre.nix est réécrit par
« make install_os », et ce qu'on y ajoute disparaît sans un mot.

La locale demandée ne s'appliquait pas : cloud-init passe par locale-gen
et update-locale, absents ici. Mesuré — fr_CA demandé, en_US obtenu. Rien
n'est imposé à une NixOS qu'on avait déjà.

--- EN ---

Deployment writes /etc/motd everywhere and relies on pam_motd to show it —
true of the four cloud images, false here: sshd returns "printmotd no" and
the PAM stack holds none. The guide was written, complete, and nobody read
it. It gains a NixOS block, including the trap it exists to name —
/etc/nixos/erplibre.nix is rewritten by "make install_os", and what you
add there vanishes without a word.

The requested locale did not apply: cloud-init goes through locale-gen and
update-locale, absent here. Measured — fr_CA asked, en_US obtained.
Nothing is imposed on a NixOS one already had.

Assisted-by: Claude Opus 5
2026-09-16 21:32:03 -04:00
48a1d5ce20 [FIX] cache et journal : hôte imbriqué soustrait, décision écrite
Un invité sans magasin de confiance ne peut RIEN recevoir, et sur un hôte
Proxmox c'est l'HÔTE qu'il faut excepter : imbriqué, l'invité sort masqué
derrière lui et le pont ne voit jamais sa propre adresse. Mesuré depuis
l'invité : code 000 et vérification SSL 19, puis 200 et 0. Sans cela le
gestionnaire de paquets se rabattait sur 564 dérivations à construire.

Le journal par VM ne portait AUCUN message du menu : la cause vivait sur
une console qui défile, et le fichier qu'on rouvre après l'échec n'avait
que le symptôme. Et « db_drop_all » annonçait détruites des bases qui ne
l'étaient pas, son code de retour jeté.

--- EN ---

A guest with no trust store can receive NOTHING, and on a Proxmox host it
is the HOST that must be exempted: nested, the guest leaves masqueraded
behind it and the bridge never sees its own address. Measured from the
guest: code 000 and SSL verification 19, then 200 and 0. Without it the
package manager fell back to building 564 derivations.

The per-VM log carried NO message from the menu: the cause lived on a
console that scrolls away, and the file reopened after failure held only
the symptom. And "db_drop_all" announced databases as dropped that were
not, its exit status discarded.

Assisted-by: Claude Opus 5
2026-09-16 21:32:03 -04:00
c1f2f8db7f [FIX] nixos : pare-feu ouvert, et ce qu'on appelle par son nom déclaré
NixOS active un pare-feu par défaut ; aucune des quatre autres images
cloud n'en active. Le service écoutait bien et répondait en local, mais
l'hôte ne recevait RIEN — pas un refus, un silence jusqu'au délai — et le
suivi déclarait Odoo absent sur une machine où il tournait. Mesuré : 000
après 12 s, puis 303 en 9 ms.

Un système déclaratif n'a que ce qui est écrit : xmlsec1, sans lequel
Odoo refuse d'installer auth_saml ; parallel et shfmt, appelés par leur
nom nu ; growpart, dont l'absence faisait rendre 0 à un agrandissement qui
n'agrandissait rien ; et l'agent invité, qui venait de l'image.

--- EN ---

NixOS enables a firewall by default; none of the four other cloud images
does. The service did listen and answered locally, but the host received
NOTHING — not a refusal, silence until the timeout — and the monitor
declared Odoo absent on a machine where it was running. Measured: 000
after 12 s, then 303 in 9 ms.

A declarative system has only what is written: xmlsec1, without which Odoo
refuses to install auth_saml; parallel and shfmt, called by bare name;
growpart, whose absence made a disk grow return 0 while growing nothing;
and the guest agent, which came from the image.

Assisted-by: Claude Opus 5
2026-09-16 21:32:03 -04:00
e0624d9aaa [ADD] cache : retirer une seule entrée du magasin, par URL
Rien n'invalidait une entrée dont la somme ne correspond pas : le magasin
continuait de la servir, et retélécharger ne changeait rien puisque c'est
lui qui répond. Les deux purges existantes ne l'atteignent pas — l'une
efface tout, l'autre saute ce qui est récent alors que chaque service
remet cette date, si bien qu'un objet empoisonné qui sert ne vieillit
jamais.

Symétrique de « --detient » : mêmes lignes, mêmes fonctions de clé, mêmes
refus, et un objet présent qui résiste est dit refusé plutôt qu'oublié.
Mesuré : garde, puis oublié 53080, puis absent.

--- EN ---

Nothing invalidated an entry whose checksum does not match: the store kept
serving it, and re-downloading changed nothing since the store is what
answers. Neither existing purge reaches it — one erases everything, the
other skips what is recent while every service resets that date, so a
poisoned object that serves never ages.

Symmetric to "--detient": same lines, same key functions, same refusals,
and a present object that resists is reported refused rather than
forgotten. Measured: held, then forgotten 53080, then absent.

Assisted-by: Claude Opus 5
2026-09-16 21:32:03 -04:00
64a8e6400f [FIX] nixos : le service ERPLibre déclaré, démarré quand Odoo est là
Trois obstacles enchaînés, aucun visible sans une vraie machine. /etc est
généré depuis le store : le « tee » d'une unité y rend « Read-only file
system », et l'installation échouait à sa DERNIÈRE étape, après que le
clone, le venv et un démarrage d'Odoo avaient réussi. ExecStart=/bin/bash
rendait 203/EXEC, /bin étant un montage FUSE que systemd ne voit pas ; et
le PATH d'une unité n'a pas bash, que réclament les shebangs de run.sh.
Déclarée, l'unité est démarrée par la reconstruction — qui a lieu pendant
« make install_os », quand la source d'Odoo n'arrive qu'après : une
condition la fait sauter plutôt qu'échouer 21 fois de suite. Mesuré :
service actif, 8069 servi par lui, login en 303.

--- EN ---

Three chained obstacles, none visible without a real machine. /etc is
generated from the store: « tee » of a unit there returns « Read-only file
system », and the install failed at its LAST step, after the clone, the
venv and an Odoo start had all succeeded. ExecStart=/bin/bash returned
203/EXEC, /bin being a FUSE mount systemd does not see; and a unit's PATH
carries no bash, which run.sh's shebangs ask for.
Declared, the unit is started by the rebuild — which happens during
« make install_os », while Odoo's source lands later: a condition makes it
skip rather than fail 21 times over. Measured: service active, 8069 served
by it, login in 303.

Assisted-by: Claude Opus 5
2026-09-16 21:31:50 -04:00
2f08ec0890 [FIX] long_test et cache git : sudo pour créer, seuil nommé au refus
Les tests longs lançaient la CLI sans privilège, alors que le dossier des
images appartient à root en 755 : arrêt à l'étape 1 sur « Permission
refusée », avant d'avoir rien créé. Ils ne pouvaient donc pas tourner du
tout — pas même échouer utilement. Le menu passe sudo depuis toujours ;
c'est cette forme qui est reprise, et jamais à blanc.

Le refus de place d'un miroir reprenait un champ que tout appelant laisse
à zéro : « moins de 0 o libres » n'annonce aucun seuil et ne dit pas ce
qui a été mesuré. Treize tests héritaient du plancher de production,
10 Gio, et rougissaient sur un /tmp en tmpfs.

--- EN ---

The long tests ran the CLI unprivileged, while the image directory is
owned by root, mode 755: they stopped at step 1 on "permission denied",
before creating anything. They therefore could not run at all — not even
fail usefully. The menu has always passed sudo; that is the form taken
here, and never on a dry run.

A mirror's refusal for lack of space echoed a field every caller leaves at
zero: "less than 0 B free" announces no threshold and does not say what
was measured. Thirteen tests inherited the production floor, 10 GiB, and
went red on a tmpfs /tmp.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
83b9674176 [REF] qemu firmware : une table pour les deux voies, fuseau décodé
Deux tables opposées vivaient dans le même fichier : l'une forçait le
BIOS, l'autre l'UEFI, chacune lue d'un seul côté. Elles pouvaient se
contredire sans que rien ne le dise. FIRMWARE_IMPOSE les remplace, et les
deux suites passent inchangées. « --bios » l'emportait sur un fait
physique : forcé sur une image sans secteur d'amorçage, il donnait une VM
« running » à console muette. Il est ignoré, et l'appelant l'apprend.

Le fuseau était lu en UTF-8 strict sous un « except OSError » : un octet
qui n'en est pas faisait échouer tout le déploiement pour une traduction
de confort.

--- EN ---

Two opposing tables lived in one file: one forced BIOS, the other UEFI,
each read by one side only. They could contradict each other silently.
FIRMWARE_IMPOSE replaces both, and both suites pass unchanged. "--bios"
overrode a physical fact: forced on an image with no boot sector it gave a
"running" VM with a mute console. It is ignored now, and the caller is
told.

The timezone was read as strict UTF-8 under an "except OSError": a byte
that is not one failed the whole deployment for a convenience
translation.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
4f4664f804 [FIX] cache qemu : soustraire l'invité qui n'a pas de magasin
Le détournement est TRANSPARENT et vaut pour tout le pont : ne pas donner
l'autorité à une VM ne la dispense pas d'être interceptée, elle échoue sur
« self-signed certificate in certificate chain ». NixOS n'a pas d'ancre de
confiance par fichier, et la poser par déclaration arriverait trop tard —
la première reconstruction EST le premier téléchargement. La VM est donc
exceptée par son adresse MAC, avant sa création, et cela se dit.

L'image Proxmox n'est mise en place qu'une fois complète : « wget -O »
écrivait dans la cible, et une coupure y figeait un fichier tronqué que
le test de présence acceptait à chaque déploiement suivant.

--- EN ---

Interception is TRANSPARENT and covers the whole bridge: withholding the
authority from a VM does not spare it, it fails on "self-signed
certificate in certificate chain". NixOS has no per-file trust anchor, and
declaring one would come too late — the first rebuild IS the first
download. The VM is therefore exempted by MAC, before creation, and it is
said.

The Proxmox image is put in place only once complete: "wget -O" wrote into
the target, and an interruption froze a truncated file there that the
presence test accepted on every later deployment.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
f3edd806bc [ADD] proxmox : NixOS s'y déploie, ce qui a pris trois correctifs
Aucune lecture de code ne les aurait trouvés. Son image n'a pas de secteur
d'amorçage BIOS : une VM créée en SeaBIOS se déclare « running » avec une
console MUETTE, d'où un marqueur par distribution — Debian 13 démarre en
SeaBIOS sur le même hôte, et renverser le défaut coûterait un disque EFI
à chaque VM du parc.

« --ciuser » s'en remet au compte par DÉFAUT de l'image, que NixOS nomme
autrement : le cloud-config du dépôt part en extrait pour toutes les
distributions. Et le lecteur cloud-init passe sur le bus SCSI, une image
bâtie pour virtio seul ne voyant jamais un lecteur IDE.

--- EN ---

No code reading would have found them. Its image has no BIOS boot sector:
a VM created in SeaBIOS reports "running" with a SILENT console, hence a
per-distribution marker — Debian 13 boots in SeaBIOS on the same host, and
flipping the default would cost an EFI disk on every VM.

"--ciuser" defers to the image's DEFAULT account, which NixOS names
otherwise: the repository's cloud-config now travels as a snippet for
every distribution. And the cloud-init drive moves to the SCSI bus, an
image built for virtio alone never seeing an IDE drive.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
5c87238dd5 [ADD] long_test : ERPLibre installé sur NixOS, de bout en bout
Une machine, une question binaire : le chemin que le MENU emprunte
aboutit-il sur un système déclaratif. La commande d'installation est celle
du menu, prise telle quelle — un test qui installerait par ses propres
soins prouverait SON chemin, pas celui du produit, et c'est là que les
pannes se cachaient.

Le verdict est l'ÉTAT de la machine, non un code de retour :
« nixos-rebuild » rend 4 quand une unité n'a pas redémarré alors que le
système est activé. Il vit dans long_test/ parce qu'il crée une VM et
prend des heures ; le lanceur unitaire doit rester lançable en secondes.

--- EN ---

One machine, one binary question: does the path the MENU takes succeed on
a declarative system. The install command is the menu's, taken as is — a
test installing by its own means would prove ITS path, not the product's,
and that is where the failures hid.

The verdict is the machine's STATE, not an exit code: "nixos-rebuild"
returns 4 when a unit failed to restart while the system is active. It
lives in long_test/ because it creates a VM and takes hours; the unit
runner must stay runnable in seconds.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
a9f91dda87 [FIX] install nixos : l'amorçage, les en-têtes, les manuels écartés
L'amorçage d'ERPLibre n'atteignait pas NixOS : le Makefile force
« SHELL := /bin/bash », que l'image ne porte pas, et le fournisseur de
Python ne cherche l'interpréteur du système qu'en /usr/bin/pythonX.Y.
Les deux répondent une fois envfs déclaré.

Ce qui n'a pas de roue amont se compile et réclame ses en-têtes : ils sont
liés au profil du système, leurs bibliothèques vont au chargeur, et
pg_config — dérivation à part dans nixpkgs — est nommé. La sortie « doc »
de CPython est écartée : sa construction est un Sphinx de 3 000 pages qui
domine le temps d'installation d'une VM de 4 Go.

--- EN ---

ERPLibre's bootstrap did not reach NixOS: the Makefile forces "SHELL :=
/bin/bash", which the image lacks, and the Python provider only looks for
the system interpreter at /usr/bin/pythonX.Y. Both answer once envfs is
declared.

What has no upstream wheel compiles and demands its headers: they are
linked into the system profile, their libraries reach the loader, and
pg_config — a separate derivation in nixpkgs — is named. CPython's "doc"
output is left out: building it is a 3000-page Sphinx run that dominates
install time on a 4 GB VM.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
fa5892d006 [FIX] qemu cloud-init : locale, clavier et profil, par distribution
Deux réglages régionaux échouaient sur toute VM Debian, et le seul signe
en était le mot « error » dans un compte-rendu qui le porte à chaque fois.
« update-locale » refuse un locale qui n'est pas généré, et le module du
clavier finit par redémarrer console-setup, absent de l'image
genericcloud : le réglage est écrit avant cet échec, donc le poser
nous-mêmes ne perd que la console texte.

Et l'installateur nix n'écrit plus dans ~/.bashrc : un fichier que
l'utilisateur possède ne se modifie pas pour la durée d'un amorçage.

--- EN ---

Two regional settings failed on every Debian VM, and the only sign was the
word "error" in a report that carries it every time. "update-locale"
refuses a locale that is not generated, and the keyboard module ends by
restarting console-setup, absent from the genericcloud image: the setting
is written before that failure, so placing it ourselves loses only the
text console.

And the nix installer no longer writes into ~/.bashrc: a file the user
owns is not edited for the duration of a bootstrap.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
fce1d5b00b [ADD] qemu : NixOS déployable, de son image à ses dépendances
Neuvième système du catalogue, et le seul dont aucune distribution ne
publie d'image cloud : celle d'un tiers est épinglée et sa somme vérifiée
à chaque téléchargement. Son seed diffère aussi — networkd prend la clé
d'un bloc pour un NOM d'interface là où netplan honore un « match: », et
sshd refuse un compte dont le shell n'existe pas, d'où /bin/sh.

Ses dépendances se DÉCLARENT dans conf/nixos/erplibre.nix : envfs répond
à /usr/bin/env, nix-ld donne aux roues manylinux leur chargeur. Vérifié
sur une VM : le verrou de 362 paquets s'installe et Odoo répond.

--- EN ---

Ninth system of the catalogue, and the only one no distribution publishes
a cloud image for: a third party's is pinned and its sum verified at every
download. Its seed differs too — networkd reads a block's key as an
interface NAME where netplan honours a "match:", and sshd refuses an
account whose shell does not exist, hence /bin/sh.

Its dependencies are DECLARED in conf/nixos/erplibre.nix: envfs answers
/usr/bin/env, nix-ld gives manylinux wheels their loader. Checked on a VM:
the 362-package lock installs and Odoo answers.

Assisted-by: Claude Opus 5
2026-09-16 21:31:03 -04:00
aa6774939f [FIX] déploiement qemu : attendre l'étape finale de cloud-init
« cloud-init status --wait » rend la main dès que cloud-init se déclare en
erreur — un module accessoire y suffit — alors que son étape finale écrit
encore l'autorité du cache, les variables et le fichier sudoers. Une session
ouverte dans cette seconde-là vit sans ces variables, PAM ne relisant plus le
fichier, et sudo n'a alors rien à conserver : l'installation lancée par sudo
rejette le certificat du cache. L'attente guette désormais l'unité
cloud-final, et son seul état « activating » : ce oneshot reste ACTIF une fois
fini, si bien qu'« is-active » y ferait attendre la borne entière pour rien.

--- EN ---

« cloud-init status --wait » returns as soon as cloud-init declares itself in
error — an accessory module is enough — while its final stage still writes the
cache authority, the variables and the sudoers file. A session opened in that
second lives without those variables, PAM never rereading the file, and sudo
then has nothing to keep: an install run by sudo rejects the cache
certificate. The wait now watches the cloud-final unit, and only its
« activating » state: this oneshot stays ACTIVE once finished, so « is-active »
would wait the whole bound for nothing.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
156956a1f0 [FIX] test long cache : ne pas compter le refus d'un miroir en faute
The verdict counted every upstream line of an already seen package as a file
the cache failed to serve. A mirror that publishes the distribution under
another path answers 404 to each package; the client moves to the next
mirror, which the cache serves from disk. The 404 goes upstream on both VMs
without anything missing, and an openSUSE campaign failed on six such lines.
An upstream answer of 400 or more is now reported apart and not counted.
Checked: a test for the refusal, and one where a delivered 200 still fails.

--- FR ---

Le verdict comptait toute ligne d'amont d'un paquet déjà vu comme un fichier
que le cache n'a pas servi. Un miroir qui publie la distribution sous un
autre chemin répond 404 à chaque paquet ; le client passe au miroir suivant,
que le cache sert du disque. Le 404 repart à l'amont aux deux VM sans que
rien ne manque, et une campagne openSUSE échouait sur six de ces lignes. Une
réponse d'amont de 400 ou plus est désormais signalée à part, sans compter.
Vérifié : un test du refus, et un où un 200 livré échoue toujours.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
433fe753c3 [FIX] hors ligne : HEAD servi, attente NTP levée, étape nommée
Trois défauts qui empêchaient une VM hors ligne d'aboutir. zypper vérifie
chaque dépôt par un HEAD, jamais gardé sous une clé portable : le dépôt était
déclaré invalide alors que le magasin tenait son index, et un HEAD reçoit
désormais les en-têtes du corps du GET gardé. Une image qui attend la
synchronisation NTP avant son étape finale ne démarrait jamais son serveur
ssh, faute de serveur de temps joignable ; l'attente est levée sur une VM
déployée hors ligne. Enfin, une troisième VM en échec nomme l'étape qui l'a
arrêtée, là où le résumé d'une série n'affichait qu'un verdict nu.

--- EN ---

Three faults that kept an offline VM from finishing. zypper checks each
repository with a HEAD, never stored under a portable key: the repository was
declared invalid while the store held its index, and a HEAD now gets the
headers of the stored GET body. An image waiting for NTP synchronisation
before its final stage never started its ssh server, no time server being
reachable; that wait is lifted on a VM deployed offline. Last, a failing third
VM names the step that stopped it, where a series summary showed a bare
verdict.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
15477d52ac [FIX] déploiement et mesure : openSUSE, sudo, console, mémoire
Quatre défauts qu'une campagne complète sur les sept systèmes du catalogue a
mis au jour. La famille zypper visait un faisceau de certificats qu'openSUSE
n'écrit pas, et pip s'arrêtait là, en ligne comme hors ligne. sudo remet
l'environnement à zéro : sans env_keep, une installation lancée par sudo
rejette l'autorité du cache là où PAM ne relit pas /etc/environment. Le
journal de console n'était gardé que pour la voie installateur, si bien
qu'une image cloud bloquée avant ssh ne laissait rien à lire. Et une VM de
8 Gio restait allumée après sa mesure, épuisant la mémoire de l'hôte.

--- EN ---

Four faults a full campaign over the catalogue's seven systems brought out.
The zypper family pointed at a certificate bundle openSUSE does not write,
and pip stopped there, online as well as offline. sudo resets the
environment: without env_keep, an install run by sudo rejects the cache
authority where PAM does not reread /etc/environment. The console log was
kept for the installer path only, so a cloud image stuck before ssh left
nothing to read. And an 8 GiB VM stayed up after its measurement, exhausting
the host's memory.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
f20e21b7d2 [ADD] cache qemu : nettoyage par âge et plafond de taille
Le cache ne diminuait jamais de lui-même et vit sur le disque de
l'orchestrateur : un parc qui déploie chaque jour finissait par le remplir,
sans que rien ne le signale. Deux règles existent désormais, éteintes par
défaut et réglées depuis le menu : l'âge du dernier usage, et un plafond de
taille qui efface le moins récemment servi jusqu'à tenir dessous, objets et
dépôts git confondus. Un minuteur systemd quotidien les applique, et une
réinstallation garde les valeurs choisies. Le menu montre d'abord ce qui
partirait.

--- EN ---

The cache never shrank by itself and lives on the orchestrator's disk: a site
deploying every day filled it up with nothing to say so. Two rules now exist,
off by default and set from the menu: the age of last use, and a size ceiling
that erases the least recently served until the cache fits under it, objects
and git mirrors alike. A daily systemd timer applies them, and a reinstall
keeps the chosen values. The menu first shows what would go.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
cb57a79da0 [ADD] test long cache : enchaîner une campagne par système
Each campaign covered one system, so proving the cache beyond Ubuntu meant
relaunching by hand and comparing reports one by one. --distro now accepts
« tous » or a comma list: each system runs its full campaign with the
catalogue version, its VMs are destroyed before the next one — three VMs per
system would not fit together on the host disk — and a table compares verdict,
durations and upstream bytes. A failure does not stop the series; an unknown
name is refused before any VM is created. Checked: tests and mutations.

--- FR ---

Chaque campagne couvrait un système : prouver le cache au-delà d'Ubuntu
voulait dire relancer à la main et comparer les rapports un à un. --distro
accepte désormais « tous » ou une liste : chaque système fait sa campagne
complète avec la version du catalogue, ses VM sont défaites avant le suivant —
trois VM par système ne tiendraient pas ensemble sur le disque de l'hôte — et
un tableau compare verdict, durées et octets d'amont. Un échec n'arrête pas la
série ; un nom inconnu est refusé avant qu'aucune VM ne soit créée. Vérifié :
tests et mutations.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
4b60dcba30 [ADD] déploiement qemu : couper l'audit npm, traduire l'exception
L'audit de npm interroge un service qu'aucun cache ne rejoue : amont coupé,
il échoue à chaque installation sans rien vérifier. Une VM déployée hors
ligne reçoit donc NPM_CONFIG_AUDIT=false, écrit dans /etc/environment et
relu après cloud-init ; une VM en ligne garde son audit. Le retrait d'une
exception du détournement parle désormais la langue du menu, son message
allant à l'erreur standard tandis que la sortie standard garde les règles
que nft lit, qui ne se traduisent jamais.

--- EN ---

The npm audit queries a service no cache can replay: upstream cut, it fails
every install without checking anything. A VM deployed offline therefore gets
NPM_CONFIG_AUDIT=false, written in /etc/environment and read back after
cloud-init; an online VM keeps its audit. Removing a redirection exception
now speaks the menu's language, its message going to standard error while
standard output keeps the rules nft reads, which are never translated.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
7df58956ea [FIX] tests assistant : juger la frontière dans un interpréteur neuf
Six tests asserted that an assistant module does not import script.todo.todo
by looking at sys.modules of the running process. Under the full suite another
test had already imported todo, so all six failed whatever the module did. Each
now imports its module in a fresh interpreter and reads the answer there.
Checked: the six pass after test_todo_menu, the order that broke them, and a
module made to import todo fails its test.

--- FR ---

Six tests affirmaient qu'un module de l'assistant n'importe pas
script.todo.todo en regardant le sys.modules du processus en cours. Dans la
suite complète, un autre test avait déjà importé todo : les six échouaient
quoi que fasse le module. Chacun importe désormais son module dans un
interpréteur neuf et y lit la réponse. Vérifié : les six passent après
test_todo_menu, l'ordre qui les faisait tomber, et un module qu'on fait
importer todo fait échouer son test.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
cd2b8266d4 [FIX] requirement erplibre : déclarer openpyxl, sauter sans lui
Three Transform data tests wrote an Excel workbook with openpyxl, which the
.venv.erplibre requirements never declared, so the unit suite reported three
errors on every install. openpyxl is now declared there. The tests skip when it
cannot be imported in their own interpreter, since a venv built before this
line still lacks it; the dedicated engine venv is not the right probe, as the
tests write their workbook in-process. Checked: 707 Transform tests pass, and
with openpyxl made unimportable the three are skipped, not errors.

--- FR ---

Trois tests de Transform data écrivaient un classeur Excel avec openpyxl, que
les dépendances de .venv.erplibre ne déclaraient pas : la suite unitaire
rendait trois erreurs sur toute installation. openpyxl y est désormais
déclaré. Les tests se sautent quand il ne s'importe pas dans leur interpréteur,
un venv bâti avant cette ligne ne l'ayant toujours pas ; le venv dédié du
moteur n'est pas la bonne sonde, les tests écrivant leur classeur sur place.
Vérifié : 707 tests de Transform passent, et openpyxl rendu inimportable, les
trois sont sautés et non en erreur.

Assisted-by: Claude Opus 5
2026-09-16 05:36:13 -04:00
fb2f71477e [ADD] cache qemu : messages du binaire en anglais ou en français
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.

--- EN ---

The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.

Assisted-by: Claude Opus 5
2026-09-16 02:59:01 -04:00
977722b7cd [ADD] proxmox deploy : accélération 3D, bibliothèques sondées et posées
A Proxmox VM always used the serial console as its display, so nothing inside
it could render. The form offers 3D: the VM gets « --vga virtio-gl » and the
account joins the render and video groups in the guest, which « qm set »
cannot write; without them GL falls back to software while VIRGL reports
success. The box is offered only where the host has a render node, VIRGL, GL
and EGL, Proxmox refusing to start the VM otherwise once its disk is written.
What is missing is named with its package, and a button installs it, then
reads the host again. Covered by test/test_proxmox_form.py.

--- FR ---

Une VM Proxmox prenait toujours la console série pour affichage : rien ne
pouvait y rendre. Le formulaire offre la 3D : la VM reçoit « --vga virtio-gl »
et le compte entre dans les groupes render et video dans l'invité, ce que
« qm set » ne sait pas écrire ; sans eux, GL retombe en logiciel alors que
VIRGL annonce un succès. La case n'est offerte que si l'hôte a un nœud de
rendu, VIRGL, GL et EGL, Proxmox refusant sinon de démarrer la VM une fois son
disque écrit. Ce qui manque est nommé avec son paquet, et un bouton le pose
puis relit l'hôte. Couvert par test/test_proxmox_form.py.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
01c5290e30 [FIX] qemu install : poser mise, pyenv et starship, verdict par outil
Without pipefail, « curl … | sh » returns the status of sh, 0 on empty input:
a failed download passed for an install, its fallback never ran, and the
failure surfaced later as « command not found ». mise and pyenv are downloaded
into a mktemp file, then run, and a check refuses any fallback behind a pipe
without pipefail. starship's installer runs as root under a root timeout, never
reaching the « sudo -v » that sudo-rs refuses, and its shell hook is guarded.
Each tool then reports the version found or « not installed », without
tripping « set -e ».

--- FR ---

Sans pipefail, « curl … | sh » rend le statut de sh, 0 sur une entrée vide :
un téléchargement raté passait pour une pose, son repli ne tournait jamais, et
l'échec se lisait plus loin sous « command not found ». mise et pyenv sont
téléchargés dans un fichier tiré par mktemp, puis exécutés, et un contrôle
refuse tout repli derrière un tube sans pipefail. L'installateur de starship
tourne en root sous un délai root, sans atteindre le « sudo -v » que sudo-rs
refuse, et son crochet de shell est gardé. Chaque outil dit ensuite la version
trouvée ou « non installé », sans faire tomber « set -e ».

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
6ecf4c86ce [FIX] test proxmox : sauter quand l'hôte n'a pas les outils Debian
The Proxmox install script calls Debian's own tools — dpkg, apt-get,
hostname, debconf-set-selections. A host without them cannot PLAY it: the
shell returns 127 at the first call, and the truncated output that follows
made nine tests fail on an Arch host. A report carrying nine failures nobody
can act on makes its reader stop looking for what matters. They skip now,
naming the missing tool, judged on what happened rather than on an inventory
— a host may have dpkg without debconf-set-selections.

--- FR ---

Le script d'installation Proxmox appelle les outils de Debian — dpkg,
apt-get, hostname, debconf-set-selections. Une machine qui ne les a pas ne
peut pas le JOUER : le shell rend 127 au premier appel, et la sortie
tronquée faisait échouer neuf tests sur un hôte Arch. Un rapport qui porte
neuf échecs sur lesquels personne ne peut agir fait cesser d'y chercher ce
qui compte. Ils sautent désormais en nommant l'outil absent, jugés sur ce
qui s'est passé et non sur un inventaire — une machine peut porter dpkg
sans debconf-set-selections.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
7e3d0d1f88 [IMP] menu proxmox : une icône à chaque entrée qui n'en portait pas
Ten of the eighteen entries carried no pictogram, and the eye picks one out
of such a list before it reads. Each takes the icon its siblings already use
elsewhere in the tool — 📋 lists, 🧹 cleans, 📊 measures, 🔍 previews, 📥
downloads, 🗂 catalogues, 🖥 tunnels a desktop — so the same picture keeps
meaning the same thing from one menu to the next. A table in the tests holds
the pairing, in both languages, and checks the menu still shows the entries
it freezes.

--- FR ---

Dix des dix-huit entrées ne portaient aucun pictogramme, et l'œil s'y repère
avant de lire. Chacune reçoit l'icône que ses semblables portent déjà
ailleurs dans l'outil — 📋 liste, 🧹 nettoie, 📊 mesure, 🔍 prévisualise, 📥
télécharge, 🗂 catalogue, 🖥 ouvre un bureau —, si bien qu'une même image
garde le même sens d'un menu à l'autre. Un tableau dans les tests fige le
couple, dans les deux langues, et vérifie que le menu affiche encore les
entrées qu'il fige.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
d4ec22ca4a [FIX] test longtest : ne plus dépendre de la machine ni de l'horloge
Two tests of test/test_todo_longtest.py measured their surroundings rather
than the code. The descent plan shrinks with free resources by design, so
demanding four levels failed on a host whose disk had filled; the test checks
the invariant at the depth the host allows — each level narrower than its
parent, two vCPUs at least — and skips below two levels, saying so. The
dry-run test counted reports, whose names carry the second they were written
in; it now requires that no report of a real descent was written.

--- FR ---

Deux tests de test/test_todo_longtest.py mesuraient leur environnement plutôt
que le code. Le plan de descente rétrécit avec les ressources libres, c'est
voulu : exiger quatre étages échouait sur un hôte au disque rempli. Le test
vérifie l'invariant à la profondeur que l'hôte permet — chaque étage plus
étroit que son parent, deux vCPU au moins — et se saute sous deux étages en le
disant. Le test à blanc comptait des rapports dont le nom porte la seconde de
leur écriture ; il exige désormais qu'aucun rapport de vraie descente ne soit
écrit.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
1d891e6e33 [IMP] tests longs : confirmer avant de créer ou de détruire des machines
The long-test menu printed the command and ran it at once: a mistyped digit
created real VMs, and « --detruire » removes machines with their disks. The
command stays printed, since it is what makes the question answerable, and the
prompt follows it. Warning and question are chosen from the arguments, so a
destruction is never confirmed with the words of a launch. A dry run or a
report creates nothing and asks nothing. Covered by
test/test_longtest_confirm.py.

--- FR ---

Le menu des tests longs affichait la commande et l'exécutait aussitôt : un
chiffre tapé de travers créait de vraies VM, et « --detruire » efface des
machines avec leurs disques. La commande reste affichée, c'est elle qui rend
la question répondable, et l'invite la suit. L'avertissement et la question se
choisissent sur les arguments : une destruction n'est jamais confirmée avec
les mots d'un lancement. Un plan à blanc ou un rapport ne crée rien et ne
demande rien. Couvert par test/test_longtest_confirm.py.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
814251f19f [FIX] déploiement qemu : amorcer Fedora en BIOS, sa chaîne UEFI se fige
Aucune VM Fedora ne démarrait : pas de console, pas de bail DHCP, une machine
« en cours d'exécution » qui ne fait rien. Le micrologiciel charge et DÉMARRE
le chargeur — il l'annonce — puis se fige sans écrire un octet sur le disque.
La même image en BIOS démarre son noyau : ce n'est donc ni l'image, saine, ni
sa partition EFI, dont le chemin de repli est bien là. Ni l'entropie ni la
machine q35 n'y changent rien. Une table nomme les distributions concernées et
« --bios » l'emporte toujours, pour l'hôte qui n'a pas OVMF.

--- EN ---

No Fedora VM would start: no console, no DHCP lease, a machine "running" that
does nothing. The firmware loads and STARTS the loader — it says so — then
freezes without writing a byte to disk. The same image under BIOS boots its
kernel: so it is neither the image, which is sound, nor its EFI partition,
whose fallback path is present. Neither entropy nor the q35 machine changes
anything. A table names the distributions concerned, and "--bios" always
wins, for the host that has no OVMF.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
2892606690 [FIX] déploiement qemu : nom d'hôte valide, fuseau connu de l'invité
Deux réglages que cloud-init applique au premier démarrage, et qui échouaient
tous les deux SANS arrêter le déploiement. Un nom d'hôte n'accepte ni souligné
ni point, là où un nom de domaine libvirt les tolère : la VM gardait le nom
générique de son image. Un alias de fuseau hérité — la forme que plusieurs
distributions récentes ont reléguée à un paquet séparé — faisait marquer
l'exécution de cloud-init en erreur et laissait la machine en UTC, ce qui ne
se voit qu'après coup sur des horodatages à +0000. Le nom est nettoyé, le
fuseau rendu canonique par la table d'alias de tzdata.

--- EN ---

Two settings cloud-init applies at first boot, both of which failed WITHOUT
stopping the deployment. A hostname accepts neither underscore nor dot, where
a libvirt domain name tolerates them: the VM kept its image's generic name. A
legacy timezone alias — the form several recent distributions moved to a
separate package — marked the cloud-init run as failed and left the machine in
UTC, which only shows up later on +0000 timestamps. The name is cleaned, the
timezone made canonical through tzdata's alias table.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00
a7706adf3d [ADD] cache qemu : miroir des téléchargements des VM, hors ligne compris
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.

--- FR ---

Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.

Assisted-by: Claude Opus 5
2026-09-14 16:46:15 -04:00