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
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
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
Choosing « remote » as the migration source raised AttributeError: the method
had moved to DatabaseManager, and todo_upgrade still called it on the TODO
object. The remote path was therefore unusable — the only way to start a
migration from a production backup rather than a local zip.
--- FR ---
Choisir « remote » comme source de migration levait une AttributeError : la
méthode avait été déplacée vers DatabaseManager, et todo_upgrade l'appelait
toujours sur l'objet TODO. Le chemin distant était donc inutilisable — le seul
moyen de démarrer une migration depuis une sauvegarde de production plutôt que
depuis un zip local.
Assisted-by: Claude Opus 5
Reflect the current year in all TechnoLibre
license headers across script/, test/, and docker/.
Generated by Claude Code 2.1.74 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
- add example odoo test
- prevent delete production file with validation
- add makefile with selenium
- script prod to dev uninstall module after installation
- adapt todo with private directory
- script to download remote database
- TODO show documentation for migration