[FIX] migration: rebâtir le clone annule aussi sa préparation
Quand OpenUpgrade échoue, le pilote remet le drapeau de clonage à zéro pour que la base intermédiaire soit refaite depuis la version d'avant. Mais les étapes qui avaient préparé CE clone gardaient le leur : le SQL de pré-migration, les désinstallations, les installations. La base neuve repartait sans sa préparation, et OpenUpgrade retombait sur le problème même que ce SQL existe pour écarter. Vu sur test_neutralize_upgrade_18 : clone à refaire, et pourtant fix_migration_odoo170_to_odoo180.sql déjà consigné comme appliqué. Les trois drapeaux tombent maintenant avec le clone. --- EN --- When OpenUpgrade fails, the driver clears the clone flag so the intermediate database is rebuilt from the previous version. But the steps that had prepared THAT clone kept theirs: the pre-migration SQL, the uninstalls, the installs. The fresh database started without its preparation, and OpenUpgrade met the very problem that SQL exists to prevent. Seen on test_neutralize_upgrade_18: clone pending, yet fix_migration_odoo170_to_odoo180.sql already recorded as applied. The three flags now fall with the clone. Assisted-by: Claude Opus 5
This commit is contained in:
parent
24f38c79ad
commit
e3b1fd48be
2 changed files with 89 additions and 0 deletions
|
|
@ -2665,6 +2665,24 @@ class TodoUpgrade:
|
|||
self.dct_progression["state_4_clone_odoo_lst"] = (
|
||||
lst_clone_odoo
|
||||
)
|
||||
# Tout ce qui a été fait À ce clone meurt avec lui : la
|
||||
# base reconstruite est une copie neuve de la version
|
||||
# précédente et réclame la même préparation. Laisser ces
|
||||
# drapeaux debout sautait le SQL de pré-migration sur le
|
||||
# clone neuf, et OpenUpgrade retombait sur le problème
|
||||
# même que ce SQL existe pour écarter.
|
||||
lst_fix_migration_odoo[index] = []
|
||||
self.dct_progression["state_4_fix_migration_odoo_lst"] = (
|
||||
lst_fix_migration_odoo
|
||||
)
|
||||
lst_module_uninstall_module[index] = False
|
||||
self.dct_progression["state_4_uninstall_module"] = (
|
||||
lst_module_uninstall_module
|
||||
)
|
||||
lst_module_install_module[index] = False
|
||||
self.dct_progression["state_4_install_module"] = (
|
||||
lst_module_install_module
|
||||
)
|
||||
self.write_config()
|
||||
print(
|
||||
f"\n❌ -> {t('Database migration to Odoo')}"
|
||||
|
|
|
|||
|
|
@ -278,5 +278,76 @@ class TestAFailedOpenUpgradeStaysUnrecorded(unittest.TestCase):
|
|||
self.assertEqual(len(self.lignes_affectation()), 1)
|
||||
|
||||
|
||||
class TestDiscardingTheCloneDiscardsItsPreparation(unittest.TestCase):
|
||||
"""Rebâtir le clone annule tout ce qu'on lui avait fait.
|
||||
|
||||
Quand OpenUpgrade échoue, le pilote remet le drapeau de clonage à zéro
|
||||
pour que la base intermédiaire soit refaite depuis la version
|
||||
précédente. Mais les drapeaux des étapes qui avaient préparé CE
|
||||
clone — le SQL de pré-migration, les désinstallations, les
|
||||
installations — restaient debout. La base neuve repartait donc sans
|
||||
sa préparation, et OpenUpgrade retombait sur le problème même que le
|
||||
SQL existe pour écarter.
|
||||
"""
|
||||
|
||||
PAR_CLONE = (
|
||||
"lst_fix_migration_odoo",
|
||||
"lst_module_uninstall_module",
|
||||
"lst_module_install_module",
|
||||
)
|
||||
|
||||
def bloc_abandon(self):
|
||||
with io.open(SOURCE, encoding="utf-8") as handle:
|
||||
arbre = ast.parse(handle.read())
|
||||
for noeud in ast.walk(arbre):
|
||||
if isinstance(
|
||||
noeud, ast.If
|
||||
) and TestAFailedOpenUpgradeStaysUnrecorded._remet_le_clone_a_zero(
|
||||
noeud
|
||||
):
|
||||
return noeud
|
||||
return None
|
||||
|
||||
def remis_a_zero(self):
|
||||
"""Les listes que ce bloc remet à leur valeur vide."""
|
||||
noms = set()
|
||||
for petit in ast.walk(self.bloc_abandon()):
|
||||
if not isinstance(petit, ast.Assign):
|
||||
continue
|
||||
if not (
|
||||
isinstance(petit.value, ast.Constant)
|
||||
and petit.value.value is False
|
||||
) and not (
|
||||
isinstance(petit.value, ast.List) and not petit.value.elts
|
||||
):
|
||||
continue
|
||||
for cible in petit.targets:
|
||||
if isinstance(cible, ast.Subscript) and isinstance(
|
||||
cible.value, ast.Name
|
||||
):
|
||||
noms.add(cible.value.id)
|
||||
return noms
|
||||
|
||||
def test_the_failure_block_is_found(self):
|
||||
self.assertIsNotNone(self.bloc_abandon())
|
||||
|
||||
def test_every_per_clone_flag_is_reset(self):
|
||||
remis = self.remis_a_zero()
|
||||
for nom in self.PAR_CLONE:
|
||||
self.assertIn(nom, remis, f"{nom} survit à son clone")
|
||||
|
||||
def test_each_reset_is_persisted(self):
|
||||
# Remettre la liste à zéro sans l'écrire ne survit pas au
|
||||
# processus : c'est la reprise qui relit le fichier.
|
||||
corps = ast.dump(self.bloc_abandon())
|
||||
for cle in (
|
||||
"state_4_fix_migration_odoo_lst",
|
||||
"state_4_uninstall_module",
|
||||
"state_4_install_module",
|
||||
):
|
||||
self.assertIn(cle, corps)
|
||||
self.assertIn("write_config", corps)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in a new issue