From 7d001ff163bb44dec12bdc8387d6aa674df3daf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Tue, 13 Jan 2026 15:08:11 +0100 Subject: [PATCH] [IMP] adds pythons script to clean obsolete addons --- finalize_db.sh | 5 + post_migration_cleanup_obsolete_modules.py | 128 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 post_migration_cleanup_obsolete_modules.py diff --git a/finalize_db.sh b/finalize_db.sh index bb8e81e..3a713f3 100755 --- a/finalize_db.sh +++ b/finalize_db.sh @@ -16,6 +16,11 @@ PYTHON_SCRIPT=post_migration_fix_duplicated_views.py echo "Remove duplicated views with script $PYTHON_SCRIPT ..." exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1 +# Uninstall obsolette add-ons +PYTHON_SCRIPT=post_migration_cleanup_obsolete_modules.py +echo "Uninstall obsolete add-ons with script $PYTHON_SCRIPT ..." +exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1 + # Give back the right to user to access to the tables # docker exec -u 70 "$DB_CONTAINER_NAME" pgm chown "$FINALE_SERVICE_NAME" "$DB_NAME" diff --git a/post_migration_cleanup_obsolete_modules.py b/post_migration_cleanup_obsolete_modules.py new file mode 100644 index 0000000..d3ca08e --- /dev/null +++ b/post_migration_cleanup_obsolete_modules.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +""" +Post-Migration Obsolete Module Cleanup +Run this AFTER migration to detect and remove modules that exist in the database +but no longer exist in the filesystem (addons paths). +""" + +print("\n" + "="*80) +print("POST-MIGRATION OBSOLETE MODULE CLEANUP") +print("="*80 + "\n") + +import odoo.modules.module as module_lib + +# Get all modules from database +all_modules = env['ir.module.module'].search([]) + +print(f"Analyzing {len(all_modules)} modules in database...\n") + +# Detect obsolete modules (in database but not in filesystem) +obsolete_modules = [] +for mod in all_modules: + mod_path = module_lib.get_module_path(mod.name, display_warning=False) + if not mod_path: + obsolete_modules.append(mod) + +if not obsolete_modules: + print("✓ No obsolete modules found! Database is clean.") + print("=" * 80 + "\n") + exit() + +# Separate modules by state +safe_to_delete = [m for m in obsolete_modules if m.state != 'installed'] +installed_obsolete = [m for m in obsolete_modules if m.state == 'installed'] + +# Display obsolete modules +print(f"Obsolete modules found: {len(obsolete_modules)}\n") + +if installed_obsolete: + print("-" * 80) + print("⚠️ OBSOLETE INSTALLED MODULES (require attention)") + print("-" * 80) + for mod in sorted(installed_obsolete, key=lambda m: m.name): + print(f" • {mod.name:40} | ID: {mod.id}") + print() + +if safe_to_delete: + print("-" * 80) + print("OBSOLETE UNINSTALLED MODULES (safe to delete)") + print("-" * 80) + for mod in sorted(safe_to_delete, key=lambda m: m.name): + print(f" • {mod.name:40} | State: {mod.state:15} | ID: {mod.id}") + print() + +# Summary +print("=" * 80) +print("SUMMARY") +print("=" * 80 + "\n") +print(f" • Obsolete uninstalled modules (safe to delete): {len(safe_to_delete)}") +print(f" • Obsolete INSTALLED modules (caution!): {len(installed_obsolete)}") + +# Delete uninstalled modules +if safe_to_delete: + print("\n" + "=" * 80) + print("DELETING OBSOLETE UNINSTALLED MODULES") + print("=" * 80 + "\n") + + deleted_count = 0 + failed_deletes = [] + + for mod in safe_to_delete: + try: + mod_name = mod.name + mod_id = mod.id + mod.unlink() + print(f"✓ Deleted: {mod_name} (ID: {mod_id})") + deleted_count += 1 + except Exception as e: + print(f"✗ Failed: {mod.name} - {e}") + failed_deletes.append({'name': mod.name, 'id': mod.id, 'reason': str(e)}) + + # Commit changes + print("\n" + "=" * 80) + print("COMMITTING CHANGES") + print("=" * 80 + "\n") + + try: + env.cr.commit() + print("✓ All changes committed successfully!") + except Exception as e: + print(f"✗ Commit failed: {e}") + print("Changes were NOT saved!") + exit(1) + + # Final result + print("\n" + "=" * 80) + print("RESULT") + print("=" * 80 + "\n") + print(f" • Successfully deleted modules: {deleted_count}") + print(f" • Failed deletions: {len(failed_deletes)}") + + if failed_deletes: + print("\n⚠️ Modules not deleted:") + for item in failed_deletes: + print(f" • {item['name']} (ID: {item['id']}): {item['reason']}") + +if installed_obsolete: + print("\n" + "=" * 80) + print("⚠️ WARNING: OBSOLETE INSTALLED MODULES") + print("=" * 80 + "\n") + print("The following modules are marked 'installed' but no longer exist") + print("in the filesystem. They may cause problems.\n") + print("Options:") + print(" 1. Check if these modules were renamed/merged in the new version") + print(" 2. Manually uninstall them if possible") + print(" 3. Force delete them (risky, may break dependencies)\n") + + for mod in sorted(installed_obsolete, key=lambda m: m.name): + # Find modules that depend on this module + dependents = env['ir.module.module'].search([ + ('state', '=', 'installed'), + ('dependencies_id.name', '=', mod.name) + ]) + dep_info = f" <- Dependents: {dependents.mapped('name')}" if dependents else "" + print(f" • {mod.name}{dep_info}") + +print("\n" + "=" * 80) +print("CLEANUP COMPLETED!") +print("=" * 80 + "\n")