[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
|
|
"""Full-screen browsing of the at-risk website COW copies.
|
|
|
|
|
|
|
|
|
|
|
|
Read-only, without exception: this screen exists to decide, and deciding
|
|
|
|
|
|
supposes having looked. Neutralizing stays a separate, explicit command.
|
|
|
|
|
|
|
|
|
|
|
|
Two views of the same copy, one key apart
|
|
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
« What do I lose » and « why does it break » are different questions with
|
|
|
|
|
|
different answers, and putting them side by side would halve the width of
|
|
|
|
|
|
each on a screen that already shows XML. They share the pane instead, and
|
|
|
|
|
|
``space`` switches — the header always says which one is showing, because a
|
|
|
|
|
|
diff and a declaration look alike at a glance.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
2026-08-13 04:21:24 -04:00
|
|
|
|
sys.path.append(
|
|
|
|
|
|
os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
from script.todo.todo_i18n import t
|
|
|
|
|
|
except Exception: # pragma: no cover - repli si i18n indisponible
|
|
|
|
|
|
|
|
|
|
|
|
def t(key: str) -> str:
|
|
|
|
|
|
return key
|
|
|
|
|
|
|
[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
|
|
|
|
|
|
CSS = """
|
|
|
|
|
|
Screen { layout: vertical; }
|
|
|
|
|
|
#head { height: 3; padding: 0 1; background: $panel; color: $text; }
|
|
|
|
|
|
#body { height: 1fr; }
|
|
|
|
|
|
#views { width: 38; border-right: solid $accent; }
|
|
|
|
|
|
#pane { width: 1fr; padding: 0 1; }
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_app(lst_finding):
|
|
|
|
|
|
"""Build the application. Textual is imported here, not at module level.
|
|
|
|
|
|
|
|
|
|
|
|
The module stays importable — and therefore testable — on a machine
|
|
|
|
|
|
without Textual, which is also what lets the caller fall back to the text
|
|
|
|
|
|
report rather than fail.
|
|
|
|
|
|
"""
|
|
|
|
|
|
from textual.app import App, ComposeResult
|
|
|
|
|
|
from textual.containers import Horizontal, VerticalScroll
|
|
|
|
|
|
from textual.widgets import DataTable, Footer, Header, Static
|
|
|
|
|
|
|
|
|
|
|
|
from cow_drift import render_diff, render_shape
|
|
|
|
|
|
|
|
|
|
|
|
class DriftApp(App):
|
|
|
|
|
|
CSS = globals()["CSS"]
|
|
|
|
|
|
BINDINGS = [
|
|
|
|
|
|
("q,escape", "quit", "Quit"),
|
|
|
|
|
|
("space,tab", "toggle", "Diff / declarations"),
|
|
|
|
|
|
("c", "copy", "Copy"),
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, lst_finding):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
self.lst_finding = lst_finding
|
|
|
|
|
|
self.shape = False
|
|
|
|
|
|
self.index = 0
|
|
|
|
|
|
|
|
|
|
|
|
def compose(self) -> ComposeResult:
|
|
|
|
|
|
yield Header()
|
|
|
|
|
|
yield Static("", id="head")
|
|
|
|
|
|
with Horizontal(id="body"):
|
|
|
|
|
|
yield DataTable(id="views", cursor_type="row")
|
|
|
|
|
|
with VerticalScroll(id="pane"):
|
|
|
|
|
|
yield Static("", id="content")
|
|
|
|
|
|
yield Footer()
|
|
|
|
|
|
|
|
|
|
|
|
def on_mount(self):
|
|
|
|
|
|
self.title = "Website COW copies at risk"
|
|
|
|
|
|
table = self.query_one("#views", DataTable)
|
|
|
|
|
|
table.add_columns("copy", "+/-")
|
|
|
|
|
|
for finding in self.lst_finding:
|
|
|
|
|
|
table.add_row(
|
|
|
|
|
|
(finding["key"] or str(finding["id"]))[:28],
|
|
|
|
|
|
self._weight(finding),
|
|
|
|
|
|
key=str(finding["id"]),
|
|
|
|
|
|
)
|
|
|
|
|
|
self._show()
|
|
|
|
|
|
|
|
|
|
|
|
def _weight(self, finding):
|
|
|
|
|
|
"""How much the copy diverges, so the list can be triaged."""
|
|
|
|
|
|
if finding["module_id"] is None:
|
|
|
|
|
|
return "—"
|
|
|
|
|
|
import difflib
|
|
|
|
|
|
|
|
|
|
|
|
diff = list(
|
|
|
|
|
|
difflib.unified_diff(
|
|
|
|
|
|
finding["module_arch"].splitlines(),
|
|
|
|
|
|
finding["copy_arch"].splitlines(),
|
|
|
|
|
|
lineterm="",
|
|
|
|
|
|
n=0,
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
plus = sum(
|
|
|
|
|
|
1
|
|
|
|
|
|
for x in diff
|
|
|
|
|
|
if x.startswith("+") and not x.startswith("+++")
|
|
|
|
|
|
)
|
|
|
|
|
|
minus = sum(
|
|
|
|
|
|
1
|
|
|
|
|
|
for x in diff
|
|
|
|
|
|
if x.startswith("-") and not x.startswith("---")
|
|
|
|
|
|
)
|
|
|
|
|
|
return f"+{plus}/-{minus}"
|
|
|
|
|
|
|
|
|
|
|
|
def _show(self):
|
|
|
|
|
|
if not self.lst_finding:
|
|
|
|
|
|
return
|
|
|
|
|
|
finding = self.lst_finding[self.index]
|
|
|
|
|
|
which = "declarations" if self.shape else "what the copy changed"
|
|
|
|
|
|
self.query_one("#head", Static).update(
|
|
|
|
|
|
f"{finding['key']} · id={finding['id']}"
|
|
|
|
|
|
f" · website={finding['website_id']}\n"
|
|
|
|
|
|
f"[{which}] — space to switch"
|
|
|
|
|
|
)
|
|
|
|
|
|
render = render_shape if self.shape else render_diff
|
|
|
|
|
|
self.query_one("#content", Static).update(render(finding))
|
|
|
|
|
|
|
|
|
|
|
|
def on_data_table_row_highlighted(self, event):
|
|
|
|
|
|
if event.data_table.id == "views" and self.lst_finding:
|
|
|
|
|
|
self.index = event.cursor_row
|
|
|
|
|
|
self._show()
|
|
|
|
|
|
|
|
|
|
|
|
def action_toggle(self):
|
|
|
|
|
|
self.shape = not self.shape
|
|
|
|
|
|
self._show()
|
|
|
|
|
|
|
|
|
|
|
|
def action_copy(self):
|
|
|
|
|
|
if not self.lst_finding:
|
|
|
|
|
|
return
|
|
|
|
|
|
from cow_drift import render_diff, render_shape
|
|
|
|
|
|
|
|
|
|
|
|
render = render_shape if self.shape else render_diff
|
|
|
|
|
|
# Truncated keeping the END: that is where the added lines are,
|
|
|
|
|
|
# and what someone is most often after.
|
|
|
|
|
|
self.copy_to_clipboard(
|
|
|
|
|
|
render(self.lst_finding[self.index])[-100_000:]
|
|
|
|
|
|
)
|
|
|
|
|
|
self.notify("Copied.")
|
|
|
|
|
|
|
|
|
|
|
|
return DriftApp(lst_finding)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_tui(lst_finding, run_app=True):
|
2026-08-13 04:21:24 -04:00
|
|
|
|
"""Ouvrir l'écran. False si on n'a pas pu — et alors on DIT pourquoi.
|
|
|
|
|
|
|
|
|
|
|
|
Trois refus, trois raisons, aucune n'est une panne : rien à montrer, pas
|
|
|
|
|
|
de terminal, ou Textual absent. Se taire faisait réafficher le rapport
|
|
|
|
|
|
texte à la place de l'écran demandé — la même sortie que la touche
|
|
|
|
|
|
précédente, sans rien qui distingue les deux.
|
[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
|
2026-08-13 04:21:24 -04:00
|
|
|
|
Textual n'est installé que dans `.venv.erplibre`. Lancer ce script
|
|
|
|
|
|
directement, maintenant qu'il porte le bit d'exécution, prend le python
|
|
|
|
|
|
du système : le shebang ne connaît pas le venv. C'est le chemin qui rend
|
|
|
|
|
|
ce message nécessaire.
|
[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
"""
|
|
|
|
|
|
if not lst_finding:
|
|
|
|
|
|
return False
|
|
|
|
|
|
if not sys.stdout.isatty():
|
2026-08-13 04:21:24 -04:00
|
|
|
|
print(f"ℹ️ {t('Not a terminal: showing the text report instead.')}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
try:
|
|
|
|
|
|
from script.todo import textual_setup
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
textual_setup = None
|
|
|
|
|
|
if textual_setup and not textual_setup.available():
|
|
|
|
|
|
# Le conseil AVANT la proposition d'installer : Textual est déjà là,
|
|
|
|
|
|
# dans le venv. Proposer de l'installer une seconde fois sous
|
|
|
|
|
|
# « --user » répond à côté de la question.
|
|
|
|
|
|
if not textual_setup.in_venv():
|
|
|
|
|
|
print(
|
|
|
|
|
|
f"ℹ️ {t('Textual is missing from this interpreter:')}"
|
|
|
|
|
|
f" {sys.executable}"
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f" {t('Run it with')} .venv.erplibre/bin/python3")
|
|
|
|
|
|
if textual_setup and not textual_setup.ensure():
|
[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
return False
|
|
|
|
|
|
try:
|
|
|
|
|
|
app = build_app(lst_finding)
|
|
|
|
|
|
except ImportError:
|
2026-08-13 04:21:24 -04:00
|
|
|
|
print(
|
|
|
|
|
|
f"ℹ️ {t('Textual is missing from this interpreter:')}"
|
|
|
|
|
|
f" {sys.executable}"
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f" {t('Run it with')} .venv.erplibre/bin/python3")
|
[ADD] migration: look at a COW copy before agreeing to give it up
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
2026-08-11 23:49:05 -04:00
|
|
|
|
return False
|
|
|
|
|
|
if not run_app:
|
|
|
|
|
|
return app
|
|
|
|
|
|
app.run()
|
|
|
|
|
|
return True
|