import os import sys import argparse import subprocess import configparser import psycopg2 def get_custom_addons_modules(addons_path): """ Scans the given addons path and returns a list of directory names that contain a __manifest__.py file. """ modules = [] if not os.path.exists(addons_path): print(f"Error: Addons path '{addons_path}' does not exist.") return [] for item in os.listdir(addons_path): item_path = os.path.join(addons_path, item) if os.path.isdir(item_path): manifest_path = os.path.join(item_path, '__manifest__.py') if os.path.exists(manifest_path): modules.append(item) return modules def get_db_params(config_path): """ Parses the Odoo config file to extract database connection parameters. """ config = configparser.ConfigParser() try: config.read(config_path) except Exception as e: print(f"Error reading config file: {e}") return {} db_params = {} if 'options' in config: params = config['options'] db_params['host'] = params.get('db_host', 'localhost') db_params['port'] = params.get('db_port', '5432') db_params['user'] = params.get('db_user', 'odoo') db_params['password'] = params.get('db_password', '') return db_params def get_installed_modules(modules_list, db_name, db_params): """ Connects to the database and filters the given list of modules specifically for those that are installed (state='installed'). """ installed_modules = [] conn_params = { 'dbname': db_name, 'user': db_params.get('user', 'odoo'), 'password': db_params.get('password', ''), 'host': db_params.get('host', 'localhost'), 'port': db_params.get('port', '5432') } # Filter out empty or None values to use defaults or avoid errors conn_params = {k: v for k, v in conn_params.items() if v} try: conn = psycopg2.connect(**conn_params) cur = conn.cursor() # Check if modules are installed if not modules_list: return [] query = "SELECT name FROM ir_module_module WHERE state = 'installed' AND name IN %s" cur.execute(query, (tuple(modules_list),)) rows = cur.fetchall() installed_modules = [row[0] for row in rows] cur.close() conn.close() except psycopg2.Error as e: print(f"Database error: {e}") print("Ensure psycopg2 matches the server configuration.") return [] except Exception as e: print(f"Error checking installed modules: {e}") return [] return installed_modules def main(): parser = argparse.ArgumentParser(description="Upgrade installed modules in customaddons for a specific database.") parser.add_argument('-d', '--database', required=True, help="Database name to upgrade modules on.") parser.add_argument('-c', '--config', required=True, help="Path to Odoo configuration file.") parser.add_argument('--odoo-bin', default='./odoo/odoo-bin', help="Path to odoo-bin executable (default: ./odoo/odoo-bin)") args = parser.parse_args() # Determine customaddons path relative to this script script_dir = os.path.dirname(os.path.abspath(__file__)) custom_addons_path = os.path.join(script_dir, 'customaddons') print(f"Scanning for modules in {custom_addons_path}...") modules = get_custom_addons_modules(custom_addons_path) if not modules: print("No modules found in customaddons.") sys.exit(0) print(f"Found {len(modules)} local modules.") # Get DB params from config db_params = get_db_params(args.config) print(f"Checking installed status in database '{args.database}'...") installed_modules = get_installed_modules(modules, args.database, db_params) if not installed_modules: print("None of the local modules are installed in the specified database.") sys.exit(0) print(f"Found {len(installed_modules)} installed modules to upgrade.") # Construct the Odoo command modules_str = ','.join(installed_modules) # Check if odoo-bin exists if not os.path.exists(args.odoo_bin) and not os.path.exists(os.path.abspath(args.odoo_bin)): print(f"Error: odoo-bin not found at {args.odoo_bin}") sys.exit(1) cmd = [ sys.executable, args.odoo_bin, '-c', args.config, '-d', args.database, '-u', modules_str, '--stop-after-init' ] print("--------------------------------------------------") print(f"Starting upgrade for database '{args.database}'...") print(f"Modules to upgrade: {modules_str}") print(f"Command: {' '.join(cmd)}") print("--------------------------------------------------") try: subprocess.run(cmd, check=True) print("--------------------------------------------------") print("Upgrade completed successfully.") except subprocess.CalledProcessError as e: print("--------------------------------------------------") print(f"Error: Upgrade failed with exit code {e.returncode}") sys.exit(e.returncode) except Exception as e: print(f"An unexpected error occurred: {e}") sys.exit(1) if __name__ == "__main__": main()