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
Bringing s390x up on Debian and Ubuntu turned up one blocker at a time,
each found on a real machine: no wheel is published there, so everything
compiles against distribution headers. manifold3d needs tbb, cmake and
ninja; pymupdf loads libclang.so by its bare name through ctypes and only
a versioned one is packaged; cryptography and bcrypt need Rust; pikepdf
needs qpdf, whose -dev package is named differently across releases.
Two traps cost the most time. One unknown name made apt refuse the whole
batch of fifteen without ever saying which, so apt itself is now asked
what is installable, and a failed batch retries package by package. And a
failed unpack leaves dpkg half-configured, after which every apt call
blames packages that are in fact installed -- hence the repair step.
npm is the other half: npm@latest outran the packaged node and aborted
install_os, NodeSource publishes nothing for s390x, and Ubuntu's nodejs
package carries no npm on any release.
--- FR ---
Porter s390x sur Debian et Ubuntu a révélé un blocage à la fois, chacun
sur une machine réelle : aucune roue n'y est publiée, donc tout se compile
contre les en-têtes de la distribution. manifold3d exige tbb, cmake et
ninja ; pymupdf charge libclang.so par son nom nu via ctypes et seul un
nom versionné est empaqueté ; cryptography et bcrypt réclament Rust ;
pikepdf veut qpdf, dont le paquet -dev change de nom selon la version.
Deux pièges ont coûté le plus. Un seul nom inconnu faisait refuser à apt
le lot entier de quinze, sans jamais dire lequel : on demande désormais à
apt ce qui est installable, et un lot en échec reprend paquet par paquet.
Et un dépaquetage raté laisse dpkg à moitié configuré, après quoi tout
apt accuse des paquets pourtant installés — d'où l'étape de réparation.
npm est l'autre moitié : npm@latest dépassait le node empaqueté et
arrêtait install_os, NodeSource ne publie rien pour s390x, et le paquet
nodejs d'Ubuntu ne porte npm sur aucune version.
Assisted-by: Claude Opus 5
Four papercuts met while using the CLI: the test menu returned a verdict
nobody could find at the end of the output, the file browser never closed
once a file was picked, a wrong KeePass password killed the whole CLI, and
a configuration key had drifted from the code that reads it.
--- FR ---
Quatre irritants rencontrés à l'usage : le menu Test rendait un verdict
introuvable en fin de sortie, le navigateur de fichiers ne se fermait
jamais une fois le fichier choisi, un mauvais mot de passe KeePass tuait
tout le CLI, et une clé de configuration avait dérivé du code qui la lit.
Assisted-by: Claude Opus 5
An IMAP/SMTP client in the menu, with its tests against real servers. Most
of the work went into refusals: an application password is named only when
the server actually refuses, an accented password is reported as never
having left the machine, and a refusal is recognised by what the server
SAYS rather than by matching its wording. A malformed date no longer takes
the whole folder down, and a received email is never read as markup.
--- FR ---
Un client IMAP/SMTP dans le menu, avec ses tests contre de vrais serveurs.
L'essentiel du travail porte sur les refus : le mot de passe d'application
n'est nommé que lorsque le serveur refuse vraiment, un mot de passe accentué
est signalé comme n'ayant jamais quitté la machine, et un refus se reconnaît
à ce que le serveur DIT plutôt qu'à ses mots. Une date illisible n'emporte
plus le dossier entier, et un courriel reçu n'est jamais lu comme du balisage.
Assisted-by: Claude Opus 5
The monitor froze the address given at launch, so it lost the VM as soon as
cloud-init renamed the host and DHCP handed out another lease. It now
re-resolves at each attempt, in the views too, and reads virsh without sudo
— « sudo -n » fails in a detached session with no tty.
First boot also stopped paying for what it does not need: the guest agent
leaves cloud-init, snapd and locale-gen go, apt takes the fastest mirror.
The timezone follows the host. And when KVM is missing, the deployment says
so before the wait instead of being mysteriously fifteen times slower.
--- FR ---
Le suivi figeait l'adresse connue au lancement : il perdait donc la VM dès
que cloud-init posait le vrai nom d'hôte et que DHCP donnait un autre bail.
Il la ré-résout désormais à chaque tentative, dans les vues aussi, et lit
virsh sans sudo — « sudo -n » échoue dans une session détachée, sans tty.
Le premier démarrage cesse aussi de payer l'inutile : l'agent invité sort
de cloud-init, snapd et locale-gen disparaissent, apt prend le miroir le
plus rapide. Le fuseau suit l'hôte. Et faute de KVM, le déploiement le dit
avant l'attente, au lieu d'être quinze fois plus lent sans raison visible.
Assisted-by: Claude Opus 5
repo cannot resolve a commit into a manifest revision on a FRESH workspace:
it gave up on « unparseable HEAD », then repo sync on « manifest not
found ». An already-initialised .repo accepts the SHA, which is why only
new installations were hit — four fresh VMs failed identically.
--- FR ---
repo ne sait pas résoudre un commit en révision de manifeste sur un espace
de travail NEUF : il abandonnait sur « unparseable HEAD », puis repo sync
sur « manifest not found ». Un .repo déjà initialisé accepte le SHA, d'où
un défaut limité aux installations neuves — quatre VM ont échoué ainsi.
Assisted-by: Claude Opus 5
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
A read-only toolkit answering « what is in this database, and what will
break on a version bump »: schema size and tables no model claims,
customised views compared with their module source, and the x_ fields
Studio left behind. It reads a backup zip directly, so a 40 GB dump needs
no restore, and it reaches every answer from the TODO menu.
--- FR ---
Une boîte à outils en lecture seule qui répond à « qu'y a-t-il dans cette
base, et qu'est-ce qui cassera à la montée de version » : taille du schéma
et tables qu'aucun modèle ne réclame, vues personnalisées comparées à leur
source, champs x_ laissés par Studio. Elle lit un zip de sauvegarde tel
quel, donc sans restaurer, et tout s'atteint depuis le menu TODO.
Assisted-by: Claude Opus 5
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
Groups the long menus into sections with icons, accepts o/oui everywhere,
shows the default of each yes/no question, and stops offering a traceback
line as a database name when PostgreSQL is unreachable.
--- FR ---
Regroupe les menus longs en sections avec icônes, accepte o/oui partout,
affiche la valeur par défaut de chaque question oui/non, et cesse de
proposer une ligne de trace d'appel comme nom de base quand PostgreSQL est
injoignable.
Assisted-by: Claude Opus 4.8
Assisted-by: Claude Opus 5
Stores the per-user settings in ~/.erplibre/todo_prefs.json: which deploy
interface to use, what to display while deploying, which migration
interface. A missing or corrupt file reads as defaults.
--- FR ---
Range les réglages propres à l'utilisateur dans
~/.erplibre/todo_prefs.json : interface de déploiement, affichage pendant
le déploiement, interface de migration. Un fichier absent ou corrompu se
lit comme les valeurs par défaut.
Assisted-by: Claude Opus 4.8
Records the menus visited and shows them as a tree, a kanban or a list.
The tree is derived from the code, so a command never visited appears too,
and can be run from there.
--- FR ---
Enregistre les menus visités et les présente en arbre, en kanban ou en
liste. L'arbre est dérivé du code, si bien qu'une commande jamais visitée
y figure aussi, et peut être lancée de là.
Assisted-by: Claude Opus 4.8
Deploy, list, test, resize, delete and clean up VMs. Adds the SSH
configuration with recursive ProxyJump, port forwarding, and registration
of the QEMU hosts in virt-manager.
--- FR ---
Déployer, lister, tester, redimensionner, supprimer et nettoyer des VM.
Ajoute la configuration SSH avec ProxyJump récursif, la redirection de port
et l'enregistrement des hôtes QEMU dans virt-manager.
Assisted-by: Claude Opus 4.8
Shows every deployment setting at once, with a plan that recomputes on each
change and marks the collisions. A collapsible progress view follows.
--- FR ---
Affiche tous les réglages de déploiement d'un coup, avec un plan qui se
recalcule à chaque changement et signale les collisions. Une vue de
progression repliable suit.
Assisted-by: Claude Opus 4.8
One Textual dashboard per install run: live status, logs, host telemetry,
error counts and history. The installs run detached, so closing it does not
stop them.
--- FR ---
Un tableau de bord Textual par exécution : état en direct, logs, télémétrie
de l'hôte, comptes d'erreurs et historique. Les installations tournent
détachées : le fermer ne les arrête pas.
Assisted-by: Claude Opus 4.8
The TUI screens need Textual. Rather than naming it, the CLI offers to
install it for the running interpreter, bounded to the major the screens
are written for.
--- FR ---
Les écrans TUI ont besoin de Textual. Plutôt que de le nommer, le CLI
propose de l'installer pour l'interpréteur courant, borné à la majeure pour
laquelle les écrans sont écrits.
Assisted-by: Claude Opus 4.8
Assisted-by: Claude Opus 5
Adds a Fedora dependency installer, rewrites the Arch one on pacman rather
than an AUR helper absent from cloud images, and repairs the Debian path:
apt lock timeout, PostGIS best-effort, Ubuntu 26.04, Node 22.
--- FR ---
Ajoute un installeur de dépendances Fedora, réécrit celui d'Arch sur pacman
plutôt qu'un assistant AUR absent des images cloud, et répare le chemin
Debian : attente du verrou apt, PostGIS best-effort, Ubuntu 26.04, Node 22.
Assisted-by: Claude Opus 4.8
Assisted-by: Claude Opus 5
Deploys Ubuntu, Debian, Fedora and Arch cloud images through libvirt, on
amd64, arm64 and s390x. Handles the image download with mirror fallback,
the cloud-init seed, UEFI without Secure Boot, and the host setup.
--- FR ---
Déploie des images cloud Ubuntu, Debian, Fedora et Arch via libvirt, en
amd64, arm64 et s390x. Gère le téléchargement avec repli sur miroir, le seed
cloud-init, l'UEFI sans Secure Boot et la préparation de l'hôte.
Assisted-by: Claude Opus 4.8
Give users a guided, confirmation-gated way to drop one or all
databases from the interactive CLI. Previously this meant running
make db_drop_all or odoo_bin db --drop by hand, which is easy to
mistype and offers no safeguard. The new entry requires an explicit
'oui'/'yes' (default no) before any irreversible deletion.
Generated by Claude Code 2.1.191 claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Add a one-command installer for the ntfy push notification server
(Ubuntu/Debian and Arch Linux), wired into the todo.py Deploy menu.
Users can now deploy a local ntfy server from the CLI and subscribe
to topics from their mobile device (ntfy app) to receive push
notifications from ERPLibre.
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
« Installation avec modules extra » (--with_extra) ne clonait pas CybroOdoo
sur un env déjà installé : validate_environment renvoyait « valide » ->
update_environment (où l'état extra est posé via set_version_installed) était
sauté -> get_version_extra restait False -> git_merge_repo_manifest n'ajoutait
pas manifest/git_manifest_extra_odooXX.xml -> CybroOdoo jamais synchronisé ni
ajouté à config.conf (« Nothing to do »).
Nouvelle étape apply_extra_modules(), appelée dans main() quand --with_extra
est demandé (même si l'env de base est déjà installé) : pose l'état extra,
régénère le manifest local + repo sync (clone CybroOdoo). La post-étape qui
suit régénère config.conf, qui prend alors le nouveau chemin d'addons.
--- EN ---
« Install with extra modules » (--with_extra) did not clone CybroOdoo on an
already-installed environment: validate_environment answered « valid » ->
update_environment, where the extra state is set through
set_version_installed, was skipped -> get_version_extra stayed False ->
git_merge_repo_manifest did not add
manifest/git_manifest_extra_odooXX.xml -> CybroOdoo was never synced nor
added to config.conf (« Nothing to do »).
A new step, apply_extra_modules(), is called from main() whenever
--with_extra is requested, even on an already-installed base environment: it
sets the extra state and regenerates the local manifest plus a repo sync,
which clones CybroOdoo. The post-step that follows regenerates config.conf,
which then picks up the new addons path.
Assisted-by: Claude Opus 4.8
update_env_version.py (cible « make install_odoo_XX ») calculait un statut
mais ne faisait JAMAIS sys.exit() et IGNORAIT le retour de
update_environment() (l'install réelle : venv/poetry). Résultat : même
quand install_locally.sh échouait (ex. poetry status 127), le script
sortait 0 -> make 0 -> remote « && » continuait (service créé) ->
__ERPLIBRE_EXIT__ 0 -> le suivi d'installation affichait ✅ à tort.
- main() suit un exit_code : install_system() ou update_environment()
renvoyant une valeur non nulle (os.system : 0 = succès) -> exit_code=1.
- sys.exit(main() or 0) propage réellement l'échec.
- Étapes post-install (pycharm, git_repo_update, generate_config) exécutées
seulement si l'install a réussi (sinon .venv.erplibre absent -> cascade
d'erreurs).
Chaîne désormais complète : install_locally.sh exit 1 -> install_locally_
dev.sh exit 1 -> os.system != 0 -> install_erplibre != 0 ->
update_environment != 0 -> main exit_code=1 -> sys.exit(1) -> make échoue
-> remote && stoppe -> __ERPLIBRE_EXIT__ != 0 -> dashboard ❌.
--- EN ---
update_env_version.py, the « make install_odoo_XX » target, computed a status
but NEVER called sys.exit() and IGNORED the return of update_environment(),
the real install with its venv and poetry. As a result, even when
install_locally.sh failed — poetry status 127, say — the script exited 0 ->
make exited 0 -> the remote « && » carried on and created the service ->
__ERPLIBRE_EXIT__ was 0 -> and the install monitor wrongly showed ✅.
- main() tracks an exit_code: install_system() or update_environment()
returning non-zero — with os.system, 0 means success — sets exit_code=1.
- sys.exit(main() or 0) actually propagates the failure.
- The post-install steps (pycharm, git_repo_update, generate_config) only run
when the install succeeded; otherwise .venv.erplibre is missing and errors
cascade.
The chain is now complete: install_locally.sh exit 1 ->
install_locally_dev.sh exit 1 -> os.system != 0 -> install_erplibre != 0 ->
update_environment != 0 -> main exit_code=1 -> sys.exit(1) -> make fails ->
the remote && stops -> __ERPLIBRE_EXIT__ != 0 -> dashboard ❌.
Assisted-by: Claude Opus 4.8
extraire_svg_graphique_by_3d annote « -> Optional[str] » mais le module
n'importait pas Optional -> « NameError: name 'Optional' is not defined »
au chargement. Ajout de « from typing import Optional ».
--- EN ---
extraire_svg_graphique_by_3d is annotated « -> Optional[str] » but the module
never imported Optional -> « NameError: name 'Optional' is not defined » at
import time. Added « from typing import Optional ».
Assisted-by: Claude Opus 4.8
webdriver.Firefox échouait : « InvalidArgumentException: Argument
--remote-allow-system-access can't be set via capabilities ». Cet argument
(ajouté pour le Firefox snap d'Ubuntu) est désormais REFUSÉ via les
capabilities par les geckodriver récents, qui gèrent seuls l'accès système
du snap. Il cassait donc TOUS les setups sur geckodriver récent (ici Arch
sans snap). On ne le passe plus.
--- EN ---
webdriver.Firefox was failing with « InvalidArgumentException: Argument
--remote-allow-system-access can't be set via capabilities ». That argument,
added for Ubuntu's snap Firefox, is now REFUSED through capabilities by
recent geckodriver versions, which handle the snap's system access on their
own. It therefore broke EVERY setup running a recent geckodriver — here Arch,
with no snap at all. It is no longer passed.
Assisted-by: Claude Opus 4.8
Installation was very noisy: poetry ran "install -vvv" and repo sync /
git daemon ran with -v/--verbose. They are now quiet by default
(poetry -q, repo sync -q, no git daemon --verbose) and the detailed
logs come back only when EL_VERBOSE=1.
Applied to install_locally.sh (poetry) and every manifest script (repo
sync + git daemon). env_var.sh documents EL_VERBOSE and respects a value
already set in the environment, so "EL_VERBOSE=1 make install_odoo_18"
works.
--- FR ---
L'installation était très bavarde : poetry tournait en « install -vvv », et
repo sync comme git daemon en -v/--verbose. Ils sont désormais silencieux par
défaut — poetry -q, repo sync -q, plus de git daemon --verbose — et les
journaux détaillés ne reviennent qu'avec EL_VERBOSE=1.
Appliqué à install_locally.sh (poetry) et à tous les scripts de manifest
(repo sync et git daemon). env_var.sh documente EL_VERBOSE et respecte une
valeur déjà passée dans l'environnement, si bien que « EL_VERBOSE=1 make
install_odoo_18 » fonctionne.
Assisted-by: Claude Opus 4.8