They run as subprocesses and had no i18n, so a French migration alternated
languages from one line to the next. The database upgrade itself had the
same gap.
The driver read English sentences out of their output to decide whether to
ask its question. Translating them would have made it mute -- no error, no
trace. The link is now the exit code, which no language touches: 0
nothing, 1 copies concerned, 2 the tool failed.
87 strings, five tools. A test rejects any displayed sentence that skips
t(), and proves itself on an untranslated one.
--- FR ---
Ils tournent en sous-processus et n'avaient aucun i18n : une migration en
français alternait les deux langues d'une ligne à l'autre. La mise à
niveau de la base elle-même souffrait du même manque.
Le pilote lisait des phrases anglaises dans leur sortie pour décider de
poser sa question. Les traduire l'aurait rendu muet — sans erreur, sans
trace. Le lien est désormais le code de sortie, qu'aucune langue ne
touche : 0 rien, 1 copies concernées, 2 l'outil a échoué.
87 chaînes, cinq outils. Un test refuse toute phrase affichée qui saute
t(), et fait sa preuve sur une chaîne non traduite.
Assisted-by: Claude Opus 5
Step 2 announced the copies the next bump will break, invited to
arbitrate, then moved on: the question came at the bump, tens of minutes
later. Yet looking writes nothing, and neutralizing here covers every bump
-- each bump database is a clone of this one.
The announcement is now a prompt: view what a copy holds, why it breaks,
full screen, or neutralize right away. Both prompts build their command in
one place, so they cannot drift apart.
Full screen means a real terminal: the viewer was started without one and
drew nothing, the pager exiting at once on a pipe.
--- FR ---
L'étape 2 annonçait les copies que le palier suivant casserait, invitait à
arbitrer, puis passait : la question venait au palier, des dizaines de
minutes plus tard. Or regarder n'écrit rien, et neutraliser ici vaut pour
tous les paliers — chaque base de palier est un clone de celle-ci.
L'annonce est désormais une invite : voir ce que porte une copie, pourquoi
elle casse, en plein écran, ou neutraliser tout de suite. Les deux invites
composent leur commande au même endroit, elles ne peuvent donc pas
diverger.
Le plein écran suppose un vrai terminal : la vue était lancée sans, et ne
dessinait rien, le pagineur sortant aussitôt sur un tube.
Assisted-by: Claude Opus 5
The screen could already replay from any step -- Enter on a row does it --
but that lived in a hint line under four named buttons. A capability you
have to guess is not offered, and it was reported as missing. It now has a
key, a button and a message, like the four others.
The b key added earlier went to the line-by-line prompts during the run,
not to this screen. Same letter on both, so going back is one key wherever
you are.
Two defects behind it. Replaying the chosen step actually SKIPPED it,
resuming at the next one. And a replayed step did not rewind the variables
it owns, so the second run started from the first one's leftovers.
Checked in a simulated terminal: b focuses the table, two arrows and Enter
return step 2, and the button focuses without choosing.
--- FR ---
L'écran savait déjà rejouer depuis n'importe quelle étape — Entrée sur une
rangée le fait — mais cela vivait dans une ligne d'indication sous quatre
boutons nommés. Une capacité qu'il faut deviner n'est pas offerte, et elle
a été signalée manquante. Elle a maintenant une touche, un bouton et un
message, comme les quatre autres.
La touche b ajoutée plus tôt s'adressait aux invites ligne à ligne pendant
l'exécution, pas à cet écran. Même lettre sur les deux : revenir en
arrière est une seule touche, où qu'on soit.
Deux défauts derrière cela. Rejouer l'étape choisie la SAUTAIT en fait,
reprenant à la suivante. Et une étape rejouée ne remettait pas à zéro les
variables qui lui appartiennent, si bien que la seconde exécution partait
des restes de la première.
Vérifié en terminal simulé : b donne le focus au tableau, deux flèches et
Entrée ramènent l'étape 2, et le bouton met le focus sans choisir.
Assisted-by: Claude Opus 5
Realising at a prompt that an earlier step deserved another answer had one
way out: Ctrl+C. That leaves the progression as it stands and makes you find
the resume screen again to rewind. « b » does it properly — it shows the
steps, rewinds the state, writes it, and says what to relaunch.
Cancelling that must not stop the migration, which is the trap: it returns to
the same prompt, exactly where you were. Removing that guard makes two tests
fail.
The COW warning printed « -d DB -t odooXX.0 » for commands meant to be pasted.
It knows both values, so it prints them, and offers --shape as well since that
is half the answer.
Checked on the four outcomes: a normal answer passes through, « b » then a
step rewinds and stops, cancelling and an unknown step both continue. 15 tests.
--- FR ---
S'apercevoir à une invite qu'une étape antérieure méritait un autre choix
n'avait qu'une issue : Ctrl+C. Cela laisse la progression telle quelle et
oblige à retrouver l'écran de reprise pour rembobiner. « b » le fait
proprement — il montre les étapes, rembobine l'état, l'écrit, et dit quoi
relancer.
Y renoncer ne doit pas arrêter la migration, et c'est le piège : on revient à
la même invite, exactement là où l'on était. Retirer ce garde-fou fait tomber
deux tests.
L'avertissement COW affichait « -d DB -t odooXX.0 » pour des commandes faites
pour être collées. Il connaît les deux valeurs, donc il les écrit, et propose
aussi --shape puisque c'est la moitié de la réponse.
Vérifié sur les quatre issues : une réponse normale passe, « b » puis une
étape rembobine et arrête, annuler et un choix inconnu continuent. 15 tests.
Assisted-by: Claude Opus 5
An early check warns, hours before the bump, that a copy will break it. It
then says « arbitrate BEFORE launching the migration » and hands over a raw
UPDATE — and no question follows. Read on a resumed run it looks like the
question already went by and was skipped, which is exactly how it was read.
It now says the migration asks at the bump itself, and shows what each copy
holds before the answer. The manual UPDATE stays, but after the two commands
that do it reversibly: telling someone to hand-write SQL when --restore exists
is offering the sharper tool first.
--- FR ---
Un contrôle précoce prévient, des heures avant le palier, qu'une copie va le
casser. Il dit ensuite « arbitrez AVANT de lancer la migration » et livre un
UPDATE brut — et aucune question ne suit. Lu au cours d'une reprise, on croit
que la question est passée et a été sautée, ce qui est exactement la lecture
qui en a été faite.
Il dit maintenant que la migration pose la question au palier lui-même, en
montrant ce que chaque copie contient avant qu'on réponde. L'UPDATE manuel
reste, mais après les deux commandes qui le font de façon réversible : dire à
quelqu'un d'écrire du SQL à la main quand --restore existe, c'est tendre
l'outil le plus coupant en premier.
Assisted-by: Claude Opus 5
The declarations view showed a 12.0 template becoming a 13.0 extension and
stopped there. Read alone it says « Odoo changed its code », and the obvious
reaction is to ask why that is anyone's problem — which is exactly what it
prompted.
It is not the problem. On a database without a copy the module upgrade
rewrites the view and nothing breaks. It breaks because a COPY exists and
froze the old shape. That sentence was missing, and so was the other half of
the answer: what the copy actually holds. A copy identical to the view it
shadows costs nothing to neutralize; one carrying five lines of theme hooks
costs those five lines. Both are now stated where the question arises.
Checked on the database mid-migration: the copy that raised the question
reports +5/-3, and the two other cases — identical to its twin, and a page
with no twin at all — each say so.
--- FR ---
La vue des déclarations montrait un gabarit 12.0 devenant une extension 13.0
et s'arrêtait là. Lue seule, elle dit « Odoo a changé son code », et la
réaction naturelle est de demander en quoi cela nous regarde — ce qu'elle a
justement provoqué.
Ce n'est pas le problème. Sur une base sans copie, la mise à jour du module
réécrit la vue et rien ne casse. Ça casse parce qu'une COPIE existe et a figé
l'ancienne forme. Cette phrase manquait, et l'autre moitié de la réponse
aussi : ce que la copie contient réellement. Une copie identique à la vue
qu'elle masque ne coûte rien à neutraliser ; une qui porte cinq lignes
d'ancres de thème coûte ces cinq lignes. Les deux sont dites là où la question
se pose.
Vérifié sur la base en cours de migration : la copie qui a soulevé la question
rapporte +5/-3, et les deux autres cas — identique à sa jumelle, et page sans
jumelle — le disent chacun.
Assisted-by: Claude Opus 5
A database coming from an old version is updated before the neutralization —
the tool offers it at the very start. That work IS step 2, done earlier, but
it was only recorded as state_1_update_all while the screen reads state_2_*.
So the step stayed « not started » right after running.
The costly half was invisible. Step 2 skipped the work through a local
variable, which is False again on a resume: with only the old flag written,
resuming a migration re-ran update_addons_all on an already-updated database.
Hours, for nothing. The check now reads the written trace, not the session.
The screen says when it happened rather than just « done », since the moment
is exactly what raised the doubt. Logs written before this fix read correctly
without being touched — the early flag alone is recognised.
Checked on the real log from the VM: step 2 goes from « not started » to
« done early, before the neutralization », the four other steps unchanged.
10 tests; removing the fix makes the resume one fail.
--- FR ---
Une base venant d'une vieille version se met à jour avant la neutralisation —
l'outil le propose au tout début. Ce travail EST l'étape 2, faite plus tôt,
mais il n'était enregistré que sous state_1_update_all quand l'écran lit
state_2_*. L'étape restait donc « non démarrée » juste après avoir tourné.
La moitié coûteuse était invisible. L'étape 2 sautait le travail grâce à une
variable locale, remise à False à la reprise : avec seulement l'ancien drapeau
écrit, reprendre une migration relançait update_addons_all sur une base déjà à
jour. Des heures, pour rien. Le contrôle lit maintenant la trace écrite, pas
la session.
L'écran dit quand cela a eu lieu plutôt que « terminée », le moment étant
justement la source du doute. Les journaux écrits avant ce correctif se lisent
correctement sans être modifiés — le drapeau précoce seul est reconnu.
Vérifié sur le vrai journal de la VM : l'étape 2 passe de « non démarrée » à
« faite plus tôt, avant la neutralisation », les quatre autres inchangées.
10 tests ; retirer le correctif fait tomber celui de la reprise.
Assisted-by: Claude Opus 5
The progression log lives at one path and the next migration writes over it.
Restarting therefore erased everything known about the run before — which
steps passed, which modules were missing, how long each took — and that is
exactly what someone looks for after having had to restart.
Both answers that discard the progression now copy it aside first, named after
the origin database and the moment of the copy, so two attempts on the same
database do not cover each other and a file moved out of its folder still says
what it is. Partial replays keep the log, so they archive nothing.
It lands under private/, not beside the original: reinstalling wipes
.venv.erplibre/ and would take the history with it.
Two copies in the same second overwrote each other in silence — undoing the
very loss this prevents. The second one is numbered now, and the test that
had skipped that case asserts it.
Checked on the real 51 KB log from the VM: 18 states copied unchanged, the
name carries the database and the timestamp, and a log with no state at all is
not archived. 12 tests.
--- FR ---
Le journal de progression vit à un seul endroit et la migration suivante écrit
par-dessus. Recommencer effaçait donc tout ce qu'on savait de la tentative
précédente — quels paliers étaient passés, quels modules manquaient, combien
de temps chacun avait pris — soit exactement ce qu'on cherche après avoir dû
recommencer.
Les deux réponses qui jettent la progression la copient désormais d'abord,
sous un nom portant la base d'origine et l'instant de la copie, pour que deux
tentatives sur la même base ne se recouvrent pas et qu'un fichier sorti de son
dossier se décrive encore. Une reprise partielle garde le journal, donc
n'archive rien.
La copie va sous private/, pas à côté de l'originale : une réinstallation
efface .venv.erplibre/ et emporterait l'historique avec elle.
Deux copies dans la même seconde s'écrasaient en silence — annulant la perte
même qu'on évite. La seconde est numérotée, et le test qui sautait ce cas
l'affirme.
Vérifié sur le vrai journal de 51 Ko de la VM : 18 états copiés à l'identique,
le nom porte la base et l'horodatage, et un journal sans aucun état n'est pas
archivé. 12 tests.
Assisted-by: Claude Opus 5
The prompt asked whether to neutralize copies without showing what they hold.
Answering meant giving up a customization sight unseen — often three lines, an
id and a container width, sometimes a whole page, and nothing told them apart.
« v » now shows both halves of the question. What the copy changed, diffed
against the module view it shadows: on the database at hand, 5 lines added and
3 removed, two CSS anchors and a container width. And why it breaks, by
showing the declaration in each version — portal.frontend_layout is a
standalone template in 12.0 and inheritance specs in 13.0, which is the whole
explanation. « w » is the same two, full screen, space to switch.
The current version comes from ir_module_module, not .odoo-version: the
checkout is switched to the target before this runs, so reading the file
compared the target with itself and printed the same declaration twice. That
is what it did until it was run against a real migration.
--list says which copies a past neutralization put aside. A renamed key is
invisible in the interface, so without it the only trace was remembering.
Checked on the VM mid-migration: both views on view 2670, the screen builds
headless and toggles, --list reports and reports nothing when there is
nothing. A missing database now exits 2 with a message instead of a traceback.
--- FR ---
L'invite demandait de neutraliser des copies sans montrer ce qu'elles
contiennent. Répondre revenait à renoncer à une personnalisation sans l'avoir
vue — souvent trois lignes, un id et une largeur de conteneur, parfois une
page entière, et rien ne les distinguait.
« v » montre désormais les deux moitiés de la question. Ce que la copie a
changé, comparé à la vue de module qu'elle masque : sur la base en cours, 5
lignes ajoutées et 3 retirées, deux ancres CSS et une largeur. Et pourquoi ça
casse, en affichant la déclaration dans chaque version —
portal.frontend_layout est un gabarit autonome en 12.0 et des consignes
d'héritage en 13.0, ce qui est toute l'explication. « w » donne les deux en
plein écran, espace pour basculer.
La version courante vient d'ir_module_module, pas de .odoo-version : le
checkout est basculé sur la cible avant cette étape, donc lire le fichier
comparait la cible avec elle-même et affichait deux fois la même déclaration.
C'est ce qu'il faisait jusqu'à l'essai sur une vraie migration.
--list dit quelles copies une neutralisation passée a mises de côté. Une clé
renommée est invisible dans l'interface ; sans cela, la seule trace était de
s'en souvenir.
Vérifié sur la VM en cours de migration : les deux vues sur la vue 2670,
l'écran se construit sans terminal et bascule, --list rapporte et ne rapporte
rien quand il n'y a rien. Une base absente sort en 2 avec un message plutôt
qu'une trace d'appel.
Assisted-by: Claude Opus 5
**Ubuntu 20.04 et 22.04 ne sont plus supportées**, sur toutes les architectures.
```
[UPD] release: deploy and run a fleet of VMs
The QEMU deployment form stops treating the fleet as one machine: vCPU,
RAM, disk, type, ERPLibre branch, Odoo version and timezone are set per
VM, a lock shields one from the global profile, an entry can be renamed
or duplicated. The install dashboard follows through -- update by parts,
restart Odoo or delete a machine without leaving it. A graphical VM picks
its desktop and its application store, and the SSH command to reach it is
composed from ~/.ssh/config.
Installation reaches four new grounds. openSUSE Leap and Tumbleweed,
AlmaLinux, Rocky and Fedora 43 join the catalogue, and the s390x build
chain works end to end on all of them: no wheel is published for that
architecture, so everything compiles against distribution headers. mise
lays down a precompiled CPython where pyenv built one, uv places the
Python packages, and both fall back to the old path on failure.
Ubuntu 20.04 and 22.04 are dropped -- pikepdf needs qpdf 12.2, built in
C++20, while focal ships GCC 9. That is the only breaking change here.
--- FR ---
Le formulaire de déploiement QEMU cesse de traiter le parc comme une
seule machine : vCPU, RAM, disque, type, branche ERPLibre, version d'Odoo
et fuseau horaire se règlent par VM, un verrou en soustrait une au profil
global, une entrée se renomme ou se duplique. Le tableau de bord suit —
mettre à jour par parties, redémarrer Odoo ou supprimer une machine sans
le quitter. Une VM graphique choisit son bureau et son magasin
d'applications, et la commande SSH pour la joindre est composée depuis
~/.ssh/config.
L'installation gagne quatre terrains. openSUSE Leap et Tumbleweed,
AlmaLinux, Rocky et Fedora 43 rejoignent le catalogue, et la chaîne de
compilation s390x y fonctionne de bout en bout : aucune roue n'est
publiée pour cette architecture, donc tout se compile contre les en-têtes
de la distribution. mise pose un CPython précompilé là où pyenv en
compilait un, uv pose les paquets Python, et les deux retombent sur
l'ancien chemin en cas d'échec.
Ubuntu 20.04 et 22.04 partent — pikepdf exige qpdf 12.2, bâti en C++20,
quand focal livre GCC 9. C'est le seul changement cassant du lot.
Assisted-by: Claude Opus 5
```
**Per-VM deployment.** vCPU, RAM, disk, VM type, ERPLibre branch, Odoo version
and timezone are each set on the VM, all the way down to the remote command
built per machine. A lock shields a VM from the global x1..x4 profile and shows
on the whole row; an entry can be renamed or deployed in several copies.
Presets are finer where development VMs actually live: every gigabyte and every
vCPU up to 16.
**Graphical VMs.** A server / desktop choice offering GNOME or Cinnamon, the
application store picked per VM (.deb only, Flatpak tooling, or snap), and a
composed SSH command to reach the remote desktop — read from `~/.ssh/config`
so a nested VM is reachable through its ProxyJump, with the hypervisor console
as a third route.
**Install dashboard.** Acts on a VM without leaving it: update by parts —
system packages, then git repositories, then Python dependencies, never chained
so one failure cannot take the rest down — restart Odoo, or delete after a
confirmation naming the disk. Architecture column, both-way scrolling and
resizable columns.
**Platforms.** openSUSE Leap 16.0 and Tumbleweed, AlmaLinux, Rocky, Fedora 43
and the CentOS-family fixes. The s390x build chain works end to end on Debian,
EL9, EL10, Fedora and openSUSE. Ubuntu 20.04 and 22.04 are removed.
**Toolchain.** `EL_PYTHON_PROVIDER` chooses mise or pyenv, `EL_PIP_PROVIDER`
chooses uv or pip; each lives in a single library file, and each falls back on
failure. Neither tool is ever installed on its own — `make install_mise` and
`make install_uv` carry that decision. A Poetry dependency can be declined per
architecture.
**Déploiement par VM.** vCPU, RAM, disque, type de VM, branche ERPLibre,
version d'Odoo et fuseau horaire se règlent chacun sur la VM, jusqu'à la
commande distante bâtie par machine. Un verrou soustrait une VM au profil
global x1..x4 et se voit à la ligne entière ; une entrée se renomme ou se
déploie en plusieurs exemplaires. Les préréglages sont plus fins là où vivent
les VM de développement : chaque gigaoctet et chaque vCPU jusqu'à 16.
**VM graphiques.** Un choix serveur / bureau proposant GNOME ou Cinnamon, le
magasin d'applications retenu par VM (.deb seul, outillage Flatpak, ou snap),
et une commande SSH composée pour joindre le bureau distant — lue depuis
`~/.ssh/config`, si bien qu'une VM imbriquée est atteignable par son
ProxyJump, avec la console de l'hyperviseur comme troisième voie.
**Tableau de bord d'installation.** Agit sur une VM sans la quitter : mise à
jour par parties — paquets système, puis dépôts git, puis dépendances Python,
jamais enchaînées pour qu'un échec n'emporte pas le reste — redémarrage
d'Odoo, ou suppression après une confirmation nommant le disque. Colonne
d'architecture, défilement dans les deux sens et colonnes ajustables.
**Plateformes.** openSUSE Leap 16.0 et Tumbleweed, AlmaLinux, Rocky, Fedora 43
et les correctifs de la famille CentOS. La chaîne de compilation s390x
fonctionne de bout en bout sur Debian, EL9, EL10, Fedora et openSUSE. Ubuntu
20.04 et 22.04 sont retirées.
**Outillage.** `EL_PYTHON_PROVIDER` choisit mise ou pyenv, `EL_PIP_PROVIDER`
choisit uv ou pip ; chacun vit dans un seul fichier de bibliothèque, et chacun
retombe sur l'autre en cas d'échec. Aucun des deux n'est jamais installé de
lui-même — `make install_mise` et `make install_uv` portent cette décision.
Une dépendance Poetry peut être déclinée par architecture.
The Unreleased section already covered the QEMU deployment, the graphical
VMs and the COW migration tools, but stopped at what existed a month ago.
It says nothing of the per-VM settings, of the mise and uv providers, or
of the s390x build chain -- and a reader deciding whether to upgrade has
only this file to go on.
Fourteen entries, spread over Added, Changed and Fixed. Nothing is
repeated: the platforms and tools already listed stay where they are, and
only what the entry does not already say is added. The migration work
follows on its own, at the end of the branch.
Entries are written on one line. Nothing wraps markdown here -- no
max_line_length in .editorconfig, prettier never runs on .md -- and a
sentence cut at column 80 reads worse in a diff and in a terminal. The
three entries this cycle had already folded are flattened with them.
Written in CHANGELOG.base.md, the only source; the two generated files
come from it. mmg was unavailable here, so the generation was reproduced
by a filter checked byte-for-byte against the committed output before
being trusted.
--- FR ---
La section Unreleased couvrait déjà le déploiement QEMU, les VM graphiques
et les outils de migration COW, mais s'arrêtait à l'état d'il y a un mois.
Elle ne dit rien des réglages par VM, des fournisseurs mise et uv, ni de
la chaîne de compilation s390x — et qui décide de mettre à jour n'a que ce
fichier pour se prononcer.
Quatorze entrées, réparties sur Ajouté, Modifié et Corrigé. Rien n'est
redit : les plateformes et outils déjà listés restent en place, et seul ce
que l'entrée ne dit pas encore est ajouté. Le travail de migration suit
à part, en fin de branche.
Les entrées tiennent sur une ligne. Rien n'impose de plier le markdown
ici — pas de max_line_length dans .editorconfig, prettier ne tourne
jamais sur du .md — et une phrase coupée à la colonne 80 se lit moins
bien, en diff comme au terminal. Les trois entrées déjà pliées de ce
cycle sont mises à plat avec elles.
Écrit dans CHANGELOG.base.md, seule source ; les deux fichiers générés en
découlent. mmg étant indisponible ici, la génération a été reproduite par
un filtre vérifié à l'octet près contre la sortie déjà versionnée avant
d'être employé.
Assisted-by: Claude Opus 5
The list named Ubuntu, Debian 12, AlmaLinux, Rocky and Arch. It has been
wrong for a while: install_dev.sh also dispatches Fedora, openSUSE and
Linux Mint, each with its own dependency script, and the Debian path
handles trixie as well as bookworm. Someone on Fedora reading this file
concludes ERPLibre is not for them.
Every line is now read from the code rather than remembered. Ubuntu is
checked strictly against 24.04, 25.10 and 26.04, and refuses anything
else; Mint against 22.3; the RHEL family arrives through ID_LIKE, hence
RHEL and CentOS Stream alongside Alma and Rocky; SUSE through zypper,
Leap 16.0 and Tumbleweed. Debian carries no version test, and its qpdf is
built from source when the distribution ships one below 12.2 -- the same
guard the three families share.
The second list, further down, repeated four platforms and forgot the
rest. It now says what make install_os does: detect the distribution and
pick its package manager.
--- FR ---
La liste nommait Ubuntu, Debian 12, AlmaLinux, Rocky et Arch. Elle est
fausse depuis un moment : install_dev.sh aiguille aussi Fedora, openSUSE
et Linux Mint, chacune vers son script de dépendances, et le chemin
Debian traite trixie autant que bookworm. Qui lit ce fichier sous Fedora
en conclut qu'ERPLibre n'est pas pour lui.
Chaque ligne est désormais lue dans le code plutôt que de mémoire. Ubuntu
est vérifiée strictement contre 24.04, 25.10 et 26.04, et refuse le
reste ; Mint contre 22.3 ; la famille RHEL arrive par ID_LIKE, d'où RHEL
et CentOS Stream aux côtés d'Alma et Rocky ; SUSE par zypper, Leap 16.0
et Tumbleweed. Debian ne porte aucun test de version, et son qpdf est
compilé depuis les sources quand la distribution en livre un sous 12.2 —
le même garde que partagent les trois familles.
La seconde liste, plus bas, répétait quatre plateformes et oubliait les
autres. Elle dit maintenant ce que fait make install_os : détecter la
distribution et choisir son gestionnaire de paquets.
Assisted-by: Claude Opus 5
The gap between 4 and 8 GB was the first one worth filling -- it is the
usual size of an ERPLibre development VM -- but every neighbouring gap
had the same fault: the lists jumped precisely where development VMs
live.
RAM now offers every gigabyte up to 16, and vCPU every count up to 16.
Disk gains 30, 50, 100 and 160 G, the steps missing between those already
there. Above 16 nothing changes: those jumps are deliberate, a machine
that large not being sized by picking from a list.
A typed value was already accepted in both interfaces, so these are a
convenience, never a constraint.
--- FR ---
L'écart entre 4 et 8 Go était le premier à combler — c'est la taille
courante d'une VM de développement ERPLibre — mais tous les écarts
voisins avaient le même défaut : les listes sautaient précisément là où
vivent les VM de développement.
La RAM offre désormais chaque gigaoctet jusqu'à 16, et les vCPU chaque
valeur jusqu'à 16. Le disque gagne 30, 50, 100 et 160 G, les paliers qui
manquaient entre ceux déjà présents. Au-delà de 16, rien ne change : ces
sauts sont voulus, une machine de cette taille ne se dimensionne pas en
piochant dans une liste.
Une valeur tapée était déjà acceptée dans les deux interfaces : ce sont
donc des commodités, jamais une contrainte.
Assisted-by: Claude Opus 5
A per-VM setting appears not to be taken into account, and nothing on
screen tells which link failed: the gap may be between the widget and
the model, or between the model and the spec. A screenshot shows
neither.
F9 writes both side by side into ~/.erplibre/deploy-form-dump.txt:
profile, shared values, overrides, locks, row generation, then VM by VM
what the list DISPLAYS against what the model HOLDS, and finally the
spec that would go to deployment.
The file can be pasted into a message, unlike an image, and it answers
the question on its own: if the list shows 16384 while the model says
1024, the defect is in the intake; if they agree and the created VM
differs, it is downstream, in deploy_qemu.
--- FR ---
Un réglage par VM ne semble pas pris en compte, et rien dans ce que
l'écran montre ne permet de trancher : l'écart peut être entre le widget
et le modèle, ou entre le modèle et la spec. Une capture d'écran ne dit
ni l'un ni l'autre.
F9 écrit les deux côte à côte dans ~/.erplibre/deploy-form-dump.txt :
profil, valeurs communes, surcharges, verrous, génération des rangées,
puis VM par VM ce que la liste AFFICHE contre ce que le modèle CONTIENT,
et enfin la spec qui partirait au déploiement.
Le fichier se recopie dans un message, contrairement à une image, et il
répond seul à la question : si la liste montre 16384 et que le modèle
dit 1024, le défaut est dans la prise en compte ; s'ils s'accordent et
que la VM créée diffère, il est en aval, dans deploy_qemu.
Assisted-by: Claude Opus 5
textual, urwid and lxml were already in the tooling requirements; the
earlier table read as if they were missing, when it only compared
against the system python. psycopg2 is the one absent, and stays so:
nothing under script/ imports it, the analysis tools query PostgreSQL
through psql, and the Odoo venvs declare it where Odoo needs it.
A test now answers this by reading the imports instead of the venv. It
bites both ways: a removed declaration, and a new undeclared import.
Also chmod +X -> +x in the Dockerfile: uppercase only adds the bit to
directories, or to files that already carry one. On what COPY had just
laid down in 644 it did nothing.
--- FR ---
[ADD] test : ce que l'outillage importe doit être déclaré
textual, urwid et lxml y étaient déjà ; le tableau précédent laissait
croire le contraire, alors qu'il comparait au python du système.
psycopg2 est le seul absent, et le reste : rien sous script/ ne
l'importe, les outils d'analyse passent par psql, et les venvs Odoo le
déclarent là où Odoo en a besoin.
Un test répond désormais à la question en lisant les imports plutôt
que le venv. Il mord des deux côtés : déclaration retirée, et import
non déclaré.
Aussi chmod +X -> +x dans le Dockerfile : la majuscule n'ajoute le bit
qu'aux répertoires, ou aux fichiers qui en portent déjà un. Sur ce que
COPY venait de déposer en 644, elle ne faisait rien.
Assisted-by: Claude Opus 5
A "Remote desktop tunnel" entry under SSH configuration. It lists the
targets, resolves them, and composes the full command -- nothing to fill
in.
todo.py runs on the libvirt HOST; the tunnel starts from the workstation.
It cannot open it, but it alone knows the VM's private IP, its port and
the address through which it was reached. That last one comes from
SSH_CONNECTION, whose third field is exactly the server address used --
far safer than a "hostname" that may resolve to nothing outside.
When ~/.ssh/config already carries the entry, the tunnel takes it:
"ssh -N -L 5902:localhost:5901 <vm>". ProxyJump makes the route and
"localhost" means the VM's OWN loopback, so the tunnel survives an IP
change. That config is also the right source of targets, not the local
libvirt: a graphical VM is often nested, and virsh would only ever list
the orchestrator.
Two details: the hypervisor console needs VNC bound to the loopback, as
"listen=none" opens no socket at all; and two menu entries had no icon.
--- FR ---
Une entrée « Tunnel bureau distant » sous Configuration SSH. Elle liste
les cibles, les résout, et compose la commande complète — rien à remplir.
todo.py tourne sur l'HÔTE libvirt ; le tunnel, lui, part du poste de
travail. Il ne peut donc pas l'ouvrir, mais il est le seul à connaître
l'IP privée de la VM, son port et l'adresse par laquelle on l'a joint.
Cette dernière vient de SSH_CONNECTION, dont le troisième champ est
exactement l'adresse serveur utilisée — bien plus sûr qu'un « hostname »
qui peut ne rien résoudre depuis l'extérieur.
Quand ~/.ssh/config porte déjà l'entrée, le tunnel l'emprunte :
« ssh -N -L 5902:localhost:5901 <vm> ». Le ProxyJump fait la route et
« localhost » désigne le bouclage DE LA VM, si bien que le tunnel survit à
un changement d'IP. Ce fichier est aussi la bonne source de cibles, pas le
libvirt local : une VM graphique est souvent imbriquée, et virsh ne
listerait jamais que l'orchestrateur.
Deux détails : la console de l'hyperviseur exige un VNC sur la boucle
locale, « listen=none » n'ouvrant aucun socket ; et deux entrées de menu
n'avaient pas d'icône.
Assisted-by: Claude Opus 5
The VM announced "Remote desktop: RDP 3389" without saying how to get
there. Its address is on libvirt's private network, which is not routed
from outside its host: the port alone helps nobody.
It now prints the tunnel to set up, address already filled in --
"ssh -L 3390:<ip>:3389 <user>@<libvirt-host>", then an RDP client on
localhost:3390. Likewise in VNC for Arch, on 5902.
This is the only workable route when the libvirt host is itself a VM
with no graphical interface: the SPICE console is on "listen=none" and
assumes virt-viewer ON that host.
--- FR ---
La VM annonçait « Bureau distant : RDP 3389 » sans dire par où passer.
Son adresse est sur le réseau privé de libvirt, qui n'est pas routé
depuis l'extérieur de son hôte : le port seul n'aide personne.
Elle imprime maintenant le tunnel à monter, adresse déjà remplie —
« ssh -L 3390:<ip>:3389 <user>@<hote-libvirt> », puis un client RDP sur
localhost:3390. Idem en VNC pour Arch, sur 5902.
C'est le seul chemin praticable quand l'hôte libvirt est lui-même une VM
sans interface graphique : la console SPICE est en « listen=none » et
suppose virt-viewer SUR cet hôte.
Assisted-by: Claude Opus 5
The timezone was typed by hand. A misspelt IANA name is not rejected by
cloud-init: it is IGNORED. The VM stays on UTC, and you only notice from
the timestamps, once deployed.
A list of twenty-five zones, Québec first, then the rest of Canada and
the places one actually meets. The host's zone goes to the top, without
duplication: a machine outside this list must still see its own at a
glance.
NAMES, not offsets: "UTC-5" says nothing about daylight saving and
cloud-init will not take it. A name carries its own switching rules.
"free value…" keeps the door open to the other six hundred zones in the
database. The choice is copied into the field, which stays the only
value the spec reads -- one place holds the answer.
--- FR ---
Le fuseau se tapait à la main. Un nom IANA mal orthographié n'est pas
refusé par cloud-init : il est IGNORÉ. La VM reste en UTC, et on ne s'en
aperçoit qu'aux horodatages, une fois déployée.
Une liste de vingt-cinq fuseaux, le Québec d'abord, puis le reste du
Canada et les places qu'on rencontre en pratique. Le fuseau de l'hôte
passe en tête, sans doublon : une machine hors de cette liste doit voir
le sien en un coup d'œil.
Des NOMS, pas des décalages : « UTC-5 » ne dit rien de l'heure d'été et
cloud-init n'en veut pas. Un nom porte ses propres règles de bascule.
« libre… » garde la porte ouverte aux six cents autres fuseaux de la
base. Le choix est recopié dans le champ, qui reste la seule valeur lue
par la spec — un seul endroit porte la réponse.
Assisted-by: Claude Opus 5
A profile list per row, beside the branch: "ERPLibre + Odoo 18",
"Odoo 17", "ERPLibre alone". The form's profile stays the default; picking
another for a VM creates an override, going back clears it.
It reaches down to execution, like the branch and the type before it.
"final_cmd" now takes either a string for the whole fleet or a
{name: command} map, and the remote command is built per machine as soon
as profiles differ. A VM can install Odoo 18 while another validates
Odoo 17, in the same deployment.
Both lists are widened -- branch to 24 columns, profile to 28. Truncated,
"develop" and "ERPLibre + Odoo 18" no longer showed what had been picked,
and that is precisely what one wants to re-read before deploying.
Two more defects: the global branch and Odoo choices reached nothing at
all, and the summary announced the global profile for every VM, then named
a version nothing would install.
--- FR ---
Une liste de profils par rangée, à côté de la branche : « ERPLibre +
Odoo 18 », « Odoo 17 », « ERPLibre seul ». Le profil du formulaire reste
le défaut ; en choisir un autre pour une VM crée une surcharge, y revenir
l'efface.
Il descend jusqu'à l'exécution, comme la branche et le type avant lui.
« final_cmd » accepte maintenant une chaîne pour tout le parc ou une carte
{nom: commande}, et la commande distante est bâtie par machine dès que les
profils diffèrent. Une VM peut donc installer Odoo 18 pendant qu'une autre
valide Odoo 17, dans le même déploiement.
Les deux listes sont élargies : la branche passe à 24 colonnes, le profil
à 28. Tronqués, « develop » et « ERPLibre + Odoo 18 » ne laissaient plus
voir ce qu'on avait choisi — et c'est précisément ce qu'on veut relire
avant de déployer.
Deux autres défauts : les choix globaux de branche et d'Odoo n'atteignaient
rien, et le sommaire annonçait le profil global pour toutes les VM, puis
nommait une version que rien n'installait.
Assisted-by: Claude Opus 5
A branch list per row. The form's branch stays the default; picking
another for a VM creates an override, going back clears it -- the VM then
follows the form again, which is what one expects when restoring the
common choice.
It reaches all the way down to execution, like VM type before it.
"branch" now takes either a string for the whole fleet or a {name: branch}
map, and the remote command is built per machine as soon as branches
differ. The ground was ready: launch_installs already knows how to take
one command per VM.
A VM can therefore carry develop while another validates 1.6.0, in the
same deployment and the same monitoring table.
One trap: a row's branch fell back to develop on remount, the list being
rebuilt from the form rather than from the override it already held.
--- FR ---
Une liste de branches par rangée. La branche du formulaire reste le
défaut ; en choisir une autre pour une VM crée une surcharge, y revenir
l'efface — la VM suit alors de nouveau le formulaire, ce qui est le plus
attendu quand on remet le choix commun.
Elle descend jusqu'à l'exécution, comme le type de VM avant elle.
« branch » accepte désormais une chaîne pour tout le parc ou une carte
{nom: branche}, et la commande distante est bâtie par machine dès que les
branches diffèrent. Le terrain était prêt : launch_installs sait déjà
prendre une commande par VM.
Une VM peut donc porter develop pendant qu'une autre valide 1.6.0, sur le
même déploiement et dans le même tableau de suivi.
Un piège : la branche d'une rangée retombait sur develop au remontage, la
liste étant rebâtie depuis le formulaire plutôt que depuis la surcharge
qu'elle portait déjà.
Assisted-by: Claude Opus 5
A "✎" per row opens an input prefilled with the current name. The given
name becomes an override like any other: it survives recomputation, and
F4 removes it along with the VM's other settings.
An explicit name makes automatic naming GIVE WAY. Switching the VM to
GNOME no longer appends "-gnome": adding a suffix would amount to
correcting the user. Clearing the field restores the automatic name,
copy and desktop suffixes included.
The name becomes the machine's HOSTNAME, so it is validated against
RFC 1123 before being accepted. One capital or one dot too many and
cloud-init silently ignores it -- the VM would stay "ubuntu", and you
would find out once deployed. Capitals are lowered, the rest is refused
with a message.
--- FR ---
Un « ✎ » par rangée ouvre une saisie préremplie du nom courant. Le nom
donné devient une surcharge comme les autres : il survit au recalcul, et
F4 le retire avec le reste des réglages de la VM.
Un nom explicite fait CÉDER le nommage automatique. Passer la VM en
GNOME n'y colle plus « -gnome » : y ajouter un suffixe reviendrait à
corriger l'utilisateur. Vider le champ rend le nom automatique, suffixes
de copie et de bureau compris.
Le nom devient le NOM D'HÔTE de la machine : il est donc validé en
RFC 1123 avant d'être retenu. Une majuscule ou un point de trop et
cloud-init l'ignore en silence — la VM resterait « ubuntu », et on le
découvrirait une fois déployée. Les majuscules sont abaissées, le reste
est refusé avec un message.
Assisted-by: Claude Opus 5
A "+" per row adds a copy of the entry, a "−" removes it. The "−" only
appears on a copy: the original cannot be deleted by mistake, it is
unticked in the catalogue.
The name adapts on its own -- erplibre-ubuntu-2404, then -2, then -3.
The first keeps the catalogue name, so existing deployments keep theirs.
The real work is in identity. entry_key was (distro, version, arch): two
copies shared it, so setting the first set the second and their locks
fought each other. It now carries the instance number, and overrides as
well as locks follow the right machine. Each instance is a COPY of the
dictionary, never a shared reference.
Removing a copy forgets its settings: keeping them would resurrect old
values on the next copy, with nothing to explain it.
--- FR ---
Un « + » par rangée ajoute une copie de l'entrée, un « − » la retire. Le
« − » n'apparaît que sur une copie : l'original ne se supprime pas par
mégarde, il se décoche au catalogue.
Le nom s'adapte seul — erplibre-ubuntu-2404, puis -2, puis -3. Le
premier garde le nom du catalogue, pour que les déploiements existants
gardent le leur.
Le vrai travail est dans l'identité. entry_key valait
(distro, version, arch) : deux copies la partageaient, donc régler la
première réglait la seconde et leurs verrous se marchaient dessus. Elle
porte maintenant le numéro d'exemplaire, et surcharges comme verrous
suivent la bonne machine. Chaque exemplaire est une COPIE du
dictionnaire, jamais une référence partagée.
Retirer une copie oublie ses réglages : les garder ferait resurgir
d'anciennes valeurs à la copie suivante, sans que rien ne l'explique.
Assisted-by: Claude Opus 5
A 🔒 box at the head of each row. Ticked, the VM's four current values --
vCPU, RAM, disk, type -- are copied into its overrides: the x1..x4 profile
no longer reaches it. Unticked, they are removed and the VM falls back
under the profile. The lock shows on the WHOLE ROW, not in a box lost at
the end: that is what lets you scan the plan and see at once what escapes
the profile.
It rests on the override mechanism already proven, indexed by catalog
identity, so it survives a remount. Locked state stays distinct from
overrides: a VM can be edited without being frozen, and the lock covers
all four fields at once.
Four traps came with it. "remove_children()" is ASYNCHRONOUS, so giving
the card an id broke the remount, the old one still being there. Lists
kept stale values across a remount, and a refresh took back the free
entry. A frozen VM changed version all the same. And switching profile
wiped the disk size that had been set.
--- FR ---
Une case 🔒 en tête de chaque rangée. Cochée, les quatre valeurs courantes
de la VM — vCPU, RAM, disque, type — sont recopiées dans ses surcharges :
le profil x1..x4 ne l'atteint plus. Décochée, elles sont retirées et la VM
retombe sous le profil. Le verrou se voit à la LIGNE ENTIÈRE, pas à une
case perdue au bout : c'est ce qui permet de balayer le plan et de savoir
d'un coup ce qui échappe au profil.
Il s'appuie sur le mécanisme de surcharge déjà éprouvé, indexé par
identité de catalogue : il survit donc à un remontage. L'état verrouillé
reste distinct des surcharges : une VM peut être modifiée sans être figée,
et le verrou couvre les quatre champs d'un coup.
Quatre pièges l'ont accompagné. « remove_children() » est ASYNCHRONE :
donner un id à la carte faisait échouer le remontage, l'ancienne étant
encore là. Les listes gardaient des valeurs périmées au remontage, et un
rafraîchissement reprenait la saisie libre. Une VM figée changeait quand
même de version. Et changer de profil effaçait la taille de disque réglée.
Assisted-by: Claude Opus 5
The architecture was missing, though it alone explains why an install
takes ten times longer: s390x and arm64 are EMULATED on an amd64 host.
Without it you look for the fault elsewhere. An "Arch" column, right after
the name.
The table was pinned at 74 columns: beyond that nothing was reachable and
no scrollbar showed, for want of reserved space. Automatic width capped at
60% of the screen, scrolling both ways, and VISIBLE bars rather than
guessable ones.
Columns can finally be adjusted: "+" widens the cursor's, "-" narrows it,
"0" returns them all to their original width -- the same keys as the mail
TUI. auto_width is turned off along the way, otherwise the setting did not
survive the table's first update, and the resize aimed at the "#" column
whatever the cursor was on.
--- FR ---
L'architecture manquait, alors qu'elle explique à elle seule pourquoi une
installation dure dix fois plus : s390x et arm64 sont ÉMULÉES sur un hôte
amd64. Sans elle, on cherche la faute ailleurs. Une colonne « Arch »,
juste après le nom.
Le tableau était figé à 74 colonnes : au-delà rien n'était atteignable et
aucune barre de défilement n'apparaissait, faute de place réservée.
Largeur automatique plafonnée à 60 % de l'écran, défilement dans les deux
sens, et barres VISIBLES plutôt que devinables.
Les colonnes s'ajustent enfin : « + » élargit celle du curseur, « - » la
rétrécit, « 0 » les ramène toutes à leur largeur d'origine — les mêmes
touches que la TUI courriel. auto_width est désactivé au passage, sans
quoi le réglage ne survivait pas à la première mise à jour du tableau, et
le redimensionnement visait la colonne « # » quel que soit le curseur.
Assisted-by: Claude Opus 5
The monitor could only watch. The "a" key now opens the selected VM's
actions: update, restart Odoo, delete.
The update is chosen by parts, and the order is not free: system packages,
then git repositories, then Python dependencies -- the latter compile
against the former. Nothing is chained with "&&": one failing part must
not take the others down. Deleting demands a second hand, a confirmation
screen naming the disk to be erased.
Two defects the dashboard revealed. Detached installs were stealing the
shell's keystrokes, the child inheriting a terminal it had no business
reading. And the elapsed time restarted from zero every time the dashboard
was reopened, since it was counted from the view rather than from the
run's own first write.
--- FR ---
Le suivi ne savait que regarder. La touche « a » ouvre désormais les
actions de la VM sélectionnée : mettre à jour, redémarrer Odoo, supprimer.
La mise à jour se choisit par parties, et l'ordre n'est pas libre :
paquets système, puis dépôts git, puis dépendances Python — ces dernières
compilent contre les premiers. Rien n'est enchaîné par « && » : une partie
en échec ne doit pas emporter les autres. Supprimer exige une seconde
main, un écran de confirmation nommant le disque à effacer.
Deux défauts que le tableau de bord a révélés. Les installations détachées
volaient les frappes du shell, l'enfant héritant d'un terminal qu'il
n'avait pas à lire. Et la durée repartait de zéro à chaque réouverture,
comptée depuis la vue plutôt que depuis la première écriture du run.
Assisted-by: Claude Opus 5
erplibre-ubuntu-2404-gnome, erplibre-ubuntu-2404-mint. A graphical VM is
recognisable from a "virsh list", and its hostname says so too --
deploy_qemu uses the name as hostname when --hostname is not given.
This is not only cosmetic. The name is the COLLISION KEY: a graphical VM
and its server twin carried the same one, so the second was reported as
"already exists" and silently skipped. They are now distinct.
The suffix is applied AFTER overrides, the only point where each
machine's type is known now that it is chosen VM by VM. In the form the
name therefore follows the choice live, both ways.
"mint" rather than "cinnamon": that is the name chosen for the fleet,
the installed package still being Cinnamon from the distribution's own
repositories. The suffix lives with the flavour, in _QEMU_DESKTOP, and
the CLI calls the same function as the TUI rather than writing a second.
--- FR ---
erplibre-ubuntu-2404-gnome, erplibre-ubuntu-2404-mint. Une VM graphique
se reconnaît d'un « virsh list », et son nom d'hôte le dit aussi —
deploy_qemu prend le nom pour hôte quand --hostname n'est pas donné.
Ce n'est pas que cosmétique. Le nom sert de CLÉ DE COLLISION : une VM
graphique et sa jumelle serveur portaient le même, donc la seconde était
signalée « existe déjà » et silencieusement ignorée. Elles se
distinguent maintenant.
Le suffixe est appliqué APRÈS les surcharges, seul moment où le type de
chaque machine est connu depuis qu'il se choisit VM par VM. Dans le
formulaire, le nom suit donc le choix en direct, dans les deux sens.
« mint » et non « cinnamon » : c'est le nom retenu pour le parc, le
paquet installé restant Cinnamon depuis les dépôts de la distribution.
Le suffixe vit avec la saveur, dans _QEMU_DESKTOP, et la CLI appelle la
même fonction que la TUI plutôt que d'en écrire une seconde.
Assisted-by: Claude Opus 5
snap was not a choice but a fate: we disabled snapd, then gnome-core
pulled a Firefox snap that froze the install for thirty minutes. The
previous fix imposed "deb". Three answers are now offered.
deb nothing but .deb, epiphany-browser as the browser. Default,
and lightest: nothing extra to download.
flatpak Flatpak tooling as well, WITHOUT the Flathub remote or any
install. The machine is ready, the admin picks its remotes.
snap Ubuntu's default, snapd left running and Firefox as a snap.
It is the only mode where snapd is not disabled -- disabling
it was precisely the cause of the freeze.
The question is only asked when it means something: at least one
graphical VM on a distro shipping snapd, Ubuntu alone here. A server
pulls no snap, and Debian ships none. The TUI greys the choice out and
says why; the CLI does not ask.
Verified on a 26.04 VM: snapd is indeed preinstalled (2.75.2), flatpak
and its GNOME Software plugin are packaged, and firefox-esr does not
exist on Ubuntu.
--- FR ---
snap n'était plus un choix mais une fatalité : on coupait snapd, puis
gnome-core tirait un Firefox-snap qui figeait l'installation trente
minutes. Le correctif précédent imposait « deb ». Trois réponses sont
maintenant offertes.
deb rien que des .deb, epiphany-browser comme navigateur. Défaut,
et le plus léger : rien de plus à télécharger.
flatpak l'outillage Flatpak en plus, SANS dépôt Flathub ni
installation. La machine est prête, l'administrateur choisit
ses dépôts.
snap le défaut d'Ubuntu, snapd laissé actif et Firefox en snap.
C'est le seul mode où snapd n'est pas coupé — l'y couper
était précisément la cause du blocage.
La question n'est posée que lorsqu'elle a un sens : au moins une VM
graphique sur une distribution qui livre snapd, Ubuntu seule ici. Un
serveur ne tire aucun snap, et Debian n'en livre pas. La TUI grise le
choix et dit pourquoi ; la CLI ne le demande pas.
Vérifié sur une VM 26.04 : snapd y est bien préinstallé (2.75.2),
flatpak et son greffon GNOME Software sont empaquetés, et firefox-esr
n'existe pas sur Ubuntu.
Assisted-by: Claude Opus 5
A graphical Ubuntu VM stopped making progress, with no error, on "Unable
to contact the store, trying every minute for the next 30 minutes".
Nothing in the log says why, and the install just looks stuck.
The cause is ours. We disable snapd a few lines earlier, to stop its
refreshes during the install. Then gnome-core recommends "firefox", which
on Ubuntu is only a transitional package whose postinst runs "snap
install" -- against a snapd we just stopped.
It is a RECOMMENDS, with alternatives. So we exclude the snap packages and
name epiphany-browser, a real .deb. Excluding firefox alone was not
enough: apt fell back to chromium-browser, a transitional package too, and
Cinnamon froze the same way on thunderbird. Measured on the VM: with this
list, GNOME (844 packages) and Cinnamon (1167) pull none, without an apt
error.
--- FR ---
Une VM Ubuntu graphique cessait d'avancer, sans erreur, sur « Unable to
contact the store, trying every minute for the next 30 minutes ». Rien
dans le log n'en dit la raison, et l'installation paraît simplement figée.
La cause est nôtre. On coupe snapd quelques lignes plus haut, pour
empêcher ses rafraîchissements pendant l'installation. Puis gnome-core
recommande « firefox », qui n'est plus sur Ubuntu qu'un paquet de
transition dont le postinst lance « snap install » — contre un snapd qu'on
vient d'arrêter.
C'est un RECOMMENDS, avec alternatives. On écarte donc les paquets snap et
on nomme epiphany-browser, un vrai .deb. Écarter le seul firefox ne
suffisait pas : apt retombait sur chromium-browser, paquet de transition
lui aussi, et Cinnamon figeait pareillement sur thunderbird. Mesuré sur la
VM : avec cette liste, GNOME (844 paquets) et Cinnamon (1167) n'en tirent
aucun, sans erreur apt.
Assisted-by: Claude Opus 5
matplotlib dies on "c++: fatal error: Killed signal terminated program
cc1plus". "Killed" is a SIGKILL: the kernel's OOM killer, and nothing in
the message names memory. A single matplotlib source file asks cc1plus
for up to 2.5 GiB.
Two causes compound. The VM is small -- the catalogue starts at 1 or
2 GiB -- and every build backend launches nproc compilations in
parallel, each with its own cc1plus. Six cores exhaust 8 GiB.
Hence two answers. A swap file tops memory up to 8 GiB, never taking
more than half the free disk nor touching fstab -- a declared and
missing swap degrades boot. And parallelism is bounded by actual
memory, 2 GiB per task.
ninja has no parallelism environment variable, but meson-python reads
"NINJA", the executable PATH -- verified in mesonpy 0.20. We point it at
a wrapper that adds the -j. That is what saves matplotlib; MAKEFLAGS and
CMAKE_BUILD_PARALLEL_LEVEL cover the rest.
--- FR ---
matplotlib s'arrête sur « c++: fatal error: Killed signal terminated
program cc1plus ». « Killed » est un SIGKILL : c'est le tueur du noyau,
et rien dans le message ne nomme la mémoire. Un seul fichier de
matplotlib demande jusqu'à 2,5 Gio à cc1plus.
Deux causes se cumulent. La VM est petite — le catalogue démarre à 1 ou
2 Gio — et chaque moteur de build lance nproc compilations en parallèle,
chacune avec son cc1plus. Six cœurs épuisent 8 Gio.
D'où deux réponses. Un fichier d'échange complète la mémoire jusqu'à
8 Gio, sans jamais prendre plus de la moitié du disque libre ni toucher
à fstab — un swap déclaré et disparu dégrade le démarrage. Et le
parallélisme est borné d'après la mémoire réelle, 2 Gio par tâche.
ninja n'a aucune variable de parallélisme, mais meson-python lit
« NINJA », le CHEMIN de l'exécutable — vérifié dans mesonpy 0.20. On y
met une enveloppe qui ajoute le -j. C'est ce qui sauve matplotlib ;
MAKEFLAGS et CMAKE_BUILD_PARALLEL_LEVEL couvrent les autres.
Assisted-by: Claude Opus 5
VM type was global: the whole fleet as servers, or all of them GNOME. It
now lives on the VM, all the way down -- the creation flag, and one remote
command per machine at install time, where a single one served them all.
The right pane is no longer a table but a row of widgets per VM: vCPU,
RAM, disk and type, each with its usual values and a free entry. The
left-hand fields become the shared default, which the screen now says. The
scope selector and the F2 modal go away: given two ways to do the same
thing, keep the visible one.
Three traps came out of it, and the tests lock them. Widget ids carry a
RANK, and the rank shifts when an entry is ticked, so an event from an
already destroyed widget applied to the VM that took its place -- rows now
carry a generation, marked BEFORE mounting, since mount_all empties the
pending children and marking after it was a race. The x1..x4 profile no
longer reached any VM. And a total of zero never said it had counted
nothing.
--- FR ---
Le type de VM était global : tout le parc en serveur, ou tout en GNOME. Il
vit désormais sur la VM, jusqu'au bout — le drapeau de création, et une
commande distante par machine à l'installation, là où une seule les
servait toutes.
Le panneau de droite n'est plus un tableau mais une rangée de widgets par
VM : vCPU, RAM, disque et type, chacun avec ses valeurs usuelles et une
saisie libre. Les champs de gauche deviennent le défaut commun, ce que
l'écran dit maintenant. Le sélecteur de portée et la modale F2 partent :
entre deux façons de faire la même chose, on garde la visible.
Trois pièges en sont sortis, et les tests les verrouillent. Les
identifiants de widgets portent un RANG, et le rang se décale quand on
coche une entrée : un événement émis par un widget déjà détruit
s'appliquait à la VM qui avait pris sa place — les rangées portent
maintenant une génération, marquée AVANT le montage, car mount_all vide
les enfants en attente et marquer après était une course. Le profil x1..x4
n'atteignait plus aucune VM. Et un total à zéro ne disait pas qu'il
n'avait rien compté.
Assisted-by: Claude Opus 5
The three resource fields applied to the whole fleet. With a single
machine needing 16 G, the other eight got it too. A scope selector
settles it: all VMs, or the row targeted in the plan. The x1..x4 profile
stays global, multiplying what each image asks for; under "selected VM"
the fields are absolute, hence active even outside the custom profile.
The F2 modal already existed but was undiscoverable, and its cursor was
unusable: the table never held focus. It stays, the toggle now focuses
the table, F4 returns a VM to the shared profile, and the plan marks
customised rows with a ✎ -- without it, two rows with different
resources have no explanation on screen.
One defect found along the way: clear() reset the cursor to the top on
every redraw, and the plan recomputes on every keystroke. You picked
debian, typed the value, the table redrew, and the next entry landed on
ubuntu with nothing to show for it.
--- FR ---
Les trois champs de ressources s'appliquaient à tout le parc. Une seule
machine ayant besoin de 16 G, il fallait les donner aux huit autres. Un
sélecteur de portée tranche : toutes les VM, ou la ligne visée dans le
plan. Le profil x1..x4 reste global, il multiplie ce que demande chaque
image ; en portée « une seule » les champs valent en absolu, et sont donc
actifs même hors profil personnalisé.
La modale F2 existait déjà mais restait introuvable, et son curseur était
inutilisable : le tableau n'avait jamais le focus. Elle demeure, la
bascule lui donne le focus, F4 rend une VM au profil commun, et le plan
marque d'un ✎ ce qui a été personnalisé — sans marque, deux lignes aux
ressources différentes n'ont aucune explication à l'écran.
Un défaut au passage : « clear() » ramenait le curseur en tête à chaque
redessin, et le plan se recalcule à chaque frappe. On choisissait debian,
on tapait la valeur, le tableau se redessinait, et la saisie suivante
partait sur ubuntu sans que rien ne le montre.
Assisted-by: Claude Opus 5
The catalogue only offered openSUSE Tumbleweed, a rolling release with
no version number. Nearly every openSUSE incident in this series came
from that: the cloud image is a snapshot lagging behind its own repos,
hence the mandatory "zypper dup", and two VMs deployed the same day saw
git-daemon 2.54 on s390x against 2.55 on amd64. For an ERP platform,
that is not what should be offered by default.
Leap 16.0 is numbered, stable, and publishes the three architectures
that matter -- verified, x86_64, aarch64 and s390x all 200. It also
unifies its tree: no separate /ports/, unlike Tumbleweed, whose
equivalent paths 404 for Leap.
Tumbleweed stays on offer, as a bellwether for breakage to come. Two
things separate them in the scripts: the repository path, and "dup" --
which on Leap means CHANGING version, not updating.
--- FR ---
Le catalogue n'offrait qu'openSUSE Tumbleweed, une rolling sans numéro
de version. Presque tous les incidents openSUSE de la série venaient de
là : l'image cloud est un instantané en retard sur ses dépôts, d'où le
« zypper dup » obligatoire, et deux VM déployées le même jour ont vu
git-daemon 2.54 sur s390x contre 2.55 sur amd64. Pour une plateforme
ERP, ce n'est pas ce qu'on veut proposer par défaut.
Leap 16.0 est numérotée, stable, et publie les trois architectures qui
comptent — vérifié, x86_64, aarch64 et s390x en 200. Elle unifie de plus
son arbre : pas de /ports/ séparé, contrairement à Tumbleweed, dont les
chemins équivalents rendent 404 pour Leap.
Tumbleweed reste offerte, comme banc d'essai des ruptures à venir.
Deux points les séparent dans les scripts : le chemin des dépôts, et
« dup » — qui sur Leap sert à CHANGER de version, pas à mettre à jour.
Assisted-by: Claude Opus 5
uv replaces pip where it can: the tools venv and the Poetry bootstrap. The
choice lives in EL_PIP_PROVIDER -- auto, uv or pip -- and in a single
file, lib_pip_provider.sh, on the exact model of the mise/pyenv switch. A
uv failure falls back to pip: it is stricter on metadata and rejects
packages pip accepts.
Three guards. The target always goes through "--python" rather than being
inferred from the active venv: poetry.toml declares a "./.venv" uv would
also look for. Python 3.7, still used by Odoo 12 and 13, stays on pip. And
"uv pip sync" is never used: it would delete pip itself and everything
Poetry laid down.
One bug falls along the way: the idempotence guard aimed at
.venv.erplibre/bin/poetry while Poetry installs into the Odoo venv, so a
successful replay reported failure all the same.
--- FR ---
uv remplace pip là où il le peut : le venv d'outils et l'amorçage de
Poetry. Le choix vit dans EL_PIP_PROVIDER — auto, uv ou pip — et dans un
seul fichier, lib_pip_provider.sh, sur le modèle exact de l'aiguillage
mise/pyenv. Un échec d'uv retombe sur pip : il est plus strict sur les
métadonnées et refuse des paquets que pip accepte.
Trois gardes. La cible passe toujours par « --python » plutôt que d'être
déduite du venv actif : poetry.toml déclare un « ./.venv » qu'uv
chercherait aussi. Python 3.7, encore utilisé par Odoo 12 et 13, reste sur
pip. Et « uv pip sync » n'est jamais employé : il supprimerait pip
lui-même et tout ce que Poetry a posé.
Un défaut tombe au passage : le garde d'idempotence visait
.venv.erplibre/bin/poetry alors que Poetry s'installe dans le venv Odoo,
si bien qu'un rejeu réussi rapportait quand même un échec.
Assisted-by: Claude Opus 5
Measured from Montréal on extra.db: mirror.quantum5.ca 2.0 s,
mirror.xenyth.net 7.1 s, against 8.0 s for geo.mirror.pkgbuild.com. The
official "geographic" mirror is therefore not the best one here, and
reflector, which sorts by throughput from inside the VM, had not picked
them.
Both are PREPENDED, never substituted: whatever reflector chose stays
below, as fallback. The write therefore comes after it, since "--save"
overwrites the file. Guarded on x86_64, these mirrors serving that
architecture only. Host side, the addition is idempotent.
The desktop install went through neither step: it ran without the near
mirrors and without the full update a rolling release demands, so it
pulled from Europe and broke on partial upgrades.
--- FR ---
Mesuré depuis Montréal sur extra.db : mirror.quantum5.ca 2,0 s,
mirror.xenyth.net 7,1 s, contre 8,0 s pour geo.mirror.pkgbuild.com. Le
miroir « géographique » officiel n'est donc pas le meilleur ici, et
reflector, qui trie par débit depuis la VM, ne les avait pas retenus.
Les deux sont mis EN TÊTE, jamais substitués : ce que reflector a choisi
reste dessous, en repli. L'écriture vient donc après lui, puisque
« --save » écrase le fichier. Gardé sur x86_64, ces miroirs ne servant que
cette architecture. Côté hôte, l'ajout est idempotent.
L'installation du bureau ne passait par aucune des deux étapes : elle
s'exécutait sans les miroirs proches et sans la mise à jour complète
qu'exige une rolling release, tirant donc d'Europe et cassant sur des
mises à jour partielles.
Assisted-by: Claude Opus 5
The interpreter provider is now chosen when creating a VM, in both
interfaces. "mise" installs it in the VM and lays down a precompiled
CPython; "pyenv" keeps today's build-from-source.
The question is only asked where it means something: mise publishes
binaries for amd64 and arm64 only. An all-s390x fleet never sees it, a
mixed fleet sees it along with the architectures that will fall back to
pyenv -- said before deploying, not discovered in a log an hour later.
mise goes into /usr/local/bin rather than ~/.local/bin: the remote command
runs through "ssh host 'command'", where neither ~/.profile nor ~/.bashrc
is read. Choosing "mise" exports "auto", not "mise", so a failed install
can still fall back to pyenv.
The unavailability notice was translated as "pyenv required", which said
the opposite of what happens: pyenv is the fallback, not a demand.
--- FR ---
Le fournisseur d'interpréteur se choisit désormais à la création d'une VM,
dans les deux interfaces. « mise » l'installe dans la VM et pose un
CPython précompilé ; « pyenv » garde la compilation d'aujourd'hui.
La question n'est posée que là où elle a un sens : mise ne publie de
binaires que pour amd64 et arm64. Un parc tout s390x ne la voit jamais, un
parc mixte la voit avec les architectures qui retomberont sur pyenv — dit
avant de déployer, pas découvert dans un log une heure plus tard.
mise va dans /usr/local/bin plutôt que ~/.local/bin : la commande distante
passe par « ssh hôte 'commande' », où ni ~/.profile ni ~/.bashrc ne sont
lus. Choisir « mise » exporte « auto », pas « mise », pour qu'une
installation ratée puisse encore retomber sur pyenv.
L'avis d'indisponibilité était traduit par « pyenv exigé », qui disait
l'inverse de ce qui se passe : pyenv est le repli, pas une exigence.
Assisted-by: Claude Opus 5
mise lays down a precompiled CPython where pyenv builds one: seconds
against one to three minutes, with no -dev package at all. The choice
lives in EL_PYTHON_PROVIDER -- auto, mise or pyenv -- and in a single
file, lib_python_provider.sh. The rest of the repository only knows venv
paths and needs to know none of this.
In auto mode an ALREADY installed interpreter wins, whichever provider put
it there. mise is never installed on its own, "curl | sh" commits too much
for a script to decide -- make install_mise carries that decision.
MISE_PYTHON_COMPILE=false forbids it from quietly compiling: without that
guard it would fall back to pyenv's own engine, giving us the slowness
without the tooling. That is also what stopped gcc from collapsing while
building CPython on a low-memory s390x guest, for nothing.
One real bug falls along the way: a failed venv did not stop the install,
which then went on and failed further down, far from the cause.
--- FR ---
mise pose un CPython précompilé là où pyenv en compile un : des secondes
contre une à trois minutes, et aucun paquet -dev. Le choix vit dans
EL_PYTHON_PROVIDER — auto, mise ou pyenv — et dans un seul fichier,
lib_python_provider.sh. Le reste du dépôt ne connaît que des chemins de
venv et n'a rien à savoir de tout cela.
En mode auto, un interpréteur DÉJÀ posé l'emporte, quel qu'en soit le
fournisseur. mise n'est jamais installé de lui-même, « curl | sh » engage
trop pour qu'un script en décide — make install_mise porte cette décision.
MISE_PYTHON_COMPILE=false lui interdit de compiler en silence : sans ce
garde, il retomberait sur le moteur de pyenv, et nous aurions sa lenteur
sans son outillage. C'est aussi ce qui a évité que gcc s'écroule en
bâtissant CPython sur une VM s390x à faible mémoire, inutilement.
Un vrai défaut tombe au passage : un venv raté n'arrêtait pas
l'installation, qui continuait et échouait plus loin, loin de la cause.
Assisted-by: Claude Opus 5
"dnf install: error: unrecognized arguments" on AlmaLinux 10 and Rocky 10,
on every call: PostgreSQL, build dependencies, pyenv. The tools group got
away with its fallback, which made the trace misleading -- gcc installed,
nothing after it did.
"--skip-unavailable" is a dnf5 option, which the script's own comment
already said while assuming it everywhere. Fedora 41+ ships dnf5, but EL9
and EL10 stay on dnf4, whose equivalent is "--setopt=strict=0". We now ask
dnf what it understands instead of inferring it from the distribution.
Three neighbouring fixes: the "c-development" group does not exist on EL,
where it is called "development"; CRB is enabled through /usr/bin/crb; and
g++ plus the missing headers are installed explicitly. Rust is added only
where wheels are absent, and qpdf is built when the distribution ships one
older than pikepdf demands.
--- FR ---
« dnf install: error: unrecognized arguments » sur AlmaLinux 10 et
Rocky 10, à chaque appel : PostgreSQL, dépendances de compilation, pyenv.
Le groupe d'outils s'en tirait par son repli, ce qui rendait la trace
trompeuse — gcc installé, rien après lui.
« --skip-unavailable » est une option de dnf5, ce que le commentaire du
script disait déjà tout en la supposant partout. Fedora 41+ livre dnf5,
mais EL9 et EL10 restent sur dnf4, dont l'équivalent est
« --setopt=strict=0 ». On demande maintenant à dnf ce qu'il comprend
plutôt que de le déduire de la distribution.
Trois correctifs voisins : le groupe « c-development » n'existe pas sur
EL, où il s'appelle « development » ; CRB s'active par /usr/bin/crb ; et
g++ ainsi que les en-têtes manquants sont posés explicitement. Rust n'est
ajouté que là où les roues manquent, et qpdf est compilé quand la
distribution en livre un plus ancien que ce qu'exige pikepdf.
Assisted-by: Claude Opus 5
Tumbleweed is the only catalog entry whose qpdf already clears the pikepdf
threshold: 12.3.2 against 12.2 required, so the half-hour qpdf build under
s390x emulation never runs there. It is also the only family foreign to
both RHEL and Debian, and it brings zypper, absent from the repository:
wired into the install_dev.sh dispatch, a dependency script, the remote
bootstrap and the desktop block.
Four SUSE traps, all met on real machines. Global options precede the
subcommand, and misplaced ones abort the call; without
"--auto-agree-with-licenses" zypper waits for an answer nobody gives in a
detached install. A compat package that PROVIDES another under a different
name makes zypper raise a conflict and drop the whole batch, so only what
nothing already provides is requested. pkg-config no longer exists as an
RPM, only as a capability. And a single mirror, unreachable one day, sent
everyone back to Europe -- three are probed in order now.
CentOS Stream is dropped again: it served as a canary but 10 duplicates
what Alma and Rocky already cover.
--- FR ---
Tumbleweed est la seule entrée du catalogue dont qpdf franchit déjà le
seuil de pikepdf : 12.3.2 contre 12.2 exigé, si bien que la demi-heure de
compilation de qpdf sous émulation s390x ne s'y déclenche jamais. C'est
aussi la seule famille étrangère à RHEL comme à Debian, et elle apporte
zypper, absent du dépôt : câblé dans l'aiguillage d'install_dev.sh, un
script de dépendances, l'amorçage distant et le bloc bureau.
Quatre pièges SUSE, tous rencontrés sur machine réelle. Les options
globales précèdent la sous-commande, mal placées elles arrêtent l'appel ;
sans « --auto-agree-with-licenses » zypper attend une réponse que personne
ne donne dans une installation détachée. Un paquet compat qui FOURNIT un
autre sous un nom différent fait lever un conflit à zypper, qui abandonne
le lot entier : on ne demande donc que ce que rien ne fournit déjà.
pkg-config n'existe plus comme RPM, seulement comme capacité. Et un miroir
unique, injoignable un jour, renvoyait tout le monde en Europe — trois
sont désormais sondés dans l'ordre.
CentOS Stream repart : il servait de canari, mais 10 double ce qu'Alma et
Rocky couvrent déjà.
Assisted-by: Claude Opus 5
Fedora does publish an s390x cloud image, just elsewhere: the arch is
SECONDARY there, its images live under "fedora-secondary" rather than
/pub/fedora/linux/. The catalog comment read "404" -- it was the wrong
tree.
It brings what nothing else has on this architecture: qpdf 12.2.0,
exactly the pikepdf 10 threshold. The qpdf build, half an hour under
emulation, disappears. With GCC 15, Python 3.14 and cargo 1.90, none of
the s390x workarounds fire there.
Only 43 is kept: Fedora builds s390x for the current release only. 41 and
42 return 404 on the master mirror, 44 sits on third-party mirrors only.
Hence a positive per-architecture declaration, read by both interfaces --
the catalog never offers what deployment would refuse.
--- FR ---
Fedora publie bien une image cloud s390x, mais ailleurs : l'architecture
est SECONDAIRE chez elle, ses images vivent sous « fedora-secondary » et
non sous /pub/fedora/linux/. Le commentaire du catalogue disait « 404 » —
c'était la mauvaise arborescence.
Elle apporte ce qu'aucune autre n'a sur cette architecture : qpdf 12.2.0,
soit exactement le seuil de pikepdf 10. La compilation de qpdf, une demi-
heure sous émulation, disparaît. Avec GCC 15, Python 3.14 et cargo 1.90,
aucun des contournements s390x ne s'y déclenche.
Seule la 43 est retenue : Fedora ne construit s390x que pour la version
courante. Les 41 et 42 rendent 404 sur le miroir maître, la 44 n'est que
sur certains miroirs tiers. D'où une déclaration positive par
architecture, que les deux interfaces relisent — le catalogue ne propose
pas ce que le déploiement refuserait.
Assisted-by: Claude Opus 5
The server-or-desktop choice only showed on the left, in the form. The
table on the right, the one read before launching, said nothing about it:
two visually identical plans could produce different VMs.
The Status column, empty until now for a VM to create, therefore carries
the type, and the totals line repeats it. An already defined VM keeps its
collision message: it is not touched, showing a desktop there would
suggest one is about to be installed on it.
--- FR ---
Le choix serveur ou bureau ne se voyait qu'à gauche, dans le formulaire.
Le tableau de droite, celui qu'on relit avant de lancer, n'en disait
rien : deux plans identiques à l'écran pouvaient produire des VM
différentes.
La colonne Statut, vide jusqu'ici pour une VM à créer, porte donc le
type, et la ligne de totaux le rappelle. Une VM déjà définie garde son
message de collision : elle n'est pas retouchée, lui afficher un bureau
laisserait croire qu'on va lui en poser un.
Assisted-by: Claude Opus 5
Ubuntu 20.04 and 22.04 leave EVERY architecture, not just s390x. pikepdf
needs qpdf 12.2, whose build requires C++20, while focal ships GCC 9 and
publishes no g++-10 for s390x at all. Python 3.8, node 10, cargo 0.67 and
OpenSSL 1.1.1 each had a workaround; the pile of them did not. 18.04
follows, already off the lists. The refusal lands before any apt, this
script also serving existing machines.
AlmaLinux 9 and 10, Rocky 9 and 10 join the catalog on all four
architectures: the twelve "latest" URLs were opened, with no index to
parse unlike Fedora. They would have booted unreachable though -- the
cloud-config forced "groups: users, sudo", but the RHEL family has no
sudo group, only wheel, and an unknown group makes useradd fail, hence no
password and no key. The very trap already known for Debian, repeated
elsewhere. Host side, EPEL and CRB are enabled: without them most -devel
packages are missing, silently.
The server / graphical choice gains Cinnamon, the Linux Mint desktop,
from the distribution's own repositories. Mint's repository is set aside:
plain HTTP, and i386/amd64 only, which would rule out arm64 and s390x.
Along the way, dnf now installs an ENVIRONMENT rather than a group --
"gnome-desktop" brings gdm and gnome-shell but not base-x, hence no X
server.
--- FR ---
Ubuntu 20.04 et 22.04 partent de TOUTES les architectures, pas seulement
de s390x. pikepdf réclame qpdf 12.2, dont la compilation exige C++20,
quand focal livre GCC 9 et ne publie même pas de g++-10 pour s390x.
Python 3.8, node 10, cargo 0.67 et OpenSSL 1.1.1 avaient chacun leur
contournement ; leur accumulation, non. 18.04 suit, déjà hors des
listes. Le refus tombe avant tout apt, ce script servant aussi les
machines existantes.
AlmaLinux 9 et 10, Rocky 9 et 10 entrent au catalogue, sur les quatre
architectures : les douze URL « latest » ont été ouvertes, aucun index à
analyser contrairement à Fedora. Elles auraient pourtant démarré
inaccessibles — le cloud-config imposait « groups: users, sudo », or la
famille RHEL n'a pas de groupe sudo mais wheel, et un groupe inconnu fait
échouer useradd, donc ni mot de passe ni clé. C'est le piège déjà connu
pour Debian, reproduit ailleurs. Côté hôte, EPEL et CRB sont activés :
sans eux la plupart des -devel manquent, en silence.
Le choix serveur / graphique gagne Cinnamon, le bureau de Linux Mint,
depuis les dépôts de la distribution. Le dépôt de Mint lui-même est
écarté : il est en HTTP nu et ne publie que i386 et amd64, ce qui
exclurait arm64 et s390x. Au passage, dnf installe désormais un
ENVIRONNEMENT et non un groupe — « gnome-desktop » apporte gdm et
gnome-shell mais pas base-x, donc pas de serveur X.
Assisted-by: Claude Opus 5
VMs could only be servers. A server-or-graphical choice joins both
interfaces, installing GNOME along with remote access to it.
Packages come from the remote command, not cloud-init: their 1 to 2 GB
would stretch an already long boot there while leaving no trace in the
monitoring, and group installs do not go through it. The desktop
therefore does not depend on ERPLibre -- a VM may be wanted graphical
and bare.
Each distribution has its own names, taken from the source: Arch has no
xrdp in its official repositories and takes TigerVNC. The SPICE display
is set only on amd64 and arm64; s390x does expose virtio-gpu-ccw, but
nothing guarantees its kernel's DRM driver, whereas remote desktop works
everywhere.
--- FR ---
Les VM ne pouvaient être que des serveurs. Un choix serveur ou graphique
s'ajoute aux deux interfaces, et pose GNOME avec son accès distant.
Les paquets viennent de la commande distante, pas de cloud-init : leurs
1 à 2 Go y allongeraient un démarrage déjà long sans laisser de trace
dans le suivi, et les installations par groupe n'y passent pas. Le bureau
ne dépend donc pas d'ERPLibre — une VM peut être voulue graphique et nue.
Chaque distribution a ses noms, relevés à la source : Arch n'a pas xrdp
dans ses dépôts officiels et prend TigerVNC. L'écran virtuel SPICE n'est
posé que sur amd64 et arm64 ; s390x expose bien virtio-gpu-ccw, mais rien
ne garantit le pilote DRM de son noyau, alors que le bureau distant, lui,
marche partout.
Assisted-by: Claude Opus 5
Changing state meant retyping the name, on a single comma-separated line.
Names are long and alike -- two of them sometimes differ by a suffix
only -- and a typo here decided whether a machine started or shut down.
The screen now reuses the numbered list from the delete screen, "all"
included, and its parser, which already accepts names: nothing is lost
for those who typed them. The warning on an unknown entry is kept, that
parser otherwise dropping it without a word.
--- FR ---
Changer l'état demandait de retaper le nom, sur une seule ligne séparée
par des virgules. Les noms sont longs et se ressemblent — deux d'entre
eux ne diffèrent parfois que par un suffixe — et une faute de frappe
portait ici sur le démarrage ou l'extinction d'une machine.
L'écran reprend donc la liste numérotée de la suppression, « all »
compris, et son analyseur, qui accepte déjà les noms : rien n'est perdu
pour qui les tapait. L'avertissement sur une entrée inconnue est
conservé, cet analyseur l'écartant autrement sans un mot.
Assisted-by: Claude Opus 5
The form's parallelism was pinned at 4, while the CLI already counted
host cores: two interfaces, two answers.
A box ticked by default now gives one run per install -- five VMs, five
deployments -- and the core count stops capping it. Unticking hands
control back to the dropdown, whose default finally follows the host. The
CLI offers the same toggle through "n".
--- FR ---
Le parallélisme du formulaire était figé à 4, quand la CLI comptait déjà
les cœurs de l'hôte : deux interfaces, deux réponses.
Une case cochée par défaut donne désormais une exécution par
installation — cinq VM, cinq déploiements — et le nombre de cœurs cesse
alors de plafonner. La décocher rend la main à la liste, dont le défaut
suit enfin l'hôte. La CLI offre la même bascule par « n ».
Assisted-by: Claude Opus 5
Monitoring stayed on the same address for 1170 s, never catching up with
the VM. Two holes, in the very re-resolution meant to prevent that.
The lease fallback required an answer on port 22. But dnsmasq keeps one
lease per MAC: when cloud-init sets the real hostname and the DHCP client
asks again, the lease MOVES the address. The old one no longer belongs to
the VM and sshd will never answer there. The lease therefore wins as soon
as it stops listing the current address.
The other hole explains the silence: virsh was muted on both branches, so
an unreachable libvirt kept the initial IP without a single line saying
so. The log went quiet for a quarter of an hour for the same reason --
cloud-init holds the package lock while writing nothing.
--- FR ---
Le suivi restait sur la même adresse pendant 1170 s, sans jamais rattraper
la VM. Deux trous, dans la re-résolution censée l'éviter.
Le repli par bail exigeait une réponse sur le port 22. Or dnsmasq garde un
bail par MAC : quand cloud-init pose le vrai nom d'hôte et que le client
DHCP redemande, le bail DÉPLACE l'adresse. L'ancienne n'appartient plus à
la VM et sshd n'y répondra jamais. Le bail l'emporte donc dès qu'il cesse
de lister l'adresse courante.
L'autre trou explique le silence : virsh était muet sur les deux branches,
si bien qu'un libvirt injoignable conservait l'IP initiale sans une ligne
pour le dire. Le log se taisait un quart d'heure pour la même raison —
cloud-init tient le verrou des paquets sans rien écrire.
Assisted-by: Claude Opus 5
Two blockers on 20.04, neither worth carrying. "TypeError: unsupported
operand type(s) for |" fired before the install even started:
update_env_version.py imports erplibre_state as the very first thing
"make install_os" does, hence before pyenv, on the system Python 3.8,
where a "str | None" annotation is evaluated at import. And cryptography
now demands OpenSSL 3, while focal ships 1.1.1.
Annotations are therefore deferred, a contract the bootstrap file already
stated in a comment and now holds across script/version and
script/install. But the releases themselves go: pikepdf requires
qpdf >= 12.2, compiled in C++20, when focal ships GCC 9.
--- FR ---
Deux blocages sur 20.04, aucun ne méritant d'être porté. « TypeError:
unsupported operand type(s) for | » se déclenchait avant même le début de
l'installation : update_env_version.py importe erplibre_state en tout
premier lieu de « make install_os », donc avant pyenv, sur le Python 3.8
du système, où une annotation « str | None » est évaluée à l'import. Et
cryptography exige désormais OpenSSL 3, quand focal livre 1.1.1.
Les annotations sont donc différées, contrat que le fichier d'amorçage
énonçait déjà en commentaire et qui tient maintenant sur script/version et
script/install. Mais les versions elles-mêmes partent : pikepdf réclame
qpdf >= 12.2, compilé en C++20, quand focal livre GCC 9.
Assisted-by: Claude Opus 5
Installs run detached (setsid -f): closing the terminal does not stop
them, but it lost the only view onto them. With no way to resume, the
only way out was deleting the VMs and starting over.
Picking "Deploy" now looks at the latest run: if VMs there still lack an
exit marker, it offers to reopen its monitoring instead of starting
another. Time since the last write is shown, a dead run being otherwise
indistinguishable from a live one.
Only the latest run is examined: an old one left without a marker would
flag a phantom install forever. Dry-run creates nothing, so it does not
ask. "Reopen monitoring" moves to the Deployment section, where one
looks for it.
--- FR ---
Les installs partent détachées (setsid -f) : fermer le terminal ne les
arrête pas, mais faisait perdre la seule vue dessus. Sans moyen de
reprendre, la seule issue était d'effacer les VM et de recommencer.
Choisir « Déployer » regarde donc le dernier run : s'il lui reste des VM
sans marqueur de sortie, il propose de rouvrir son suivi plutôt que d'en
lancer un autre. Le silence depuis la dernière écriture est affiché, un
run mort n'étant pas distinguable autrement d'un run vivant.
Seul le dernier run est examiné : un run ancien laissé sans marqueur
signalerait éternellement une install fantôme. L'aperçu ne crée rien, il
ne pose pas la question. « Rouvrir le suivi » rejoint la section
Déploiement, où on le cherche.
Assisted-by: Claude Opus 5
The CLI already took a typed value -- a letter picks a suggestion, a
digit stands for itself. The form, though, locked the custom profile
into its three dropdowns.
Each now gets a final "free value..." entry revealing an input below it.
An invalid entry overwrites nothing: the resource falls back to the
catalog value instead of freezing the view.
The 3 vCPU preset was missing too, between 2 and 4; it comes from the
same constant, so both interfaces gain it together.
--- FR ---
La CLI acceptait déjà une valeur tapée — une lettre choisit une
suggestion, un chiffre vaut pour lui-même. Le formulaire, lui, enfermait
le profil personnalisé dans ses trois listes déroulantes.
Chacune reçoit donc un dernier choix, « valeur libre… », qui révèle une
saisie sous elle. Une entrée invalide n'écrase rien : la ressource
retombe sur celle du catalogue plutôt que de bloquer la vue.
Le préréglage 3 vCPU manquait aussi, entre 2 et 4 ; il vient de la même
constante, donc les deux interfaces l'offrent ensemble.
Assisted-by: Claude Opus 5
"TypeError: unsupported operand type(s) for +: 'int' and 'NoSelection'"
as soon as the custom resource profile was picked.
Textual 8 turned Select.BLANK into a deprecated alias worth False, the
sentinel having become Select.NULL. The guard compared against False and
filtered nothing; NoSelection, lacking __bool__ and thus truthy, sailed
through "or default" into the plan totals.
The sentinel is now resolved at runtime, and both summed fields are
coerced to a positive int inside the pure function -- where the invariant
belongs, whatever Textual version is installed.
--- FR ---
« TypeError: unsupported operand type(s) for +: 'int' and 'NoSelection' »
dès qu'on choisissait le profil personnalisé.
Textual 8 a ramené Select.BLANK à un alias déprécié valant False, le
sentinelle étant devenu Select.NULL. La garde comparait donc à False et
ne filtrait plus rien ; NoSelection, dépourvu de __bool__ et tenu pour
vrai, traversait « ou valeur par défaut » jusqu'aux sommes du plan.
Le sentinelle est résolu à l'exécution, et les deux champs additionnés
sont ramenés à un entier positif dans la fonction pure — c'est là que
l'invariant appartient, quelle que soit la version de Textual.
Assisted-by: Claude Opus 5
factur-x >= 4.0 needs saxonche, which Saxonica ships no s390x wheel for.
Pin 3.x there, 4.2+ everywhere else.
PEP 508 markers now survive the whole chain: kept instead of evaluated,
and each line reaches "poetry add" as a single argument -- the
"grep -v ';'" silently dropped them. Since "poetry add" dedupes by name,
the multi-constraint form, which the CLI cannot express, is written back
into the pyproject and the lock regenerated.
PyMuPDF is set aside on s390x for the same reason: MuPDF does not build
there. Along the way, the ignore list missed pymssql, whose key carried
its version, and two pre-existing solver deadlocks, lxml-html-clean and
s3fs, blocked any regeneration.
--- FR ---
factur-x >= 4.0 dépend de saxonche, sans roue s390x chez Saxonica. On
épingle la 3.x là-bas, la 4.2+ ailleurs.
Le marqueur PEP 508 traverse maintenant la chaîne : il est conservé au
lieu d'être évalué, et chaque ligne devient un seul argument de « poetry
add » — le « grep -v ';' » écartait ces lignes en silence. « poetry add »
dédupliquant par nom, la forme multi-contraintes, hors de portée du CLI,
est réécrite dans le pyproject, puis le lock refait.
PyMuPDF est écarté sur s390x pour la même raison : MuPDF ne s'y construit
pas. Au passage, la liste d'ignorés ratait pymssql, dont la clé portait sa
version, et deux blocages préexistants du solveur, lxml-html-clean et
s3fs, interdisaient toute régénération.
Assisted-by: Claude Opus 5