Check availability of Odoo modules
#!/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 ?)