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
Son LTS est terminé, et sa suite de sécurité n'est ni servie ni archivée :
security.debian.org publie encore un index qui nomme des paquets dont le pool
ne porte plus le fichier, et archive.debian.org ne connaît pas bullseye. apt
s'arrête donc avant d'installer git, sur une version qui ne recevra plus rien.
Elle quitte le catalogue de déploiement et le script de dépendances, où la
variable qui la distinguait n'a plus d'usage. Debian 12 et 13 restent, et
l'image de la 13 est toujours le dernier point de version — sans rien à
changer ici quand le suivant paraît.
Vérifié : plus aucune trace de bullseye dans script/, hors la base des
conteneurs, traitée à part.
--- EN ---
Its LTS has ended, and its security suite is neither served nor archived:
security.debian.org still publishes an index naming packages whose pool no
longer holds the file, and archive.debian.org does not know bullseye. apt
therefore stops before installing git, on a version that will receive nothing
more. It leaves the deployment catalogue and the dependency script, where the
variable telling it apart has no use left. Debian 12 and 13 stay, and 13's
image is always the latest point release — with nothing to change here when
the next one appears.
Checked: no trace of bullseye left in script/, beyond the container base,
handled separately.
Assisted-by: Claude Opus 5
Les variables du cache — PIP_CERT et ses voisines — recevaient le chemin
canonique de la famille de paquets, sans que rien ne vérifie qu'il soit là. Une
image peut ne pas le porter : sur une Fedora récente,
« /etc/pki/tls/certs/ca-bundle.crt » manque alors que le faisceau extrait
existe. pip refuse alors TOUT téléchargement, y compris ce qui n'a rien à voir
avec le cache, et l'installation s'arrête sur des paquets publics. Le faisceau
de la famille est essayé d'abord, les autres connus ensuite ; aucun trouvé
n'écrit aucune variable, et pip garde son propre jeu de certificats.
Vérifié : la commande produite parcourt les quatre chemins avant de renoncer.
--- EN ---
The cache variables — PIP_CERT and its neighbours — received the canonical path
of the package family, with nothing checking it was there. An image may not
carry it: on a recent Fedora, "/etc/pki/tls/certs/ca-bundle.crt" is missing
while the extracted bundle exists. pip then refuses EVERY download, including
what has nothing to do with the cache, and the install stops on public
packages. The family bundle is tried first, the other known ones next; none
found writes no variable, and pip keeps its own certificate set.
Checked: the produced command walks the four paths before giving up.
Assisted-by: Claude Opus 5
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
Les six cibles d'addons visaient « ./addons/<dépôt>/ », une disposition que
seul un manifeste produit : la production les range sous
« odoo<version>/addons/ ». isort s'arrêtait donc sur une trace pour un chemin
inexistant, et « make format_all » échouait sans avoir formaté un module. Les
dépôts sont maintenant NOMMÉS, cherchés sous les deux dispositions, et celui
qu'un manifeste ne rapatrie pas s'annonce puis est ignoré. Le formateur est
celui de la communauté, qui a quitté black : ruff en 88 colonnes, avec les
sections d'imports « odoo » et « odoo.addons » qu'isort ne sait pas produire.
Vérifié : trois des six dépôts sont présents ici, les autres s'annoncent
absents sans faire échouer la cible.
--- EN ---
The six addons targets aimed at "./addons/<repo>/", a layout only one manifest
produces: production files them under "odoo<version>/addons/". isort therefore
stopped on a traceback for a non-existent path, and "make format_all" failed
without formatting a module. Repositories are now NAMED, looked up under both
layouts, and one a manifest does not check out announces itself then is
skipped. The formatter is the community's, which left black: ruff at 88
columns, with the "odoo" and "odoo.addons" import sections isort cannot
produce.
Checked: three of the six repositories are present here, the others announce
themselves absent without failing the target.
Assisted-by: Claude Opus 5
Un échec de « repo forall » arrêtait tout : la liste des fichiers locaux, déjà
obtenue, était jetée, et « make format » rendait 0 sans avoir formaté une
ligne. Sa sortie d'erreur, capturée puis jetée, emportait la seule explication,
et seule l'exception Python restait à l'écran. Google Repo n'ajoute pourtant
que les dépôts rapatriés : il est joué sans check, ce qu'il a écrit est relayé,
et les fichiers locaux sont formatés quand même. « Rien à formater » se dit au
lieu d'une sortie muette, et un échec du dépôt lui-même rend non nul.
Vérifié : avec un repo bouchonné qui échoue, son message s'affiche et le
fichier local est formaté.
--- EN ---
A failing "repo forall" stopped everything: the local file list, already
gathered, was dropped, and "make format" returned 0 without formatting a line.
Its error output, captured then thrown away, carried the only explanation, and
only the Python exception stayed on screen. Google Repo only adds the checked
out repositories: it runs without check, whatever it wrote is relayed, and
local files are formatted anyway. "Nothing to format" is said instead of a
silent exit, and a failure of the repository itself returns non-zero.
Checked: with a stub repo that fails, its message shows and the local file is
formatted.
Assisted-by: Claude Opus 5
« -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
install.sh et make lançaient todo.py par son hashbang, donc par le python3 du
PATH, sans rien garantir de sa version : une syntaxe plus récente l'arrête en
SyntaxError au chargement, et le poste hors d'âge perd tout moyen de lancer
l'installation qui l'en sortirait. L'ordre est désormais le venv d'outillage,
sinon le python3 du PATH s'il suffit, sinon l'installation. TODO lui-même se
relance dans le venv, vérifie version et modules avant ses imports, et propose
l'installation en terminal — ce que « make » sur un clone neuf promet depuis
toujours sans le tenir.
Vérifié : venv à jour, venv périmé, systèmes en 3.10, 3.14 et 3.15 vont chacun
où il faut ; un import du module ne relance rien et lève comme avant.
--- EN ---
install.sh and make ran todo.py through its hashbang, so through the python3 of
the PATH, with no guarantee about its version: a newer syntax stops it with a
SyntaxError at load, and the ageing machine loses every way to start the
install that would rescue it. The order is now the tools venv, else the python3
of the PATH when it suffices, else the install. TODO itself relaunches in the
venv, checks version and modules before its imports, and offers the install in
a terminal — what "make" on a fresh clone has always promised without
keeping.
Checked: fresh venv, stale venv, systems on 3.10, 3.14 and 3.15 each go where
they should; importing the module relaunches nothing and raises as before.
Assisted-by: Claude Opus 5
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
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
Cinq fichiers employaient des f-strings PEP 701 — guillemets imbriqués du même
type, backslash dans l'expression, expression sur plusieurs lignes — que
Python refuse avant 3.12. Or les hooks de script/git/hooks portent
« #!/usr/bin/env python3 » et tournent donc sur l'interpréteur du système,
qu'une distribution livre encore en 3.10 : la SyntaxError tombe au chargement,
sans qu'aucun garde puisse nommer la commande à taper. Les chaînes traduites
gardent leur texte à l'octet près, sans quoi leur clé se perdrait. Les
commentaires de ces fichiers passent du récit au présent, comme la règle le
demande de ce qu'on touche.
Vérifié : tout l'arbre Python parse sous 3.10, 3.11, 3.12 et 3.14.
--- EN ---
Five files used PEP 701 f-strings — quotes of the same kind nested, a backslash
in the expression, an expression spanning lines — which Python refuses before
3.12. The hooks in script/git/hooks carry "#!/usr/bin/env python3" and so run
on the system interpreter, which a distribution still ships as 3.10: the
SyntaxError lands at load, before any guard can name the command to type.
Translated strings keep their text byte for byte, else their key would be lost.
The comments of those files move from tale to present tense, as the rule asks
of what one touches.
Checked: the whole Python tree parses under 3.10, 3.11, 3.12 and 3.14.
Assisted-by: Claude Opus 5
Aucune cible ne formatait les tests : « make format_all » couvre script/ et les
six dépôts d'addons, et rien d'autre. Seul « make format » les atteignait, et
seulement ceux qu'un diff signalait, si bien que 75 fichiers sur 210 ne
suivaient aucune norme — de la dérive silencieuse dans un arbre qui sert de
référence au reste. Les tests tournent dans le venv d'outillage : ils suivent
donc ruff, comme script/, et la cible entre dans format_all.
Vérifié : « make -n format_test » nomme bien ruff, et format_all l'inclut.
--- EN ---
No target formatted the tests: "make format_all" covers script/ and the six
addons repositories, nothing else. Only "make format" reached them, and only
those a diff reported, so that 75 files out of 210 followed no standard —
silent drift in a tree the rest takes as reference. The tests run in the
tooling venv: they therefore follow ruff, like script/, and the target joins
format_all.
Checked: "make -n format_test" names ruff, and format_all includes it.
Assisted-by: Claude Opus 5
« make format » donnait isort et black à tout fichier Python : la cible py37,
plus petit dénominateur commun de six dépôts d'addons, imposait son style au
code de ce dépôt. Le contexte décide désormais — les modules Odoo gardent isort
et black, le reste passe par ruff, réglé dans .ruff.toml, comme la norme OCA
depuis qu'elle a quitté black. Les dépôts rapatriés sous script/ en sont
écartés, chemin nommé compris. La cible y reste py310 : dès 3.14 ruff écrit
« except A, B: » sans parenthèses, que le python3 des hooks ne sait pas lire.
Vérifié : un fichier reçoit deux normes selon son chemin, et aucun dépôt
rapatrié n'est touché.
--- EN ---
"make format" gave isort and black to every Python file: the py37 target,
lowest common denominator of six addons repositories, imposed its style on this
repository's code. Context now decides — Odoo modules keep isort and black,
everything else goes through ruff, set in .ruff.toml, as the OCA standard does
since it left black. The repositories checked out under script/ are excluded, a
named path included. The target stays py310 there: from 3.14 on, ruff writes
"except A, B:" unparenthesised, which the python3 of the hooks cannot read.
Checked: one file gets two standards by its path, and no checked-out
repository is touched.
Assisted-by: Claude Opus 5
[FIX] qemu : clé portable du cache, re-clé du magasin, image reprise
10 commits. Le magasin rangeait un objet sous le chemin ENTIER de son URL : un
miroir qui préfixe ce chemin donnait deux clés pour les mêmes octets, et le
cache retéléchargeait ce qu'il détenait — sur un journal de 7099 noms livrés,
1124 vivaient sous plusieurs chemins, 3,18 Gio repris à l'amont pour rien.
Seuls les six derniers segments comptent désormais, quatre pour la famille
pacman dont les chemins sont plus courts. « --recle » rattrape un magasin
déjà rempli sans rien retélécharger, et une image gardée devenue périmée est
reprise une fois au lieu de perdre la campagne entière.
Vérifié : suites Go et Python vertes, et une campagne hors ligne où la seconde
VM n'a repris aucun octet de paquet à l'amont.
--- EN ---
10 commits. The store filed an object under the WHOLE path of its URL: a mirror
prefixing that path gave two keys for the same bytes, and the cache
re-downloaded what it held — over a log of 7099 delivered names, 1124 lived
under several paths, 3.18 GiB taken upstream for nothing. Only the last six
segments count now, four for the pacman family whose paths are shorter.
« --recle » brings over a store already filled, downloading nothing, and a
kept image gone stale is fetched once more instead of losing a whole campaign.
Checked: Go and Python suites green, and an offline campaign where the second
VM took no package byte from upstream.
Assisted-by: Claude Opus 5
La section du non-publié taisait trois correctifs déjà fusionnés : l'attente
de l'étape finale de cloud-init avant de rendre la main, sans quoi une session
vit sans les variables du cache et une installation lancée par sudo rejette
son certificat ; les métadonnées de dépôt RPM nommées par l'empreinte de leur
contenu, désormais servies du disque, une plage faisant prendre le fichier
entier ; et l'attente NTP levée sur une VM hors ligne, qui sans cela n'ouvre
jamais son serveur ssh. Régénéré par mmg : 54 puces de chaque côté.
--- EN ---
The Unreleased section was silent on three fixes already merged: the wait for
cloud-init's final stage before handing over, without which a session lives
without the cache variables and an install run by sudo rejects its
certificate; RPM repository metadata named after the hash of its content, now
served from disk, a range fetching the whole file; and the NTP wait lifted on
an offline VM, which otherwise never opens its ssh server. Regenerated with
mmg: 54 bullets on each side.
Assisted-by: Claude Opus 5
Un miroir d'Arch sert « archlinux/core/os/x86_64/<paquet> » là où un autre
sert « core/os/x86_64/<paquet> » : quatre et cinq segments, plus courts que la
borne commune de six, qui les garde entiers et en fait deux clés pour un même
octet. La famille pacman passe donc à quatre, ce qui retire le segment du
miroir et garde le nom du dépôt. La borne haute reste partout ailleurs : deux
distributions peuvent publier un « .deb » de même nom pour d'autres octets, et
les confondre servirait le paquet de l'une à l'autre. Vérifié : quatre
mutations attrapées, aucune collision sur les chemins relevés.
--- EN ---
An Arch mirror serves « archlinux/core/os/x86_64/<package> » where another
serves « core/os/x86_64/<package> »: four and five segments, shorter than the
common bound of six, which keeps them whole and makes two keys for one byte.
The pacman family therefore moves to four, which drops the mirror's segment
and keeps the repository name. The high bound stays everywhere else: two
distributions may publish a « .deb » of the same name for other bytes, and
merging them would serve one's package as the other's. Checked: four mutations
caught, no collision over the paths surveyed.
Assisted-by: Claude Opus 5
Additionner déplacés, fondus et inchangés dépasse le nombre d'objets lus, et
la passe paraît alors se contredire. Elle ne se trompe pas : une copie déjà
rangée sous la clé courante est comptée inchangée quand le parcours la visite,
puis fondue si une copie plus récente vient prendre sa place. Le commentaire
le dit désormais, pour que personne ne prenne cette somme pour un inventaire.
--- EN ---
Adding moved, merged and unchanged exceeds the number of objects read, and the
pass then seems to contradict itself. It does not: a copy already stored under
the current key is counted unchanged when the walk visits it, then merged if a
more recent copy takes its place. The comment now says so, lest anyone take
that sum for an inventory.
Assisted-by: Claude Opus 5
La règle de clé d'un fichier portable a changé : seule la fin du chemin compte
désormais. Les objets écrits sous l'ancienne règle restent sur le disque mais
deviennent introuvables — le service les redemande à l'amont, et la place
qu'ils tiennent ne sert plus personne. Sur un magasin de 12 764 objets, 5 419
étaient dans ce cas, soit 9,11 Gio. La passe les range sous la clé courante
sans rien retélécharger, et fond au passage les copies qu'un miroir portait
sous plusieurs chemins — 1 422 groupes, environ 3,37 Gio rendus. Un statut
seul n'est pas touché : sa clé porte l'hôte. Vérifié : cinq tests, trois
mutations attrapées.
--- EN ---
The key rule for a portable file changed: only the end of the path now counts.
Objects written under the old rule stay on disk but become unreachable — the
service asks upstream again, and the space they hold serves no one. On a store
of 12 764 objects, 5 419 were in that case, that is 9.11 GiB. The pass stores
them again under the current key without downloading anything, and merges on
the way the copies a mirror carried under several paths — 1 422 groups, about
3.37 GiB returned. A status-only entry is left alone: its key carries the
host. Checked: five tests, three mutations caught.
Assisted-by: Claude Opus 5
Un miroir préfixe le chemin à sa guise — « /rocky/10.2/… »,
« /mirror/rocky-linux/10.2/… », « /pub/archive/fedora/… » — et le chemin
entier donnait deux clés pour un même octet : le cache retéléchargeait ce
qu'il détenait. Relevé sur un journal de 7099 noms livrés : 1124 vivaient sous
plusieurs chemins, et 3,18 Gio repartaient à l'amont pour rien. Seuls les six
derniers segments comptent désormais, les segments vides tombant avec eux. Six
est le plus petit nombre sans collision : un chemin Debian en porte six, si
bien que cinq confondraient son paquet avec celui d'Ubuntu, qui porte le même
nom pour d'autres octets — 38 cas mesurés. Vérifié : deux mutations attrapées.
--- EN ---
A mirror prefixes the path as it pleases, and the whole path gave two keys for
one byte: the cache re-downloaded what it held. Measured over a log of 7099
delivered names: 1124 lived under several paths, and 3.18 GiB went back
upstream for nothing. Only the last six segments now count, empty ones falling
with them. Six is the smallest number without collision: a Debian path carries
six, so five would confuse its package with Ubuntu's, which bears the same
name for other bytes — 38 measured cases. Checked: two mutations caught.
Assisted-by: Claude Opus 5
Le relevé des fichiers que la seconde VM découvre seule attribuait leur
existence à la publication continue d'Arch, quelle que soit la distribution
mesurée. Sur une famille RHEL, ces fichiers viennent surtout d'un miroir qui
range les mêmes paquets sous un autre chemin : la clé du magasin portant le
chemin, le cache les retélécharge alors qu'il les détient. Le message nomme
désormais les deux causes sans en imputer une à un système qui n'est pas
celui de la campagne.
--- EN ---
The report of files the second VM discovers alone blamed their existence on
Arch's rolling release, whatever the distribution measured. On a RHEL family
system they mostly come from a mirror storing the same packages under another
path: the store key carrying the path, the cache re-downloads what it already
holds. The message now names both causes without pinning one on a system that
is not the campaign's.
Assisted-by: Claude Opus 5
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
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
Le binaire a gagné le retrait d'une entrée du magasin par URL et le classement
des objets nix comme immuables sans que la version bouge : « --version » ne
distinguait plus un cache qui porte ces règles d'un cache qui ne les porte
pas, et une réinstallation ne se constatait donc pas. Un hôte pouvait servir
un binaire ignorant l'option « --oublie » tout en annonçant le même numéro.
--- EN ---
The binary gained the removal of a single store entry by URL and the
classification of nix objects as immutable without the version moving:
« --version » could no longer tell a cache holding those rules from one that
does not, so a reinstall could not be observed. A host could serve a binary
ignoring the « --oublie » option while announcing the same number.
Assisted-by: Claude Opus 5
[ADD] garde-fous : refuser une clé i18n double, borner le guide à 80
3 commits. Deux défauts qui ne se signalent pas d'eux-mêmes, et que rien
n'empêchait de revenir.
Une clé de traduction écrite deux fois fait afficher à un menu la traduction
d'un autre : Python bâtit un dictionnaire littéral de haut en bas et garde la
dernière, sans un mot. Le contrôle lit le fichier par son ARBRE, la collision
pouvant s'écrire sur une seule ligne où une expression régulière ne la voit
pas. Vert sur l'état actuel : aucune clé n'est en double aujourd'hui.
Le guide de connexion tenait dans 80 colonnes nu et en rendait 103 une fois
la VM équipée, là où l'épreuve de largeur ne regardait que le guide nu. C'est
la mise en page qui porte la règle désormais. 6706 tests.
--- EN ---
3 commits. Two defects that do not report themselves, and that nothing
prevented from coming back.
A translation key written twice makes one menu show another's translation:
Python builds a literal dictionary top to bottom and keeps the last one,
without a word. The check reads the file by its TREE, the collision being
writable on a single line where a regular expression does not see it. Green
on the current state: no key is duplicated today.
The connection guide fitted in 80 columns bare and rendered 103 once the VM
was equipped, where the width check looked only at the bare guide. The layout
now carries the rule. 6706 tests.
Assisted-by: Claude Opus 5
Ce que le guide fait désormais se voit à chaque ouverture de session sur une
VM équipée : un lecteur qui choisit de mettre à jour doit le lire.
Le garde-fou i18n n'a pas d'entrée : aucune clé n'est en double aujourd'hui,
donc rien ne change pour qui utilise le produit. Il empêche un retour, il ne
corrige pas un symptôme.
--- EN ---
What the guide now does shows at every login on an equipped VM: a reader
choosing whether to upgrade has to read it.
The i18n guard gets no entry: no key is duplicated today, so nothing changes
for whoever uses the product. It prevents a return, it does not fix a
symptom.
Assisted-by: Claude Opus 5
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
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
[ADD] qemu, proxmox et cache : NixOS devient déployable
23 commits. NixOS est le neuvième système déployable, et le seul dont aucune
distribution ne publie d'image cloud : celle d'un tiers est épinglée, sa somme
vérifiée, l'UEFI imposé là où elle n'a pas de secteur BIOS. Les dépendances
d'ERPLibre y sont DÉCLARÉES au lieu d'être posées commande par commande.
Presque rien ne s'y devine — /etc est en lecture seule, /bin un montage FUSE
que systemd ne voit pas, le pare-feu est actif et sshd ne montre aucun motd.
Le cache de téléchargement a dû apprendre à lui servir l'autorité, perdue à
trois frontières, sans quoi NixOS en restait soustrait et le hors ligne lui
était fermé. Vérifié sur machines réelles : installation complète, 0 refus de
certificat, Odoo en 303. 6697 tests.
--- EN ---
23 commits. NixOS is the ninth deployable system, and the only one no
distribution publishes a cloud image for: a third party's is pinned, its sum
verified, UEFI forced where it has no BIOS sector. ERPLibre's dependencies are
DECLARED there instead of installed one command at a time.
Almost nothing there is guessable — /etc is read-only, /bin a FUSE mount
systemd does not see, the firewall is on and sshd shows no motd. The download
cache had to learn to serve it the authority, lost at three boundaries,
without which NixOS stayed exempt from it and offline deployment was closed
to it. Checked on real machines: a complete install, 0 certificate refusals,
Odoo in 303. 6697 tests.
Assisted-by: Claude Opus 5
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
Le point annonçait qu'un fragment de service suffit à donner l'autorité du
cache à NixOS. C'était vrai de ce qu'on savait en l'écrivant, et le lecteur
qui s'y fie repart avec un mécanisme qui échoue.
Mesuré depuis : « sudo nix store info » rend « Store URL: local » et un
NIX_REMOTE vide — le nix de root ne passe pas par le démon, il télécharge
lui-même, avec l'environnement que sudo lui laisse, et sudo n'en laisse
aucun. Le point nomme désormais les trois frontières où l'environnement se
perd, et ce qui traverse chacune.
--- EN ---
The entry announced that a service drop-in is enough to give NixOS the
cache's authority. That was true of what was known when it was written, and
a reader who relies on it leaves with a mechanism that fails.
Measured since: « sudo nix store info » reports « Store URL: local » and an
empty NIX_REMOTE — root's nix does not go through the daemon, it downloads
on its own, with whatever environment sudo leaves it, and sudo leaves none.
The entry now names the three boundaries where the environment is lost, and
what crosses each.
Assisted-by: Claude Opus 5
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
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
Le classeur ne connaissait pas nix. Un « <empreinte>.narinfo » et les
« nar/<empreinte>.nar.zst » tombaient dans le volatil par défaut, donc
revalidés un par un contre l'amont — relevé sur un déploiement réel :
346 narinfo, 249 nar.zst, 54 nar.xz, tous volatils, pour des fichiers dont
le NOM est l'empreinte du chemin de store et qui ne changeront jamais.
Une dérivation modifiée porte une autre empreinte, donc un autre nom. La
clé écarte l'hôte du même coup : le nom identifie le fichier sur tout
miroir. « nix-cache-info » reste volatile, il décrit le magasin amont.
--- EN ---
The classifier did not know nix. A "<hash>.narinfo" and the
"nar/<hash>.nar.zst" fell into the volatile default, so were revalidated
one by one against upstream — read off a real deployment: 346 narinfo,
249 nar.zst, 54 nar.xz, all volatile, for files whose NAME is the store
path hash and which will never change.
A modified derivation carries another hash, hence another name. The key
drops the host at the same time: the name identifies the file on any
mirror. "nix-cache-info" stays volatile, it describes the upstream store.
Assisted-by: Claude Opus 5
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
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
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
Deux changements de comportement manquaient au non-publié, et ce sont
justement ceux qu'un lecteur doit connaître avant de mettre à jour. La
vérification d'une image ne se demande plus, donc une commande qui
passait « --verify » ne change rien et une qui ne le passait pas vérifie
désormais : « --no-verify » est l'échappatoire, à réserver au hors ligne.
Et « --bios » est refusé sur une image sans secteur d'amorçage, là où il
était obéi et donnait une VM « running » à console muette.
--- EN ---
Two behaviour changes were missing from Unreleased, and they are exactly
the ones a reader needs before upgrading. Verifying an image is no longer
asked for, so a command passing "--verify" changes nothing while one that
did not now verifies: "--no-verify" is the way out, to be kept for offline
runs.
And "--bios" is refused on an image with no boot sector, where it used to
be obeyed and gave a VM reported "running" with a silent console.
Assisted-by: Claude Opus 5
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
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
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
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
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
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
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
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
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
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
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
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
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