Skip to content
Snippets Groups Projects

Check availability of Odoo modules

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Théo - Le Filament
    Edited
    check_modules.py 6.44 KiB
    #!/usr/bin/python3
    
    import urllib.request, urllib.error
    import re
    import sys
    import yaml
    
    
    repos = {
        "https://github.com/OCA/account-financial-reporting": [
            "account_tax_balance"
        ],
        "https://github.com/OCA/account-financial-tools": [
            "account_lock_date_update",
            "account_move_name_sequence",
            "account_reconcile_show_boolean"
        ],
        "https://github.com/OCA/account-invoicing": [
            "sale_timesheet_invoice_description"
        ],
        "https://github.com/OCA/account-reconcile": [
            "account_reconciliation_widget"
        ],
        "https://github.com/OCA/bank-statement-import": [
            "account_statement_import",
            "account_statement_import_ofx"
        ],
        "https://github.com/OCA/crm": [
            "crm_stage_probability"
        ],
        "https://github.com/OCA/partner-contact": [
            "partner_disable_gravatar",
            "partner_firstname"
        ],
        "https://github.com/OCA/project": [
            "project_category",
            "project_status",
            "project_task_default_stage",
            "project_template",
            "project_timeline"
        ],
        "https://github.com/OCA/server-auth": [
            "password_security"
        ],
        "https://github.com/OCA/server-brand": [
            "disable_odoo_online",
            "remove_odoo_enterprise"
        ],
        "https://github.com/OCA/server-tools": [
            "base_search_fuzzy",
            "module_change_auto_install"
        ],
        "https://github.com/OCA/server-ux": [
            "base_technical_features",
            "date_range",
            "mass_editing"
        ],
        "https://github.com/OCA/social": [
            "base_search_mail_content",
            "mail_debrand",
            "mail_tracking"
        ],
        "https://github.com/OCA/web": [
            "web_environment_ribbon",
            "web_responsive",
            "web_no_bubble",
            "web_timeline"
        ]
    }
    
    
    class Style:
        """Set of console display styles.
    
        """
        black = '\033[30m'
        red = '\033[31m'
        green = '\033[32m'
        yellow = '\033[33m'
        blue = '\033[34m'
        magenta = '\033[35m'
        cyan = '\033[36m'
        lightgray = '\033[37m'
        default = '\033[39m'
        bold = '\033[1m'
        normal = '\033[0m'
    
    
    def print_err(message):
        print(f"{Style.red}{Style.bold}err: {message}{Style.normal}"
              f"{Style.default}", file=sys.stderr)
    
    
    def check_pr(repo, module):
        conn = urllib.request.urlopen(
            f"{repo}/pulls?q=is%3Apr+is%3Aopen+%5B{version}%5D+mig+{module}")
        html = conn.read().decode('utf-8')
        result = re.search(rf"{repo[18:]}/pull/\d+", html)
        if result:
            return f"https://github.com{result.group()}"
        return None
    
    def load_repos(host_file):
        with open(host_file, 'r', encoding='utf8') as file:
            host = yaml.safe_load(file)
            for instance in host['odoo_nonprod_instances']:
                for repo in instance['custom_modules_oca']:
                    repo_url = f"https://github.com/OCA/{repo['repo']}"
                    if repos.get(repo_url):
                        repos[repo_url].extend(repo['modules'])
                    else:
                        repos[repo_url] = repo['modules']
                for repo in instance['custom_modules']:
                    repos[f"https://sources.le-filament.com/lefilament/{repo}"] = None
    
    def print_help():
        print(f"usage:\n  {sys.argv[0]} <version> [hostvars file]\n"
              "options:\n  <version>        Odoo version\n"
              "  [hostvars file]  Ansible hostvars file to also check modules of"
              " this host")
    
    
    def main():
        if len(sys.argv) >= 2:
            version = sys.argv[1]
        else:
            print_err("missing version argument (e.g.: '16.0')")
            sys.exit(1)
        if sys.argv[1] == '-h' or sys.argv[1] == '--help':
            print_help()
            sys.exit(0)
        if len(sys.argv) == 3:
            load_repos(sys.argv[2])
    
        print(f"{Style.blue}💡 Checking availability Odoo v{version} "
              f"modules...{Style.default}\n")
    
        module_count = 0
        available_module_count = 0
        pr_count = 0
        for repo, modules in repos.items():
            tree = "tree"
            if 'github.com' not in repo:
                tree = '-/tree'
            repo_name = repo.split('/')[-1]
    
            for module in modules or []:
                module_count += 1
                try:
                    conn = urllib.request.urlopen(f"{repo}/{tree}/"
                                                  f"{version}/{module}")
                except urllib.error.HTTPError as err:
                    if err.code == 404:
                        pr_url = check_pr(repo, module)
                        if pr_url:
                            pr_count += 1
                            print(f"{Style.red}❌ Not ready: {module} "
                                  f"(in {repo_name}){Style.default} "
                                  f"PR at {pr_url}")
                        else:
                            print(f"{Style.red}❌ Not ready: {module} "
                                  f"(in {repo_name}){Style.default}")
                    else:
                        print_err(f"HTTP error code {err.code} "
                                  f"for {module} (in {repo})")
                except urllib.error.URLError as err:
                    print_err(f"HTTP error '{err.reason}' for {module} "
                              f"(in {repo_name})")
                else:
                    available_module_count += 1
                    print(f"{Style.green}✅ Available: {module} "
                          f"(in {repo_name}){Style.default}")
    
            if not modules:
                module_count += 1
                try:
                    conn = urllib.request.urlopen(f"{repo}/{tree}/{version}/")
                except urllib.error.HTTPError as err:
                    if err.code == 404:
                        print(f"{Style.red}❌ Not already: {repo_name}"
                              f"{Style.default}")
                    else:
                        print_err(f"HTTP error code {err.code} for {repo_name}")
                except urllib.error.URLError as err:
                    print_err(f"HTTP error '{err.reason}' for {repo_name}")
                else:
                    available_module_count += 1
                    print(f"{Style.green}✅ Available:  {repo_name}"
                          f"{Style.default}")
    
    
        if available_module_count < module_count:
            print(f"\n{Style.bold}Result: {Style.green}{available_module_count}"
                  f"{Style.default} / {module_count} modules are available. "
                  f"There are {pr_count} opened pull requests.{Style.normal}")
        else:
            print(f"\n{Style.green}{Style.bold}Result: all {module_count} modules "
                  f"are available. It's ready to go!{Style.normal}{Style.default}")
    
    
    if __name__ == '__main__':
        main()
    • Cool, merci Theo ! C'est cool pour les modules pour la v16 qu'on met par défaut dans l'image. Ce qui serait top serait de pouvoir lui donner en entrée un fichier yaml comme celui qu'on pousse sur les serveurs avec la liste des modules d'un client pour qu'il vérifie lesquels existent ou pas (et la version à vérifier en paramètre) - si pas de fichier yaml en entrée il affiche comme aujourd'hui avec les modules de base de notre image.

    • Théo, il faudrait modifier la fonction load_file() avec nouvelle architecture fichier host_vars :

      def load_repos(host_file):
          with open(host_file, 'r', encoding='utf8') as file:
              host = yaml.safe_load(file)
              for instance in host['odoo_instances']:
                  for repo in host['odoo_instances'][instance].get('custom_modules_oca', []):
                      repo_url = f"https://github.com/OCA/{repo['repo']}"
                      if repos.get(repo_url):
                          repos[repo_url].extend(repo['modules'])
                      else:
                          repos[repo_url] = repo['modules']
                  for repo in host['odoo_instances'][instance].get('custom_modules', []):
                      repos[f"https://sources.le-filament.com/lefilament/{repo['repo']}"] = None

      ça pourrait aussi être intéressant d'aller chercher des valeurs uniques dans la variable repos (par exemple pour les hôtes où tu as plusieurs instances build avec différentes conf) ou passer en paramètre l'instance à utiliser ?

    • Il faudrait aussi mettre à jour la liste des modules par défaut (ou la récupérer du fichier https://sources.le-filament.com/lefilament/odoo_docker/-/blob/{version}/{version}.Dockerfile ?)

    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment