[IMP] migration: per-database uninstall list, with a stated reason
Which modules must be uninstalled before a version bump depends on the data of one specific database, so that list does not belong to a shared versioned file. - Read private/odoo/migration/<database>/uninstall_module_list_odooXX0_to_odooYY0.txt first, then the shared versioned defaults under script/odoo/migration/, and merge them (duplicates dropped). private/ mirrors script/, the convention already used by script/todo/todo.json -> private/todo/todo_override.json. - Fix the parser. It was « f.readline().split() »: only the FIRST line was kept, so a multi-line list was silently truncated, and a comma-separated list turned into one bogus module name. On a file starting with a comment it returned the words of that comment as module names. It now reads every line and accepts commas, several names per line, blank lines and comments. - Support a « # reason » justification per module, printed before uninstalling; a module with no reason is flagged. Removing a module must stay reviewable. - Ignore private/odoo/ in git: these lists describe one database. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab9f94cc28
commit
4536c5bc40
5 changed files with 167 additions and 12 deletions
5
private/.gitignore
vendored
5
private/.gitignore
vendored
|
|
@ -1 +1,6 @@
|
||||||
*.kdbx
|
*.kdbx
|
||||||
|
|
||||||
|
# Per-database migration lists (modules to uninstall/install before a version
|
||||||
|
# bump). They describe one specific database, never a shared default, so they
|
||||||
|
# must not be versioned. Shared defaults belong to script/odoo/migration/.
|
||||||
|
odoo/
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,46 @@ source ./.venv.odoo15.0_python3.8.20/bin/activate && cat ./script/odoo/migration
|
||||||
<!-- [en] -->
|
<!-- [en] -->
|
||||||
Check [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
Check [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
||||||
|
|
||||||
|
## Module lists to uninstall
|
||||||
|
|
||||||
|
Before a version bump, the migration uninstalls the modules listed in
|
||||||
|
`uninstall_module_list_odoo<from>_to_odoo<to>.txt`. Two locations are read, the
|
||||||
|
private one first, and the results are merged (duplicates dropped):
|
||||||
|
|
||||||
|
1. `private/odoo/migration/<database>/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— specific to ONE database, not versioned. Which modules must be dropped
|
||||||
|
depends on the data, so this is where nearly every entry belongs.
|
||||||
|
2. `script/odoo/migration/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— shared defaults, versioned, valid for every database.
|
||||||
|
|
||||||
|
Syntax: one module per line, with a justification after `#`. Commas and several
|
||||||
|
names per line are accepted; blank lines and full-line comments are ignored.
|
||||||
|
A module without a stated reason is flagged at runtime: removing a module is a
|
||||||
|
decision someone must be able to review later.
|
||||||
|
|
||||||
<!-- [fr] -->
|
<!-- [fr] -->
|
||||||
Consultez [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
Consultez [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
||||||
|
|
||||||
|
## Listes de modules à désinstaller
|
||||||
|
|
||||||
|
Avant une montée de version, la migration désinstalle les modules listés dans
|
||||||
|
`uninstall_module_list_odoo<depuis>_to_odoo<vers>.txt`. Deux emplacements sont
|
||||||
|
lus, le privé d'abord, puis fusionnés (doublons éliminés) :
|
||||||
|
|
||||||
|
1. `private/odoo/migration/<base>/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— propre à UNE base de données, non versionné. Les modules à supprimer
|
||||||
|
dépendent des données : c'est ici que va la quasi-totalité des entrées.
|
||||||
|
2. `script/odoo/migration/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— valeurs par défaut partagées, versionnées, valables pour toute base.
|
||||||
|
|
||||||
|
Syntaxe : un module par ligne, avec une justification après `#`. Les virgules et
|
||||||
|
plusieurs noms par ligne sont acceptés ; lignes vides et commentaires pleine
|
||||||
|
ligne sont ignorés. Un module sans raison est signalé à l'exécution : supprimer
|
||||||
|
un module est une décision qui doit pouvoir être relue plus tard.
|
||||||
|
|
||||||
|
<!-- [common] -->
|
||||||
|
```
|
||||||
|
queue_job # blocks 12->13, trigger queue_job_notify
|
||||||
|
mgmtsystem_hazard # not ported to 13.0
|
||||||
|
web_syncer # dropped upstream
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,26 @@ source ./.venv.odoo15.0_python3.8.20/bin/activate && cat ./script/odoo/migration
|
||||||
```
|
```
|
||||||
|
|
||||||
Consultez [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
Consultez [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
||||||
|
|
||||||
|
## Listes de modules à désinstaller
|
||||||
|
|
||||||
|
Avant une montée de version, la migration désinstalle les modules listés dans
|
||||||
|
`uninstall_module_list_odoo<depuis>_to_odoo<vers>.txt`. Deux emplacements sont
|
||||||
|
lus, le privé d'abord, puis fusionnés (doublons éliminés) :
|
||||||
|
|
||||||
|
1. `private/odoo/migration/<base>/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— propre à UNE base de données, non versionné. Les modules à supprimer
|
||||||
|
dépendent des données : c'est ici que va la quasi-totalité des entrées.
|
||||||
|
2. `script/odoo/migration/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— valeurs par défaut partagées, versionnées, valables pour toute base.
|
||||||
|
|
||||||
|
Syntaxe : un module par ligne, avec une justification après `#`. Les virgules et
|
||||||
|
plusieurs noms par ligne sont acceptés ; lignes vides et commentaires pleine
|
||||||
|
ligne sont ignorés. Un module sans raison est signalé à l'exécution : supprimer
|
||||||
|
un module est une décision qui doit pouvoir être relue plus tard.
|
||||||
|
|
||||||
|
```
|
||||||
|
queue_job # blocks 12->13, trigger queue_job_notify
|
||||||
|
mgmtsystem_hazard # not ported to 13.0
|
||||||
|
web_syncer # dropped upstream
|
||||||
|
```
|
||||||
|
|
@ -8,3 +8,26 @@ source ./.venv.odoo15.0_python3.8.20/bin/activate && cat ./script/odoo/migration
|
||||||
```
|
```
|
||||||
|
|
||||||
Check [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
Check [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt)
|
||||||
|
|
||||||
|
## Module lists to uninstall
|
||||||
|
|
||||||
|
Before a version bump, the migration uninstalls the modules listed in
|
||||||
|
`uninstall_module_list_odoo<from>_to_odoo<to>.txt`. Two locations are read, the
|
||||||
|
private one first, and the results are merged (duplicates dropped):
|
||||||
|
|
||||||
|
1. `private/odoo/migration/<database>/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— specific to ONE database, not versioned. Which modules must be dropped
|
||||||
|
depends on the data, so this is where nearly every entry belongs.
|
||||||
|
2. `script/odoo/migration/uninstall_module_list_odooXX0_to_odooYY0.txt`
|
||||||
|
— shared defaults, versioned, valid for every database.
|
||||||
|
|
||||||
|
Syntax: one module per line, with a justification after `#`. Commas and several
|
||||||
|
names per line are accepted; blank lines and full-line comments are ignored.
|
||||||
|
A module without a stated reason is flagged at runtime: removing a module is a
|
||||||
|
decision someone must be able to review later.
|
||||||
|
|
||||||
|
```
|
||||||
|
queue_job # blocks 12->13, trigger queue_job_notify
|
||||||
|
mgmtsystem_hazard # not ported to 13.0
|
||||||
|
web_syncer # dropped upstream
|
||||||
|
```
|
||||||
|
|
@ -44,6 +44,12 @@ FILENAME_ODOO_VERSION = ".odoo-version"
|
||||||
LOCAL_MANIFEST = os.path.join(
|
LOCAL_MANIFEST = os.path.join(
|
||||||
".repo", "local_manifests", "erplibre_manifest.xml"
|
".repo", "local_manifests", "erplibre_manifest.xml"
|
||||||
)
|
)
|
||||||
|
# Module lists for a version bump. The shared, versioned defaults live under
|
||||||
|
# script/; the per-database lists live under private/ (mirroring script/, like
|
||||||
|
# script/todo/todo.json -> private/todo/todo_override.json). Which modules must
|
||||||
|
# be dropped depends on the database, so that choice is never versioned.
|
||||||
|
PATH_MIGRATION_GLOBAL = os.path.join("script", "odoo", "migration")
|
||||||
|
PATH_MIGRATION_PRIVATE = os.path.join("private", "odoo", "migration")
|
||||||
|
|
||||||
|
|
||||||
class TodoUpgrade:
|
class TodoUpgrade:
|
||||||
|
|
@ -828,18 +834,20 @@ class TodoUpgrade:
|
||||||
)
|
)
|
||||||
|
|
||||||
if not is_state_4_reach_open_upgrade:
|
if not is_state_4_reach_open_upgrade:
|
||||||
lst_module_to_uninstall = []
|
lst_module_to_uninstall, lst_uninstall_reason = (
|
||||||
uninstall_module_list_file = os.path.join(
|
self.read_uninstall_module_list(start_version, database_name)
|
||||||
"script",
|
|
||||||
"odoo",
|
|
||||||
"migration",
|
|
||||||
f"uninstall_module_list_odoo{start_version * 10}_to_odoo{(start_version + 1) * 10}.txt",
|
|
||||||
)
|
)
|
||||||
if os.path.exists(uninstall_module_list_file):
|
if lst_uninstall_reason:
|
||||||
with open(uninstall_module_list_file, "r") as f:
|
# Show WHY each module goes away: which modules must be dropped
|
||||||
lst_module_to_uninstall = [
|
# depends on the database, and a removal without a stated reason
|
||||||
a.strip() for a in f.readline().split()
|
# is a decision nobody can review later.
|
||||||
]
|
print("✨ Modules to uninstall before migration :")
|
||||||
|
for name, reason, origin in lst_uninstall_reason:
|
||||||
|
print(
|
||||||
|
f" - {name}"
|
||||||
|
+ (f" — {reason}" if reason else " — ⚠️ no reason given")
|
||||||
|
+ f" [{origin}]"
|
||||||
|
)
|
||||||
|
|
||||||
if config_state_1_uninstall_module:
|
if config_state_1_uninstall_module:
|
||||||
lst_module_to_uninstall = (
|
lst_module_to_uninstall = (
|
||||||
|
|
@ -1802,6 +1810,61 @@ class TodoUpgrade:
|
||||||
}
|
}
|
||||||
return dct_module
|
return dct_module
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_module_list_file(file_path):
|
||||||
|
"""Read a module list file, return [(module, reason), ...].
|
||||||
|
|
||||||
|
Accepted syntax, one module per line with an optional justification:
|
||||||
|
|
||||||
|
queue_job # blocks 12->13, trigger queue_job_notify
|
||||||
|
mgmtsystem_hazard # not ported to 13.0
|
||||||
|
|
||||||
|
Commas and several names per line are also accepted, so a list copied
|
||||||
|
from a command line works as-is. Blank lines and full-line comments are
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
The previous parser was « f.readline().split() »: it kept only the FIRST
|
||||||
|
line, so a multi-line list was silently truncated, and a comma-separated
|
||||||
|
list collapsed into one bogus module name.
|
||||||
|
"""
|
||||||
|
lst_module = []
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
content, _, reason = line.partition("#")
|
||||||
|
for module_name in content.replace(",", " ").split():
|
||||||
|
lst_module.append((module_name, reason.strip()))
|
||||||
|
return lst_module
|
||||||
|
|
||||||
|
def read_uninstall_module_list(self, start_version, database_name):
|
||||||
|
"""Modules to uninstall before migrating start_version -> next.
|
||||||
|
|
||||||
|
Reads the per-database private list first, then the shared versioned
|
||||||
|
defaults; duplicates are dropped, keeping the first occurrence.
|
||||||
|
|
||||||
|
Returns (lst_module, lst_detail) where lst_detail carries
|
||||||
|
(module, reason, origin_file) so the caller can justify each removal.
|
||||||
|
"""
|
||||||
|
file_name = (
|
||||||
|
f"uninstall_module_list_odoo{start_version * 10}"
|
||||||
|
f"_to_odoo{(start_version + 1) * 10}.txt"
|
||||||
|
)
|
||||||
|
lst_path = [
|
||||||
|
os.path.join(PATH_MIGRATION_PRIVATE, database_name, file_name),
|
||||||
|
os.path.join(PATH_MIGRATION_GLOBAL, file_name),
|
||||||
|
]
|
||||||
|
|
||||||
|
lst_module = []
|
||||||
|
lst_detail = []
|
||||||
|
for file_path in lst_path:
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
continue
|
||||||
|
for module_name, reason in self.parse_module_list_file(file_path):
|
||||||
|
if module_name in lst_module:
|
||||||
|
continue
|
||||||
|
lst_module.append(module_name)
|
||||||
|
lst_detail.append((module_name, reason, file_path))
|
||||||
|
return lst_module, lst_detail
|
||||||
|
|
||||||
def uninstall_from_database(
|
def uninstall_from_database(
|
||||||
self, lst_module_to_uninstall, database_name, actual_version
|
self, lst_module_to_uninstall, database_name, actual_version
|
||||||
):
|
):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue