diff --git a/script/database/get_module_list_from_database.py b/script/database/get_module_list_from_database.py new file mode 100755 index 0000000..9819aaf --- /dev/null +++ b/script/database/get_module_list_from_database.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# © 2021-2025 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import argparse +import logging +import os +import sys + +new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") +) +sys.path.append(new_path) + +from script.execute import execute +from script.git.git_tool import GitTool + +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ + Will print list of installed module from database +""", + epilog="""\ +""", + ) + parser.add_argument( + "--database", + required=True, + help="The database name to extract", + ) + args = parser.parse_args() + return args + + +def main(): + config = get_config() + execute_cmd = execute.Execute() + + if config.database: + cmd = f"cat ./script/odoo/util/show_installed_module.py | ./odoo_bin.sh shell -d {config.database}" + status, output = execute_cmd.exec_command_live( + cmd, + quiet=True, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + has_find = False + for line in output: + if has_find: + print(line.strip()) + if line.strip() == "Installed modules:": + has_find = True + + +if __name__ == "__main__": + main() diff --git a/script/database/get_repo_from_backup.py b/script/database/get_repo_from_backup.py new file mode 100755 index 0000000..4b17782 --- /dev/null +++ b/script/database/get_repo_from_backup.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# © 2021-2025 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import argparse +import json +import logging +import os +import sys +import zipfile + +new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") +) +sys.path.append(new_path) + +from script.execute import execute + +logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) + +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ + Extract all repo path from installed module into backup. + Use --backup_path or --backup_name +""", + epilog="""\ +""", + ) + parser.add_argument("--backup_path", help="Backup file path") + parser.add_argument("--backup_name", help="Backup file name") + parser.add_argument( + "--debug", + action="store_true", + help="Will show all debug and execution information", + ) + args = parser.parse_args() + + die( + not (bool(args.backup_path) or bool(args.backup_name)), + "Take --backup_path or --backup_name", + ) + + return args + + +def main(): + config = get_config() + + if config.backup_path: + file_path = config.backup_path + elif not config.backup_name.endswith(".zip"): + file_path = os.path.join("image_db", f"{config.backup_name}.zip") + else: + file_path = os.path.join("image_db", config.backup_name) + + with zipfile.ZipFile(file_path, "r") as zip_ref: + manifest_file = zip_ref.open("manifest.json") + + json_manifest_file = json.load(manifest_file) + lst_module = set(json_manifest_file.get("modules").keys()) + str_module = ";".join(lst_module) + cmd = f'./script/database/get_repo_from_module.py --module "{str_module}"' + execute_cmd = execute.Execute() + status, output = execute_cmd.exec_command_live( + cmd, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + + +def die(cond, message, code=1): + if cond: + print(message, file=sys.stderr) + sys.exit(code) + + +if __name__ == "__main__": + main() diff --git a/script/database/get_repo_from_module.py b/script/database/get_repo_from_module.py new file mode 100755 index 0000000..7d7b326 --- /dev/null +++ b/script/database/get_repo_from_module.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# © 2021-2025 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import argparse +import json +import logging +import os +import sys +import zipfile + +new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") +) +sys.path.append(new_path) + +from script.execute import execute + +logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) + +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ + Extract all repo path from installed module into backup. + Use --backup_path or --backup_name +""", + epilog="""\ +""", + ) + parser.add_argument("--module", help="Module list separate by ;") + parser.add_argument("--debug", action="store_true") + args = parser.parse_args() + + return args + + +def main(): + config = get_config() + + lst_module = set(config.module.split(";")) + + cmd = "parallel ::: " + " ".join( + [ + f"'./script/addons/check_addons_exist.py --output_path -m \"{a}\"'" + for a in lst_module + ] + ) + execute_cmd = execute.Execute() + status, output = execute_cmd.exec_command_live( + cmd, + quiet=not config.debug, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + set_path_repo = set() + for line_module_path in output: + line_repo = os.path.dirname(line_module_path) + set_path_repo.add(line_repo) + for path_repo in set_path_repo: + print(path_repo) + + +def die(cond, message, code=1): + if cond: + print(message, file=sys.stderr) + sys.exit(code) + + +if __name__ == "__main__": + main() diff --git a/script/database/image_db.py b/script/database/image_db.py index a7ae69f..757debb 100755 --- a/script/database/image_db.py +++ b/script/database/image_db.py @@ -359,6 +359,7 @@ def run_cmd(cmd, quiet=False, sys_exit=True): if not quiet: _logger.info(f"Run cmd: {cmd}") debut = time.time() + # Maybe can use exec_command_live process = subprocess.Popen( cmd, shell=True, diff --git a/script/execute/__init__.py b/script/execute/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/script/execute/execute.py b/script/execute/execute.py new file mode 100644 index 0000000..89f0041 --- /dev/null +++ b/script/execute/execute.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# © 2021-2025 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import datetime +import logging +import os +import shutil +import subprocess +import sys +import time + +import humanize + +cst_venv_erplibre = ".venv.erplibre" + +new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") +) +sys.path.append(new_path) + + +logging.basicConfig( + format=( + "%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d]" + " %(message)s" + ), + datefmt="%Y-%m-%d:%H:%M:%S", + level=logging.INFO, +) +_logger = logging.getLogger(__name__) + + +class Execute: + def __init__(self): + self.cmd_source_erplibre = "" + self.cmd_source_default = "" + exec_path_gnome_terminal = shutil.which("gnome-terminal") + if exec_path_gnome_terminal: + self.cmd_source_erplibre = ( + f"gnome-terminal -- bash -c 'source" + f" ./{cst_venv_erplibre}/bin/activate;%s'" + ) + self.cmd_source_default = "gnome-terminal -- bash -c '" f"%s'" + else: + exec_path_tell = shutil.which("osascript") + if exec_path_tell: + self.cmd_source_erplibre = ( + "osascript -e 'tell application \"Terminal\"'" + ) + self.cmd_source_erplibre += " -e 'tell application \"System Events\" to keystroke \"t\" using {command down}' -e 'delay 0.1' -e 'do script \"" + self.cmd_source_erplibre += f"cd {os.getcwd()}; source ./{cst_venv_erplibre}/bin/activate; %s\" in front window'" + self.cmd_source_erplibre += " -e 'end tell'" + else: + self.cmd_source_erplibre = ( + f"source ./{cst_venv_erplibre}/bin/activate;%s" + ) + + def exec_command_live( + self, + commande, + source_erplibre=True, + quiet=False, + single_source_erplibre=False, + new_window=False, + single_source_odoo=False, + source_odoo="", + new_env=None, + return_status_and_command=False, + return_status_and_output=False, + return_status_and_output_and_command=False, + ): + """ + Exécute une commande et affiche la sortie en direct. + + Args: + commande (str): La commande à exécuter (sous forme de chaîne de caractères). + """ + + my_env = os.environ.copy() + if new_env: + my_env.update(new_env) + + process_start_time = time.time() + return_status = None + if source_erplibre: + # commande = f"source ./{cst_venv_erplibre}/bin/activate && " + commande + # cmd = ( + # f"gnome-terminal --tab -- bash -c 'source" + # f" ./{cst_venv_erplibre}/bin/activate;{commande}'" + # ) + commande = self.cmd_source_erplibre % commande + # os.system(f"./script/terminal/open_terminal.sh {commande}") + elif single_source_erplibre: + commande = ( + f"source ./{cst_venv_erplibre}/bin/activate && %s" % commande + ) + elif single_source_odoo: + if not source_odoo and os.path.exists("./.erplibre-version"): + with open("./.erplibre-version") as f: + source_odoo = f.read() + if not source_odoo: + _logger.error( + f"You cannot execute Odoo command if no version is installed. Command : {commande}" + ) + return -1 + commande = ( + f"source ./.venv.{source_odoo}/bin/activate && {commande}" + ) + if new_window and self.cmd_source_default: + commande = self.cmd_source_default % commande + + if not quiet: + print("🏠 ⬇ Execute command :") + print(commande) + lst_output = [] + + try: + process = subprocess.Popen( + commande, + shell=True, + executable="/bin/bash", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, # Désactive la mise en tampon pour la sortie en direct + universal_newlines=True, # Pour traiter les sauts de lignes correctement + env=my_env, + ) + + while True: + ligne = process.stdout.readline() + if not ligne: + break + if not quiet: + print(ligne, end="") + if ( + return_status_and_output + or return_status_and_output_and_command + ): + # Remove last \n char + lst_output.append( + ligne.removesuffix("\r\n") + .removesuffix("\n") + .removesuffix("\r") + ) + + process.wait() # Attendre la fin du process + return_status = process.returncode + if process.returncode != 0 and not quiet: + print( + "La commande a retourné un code d'erreur :" + f" {process.returncode}" + ) + + except FileNotFoundError: + if not quiet: + if "password" in commande: + print( + f"Erreur : La commande '{commande.split(' ')[0]}'[...] n'a" + " pas été trouvée." + ) + else: + print( + f"Erreur : La commande '{commande}' n'a pas été trouvée." + ) + except Exception as e: + if not quiet: + print(f"Une erreur s'est produite : {e}") + process_end_time = time.time() + duration_sec = process_end_time - process_start_time + if humanize: + duration_delta = datetime.timedelta(seconds=duration_sec) + humain_time = humanize.precisedelta(duration_delta) + if not quiet: + print(f"🏠 ⬆ Executed ({humain_time}) :") + else: + if not quiet: + print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :") + if not quiet: + print(commande) + print() + if return_status_and_output_and_command: + return return_status, commande, lst_output + if return_status_and_command: + return return_status, commande + if return_status_and_output: + return return_status, lst_output + return return_status diff --git a/script/git/git_repo_update_group.py b/script/git/git_repo_update_group.py index 882af4b..1b64cf9 100755 --- a/script/git/git_repo_update_group.py +++ b/script/git/git_repo_update_group.py @@ -7,14 +7,12 @@ import logging import os import sys -import git -from git import Repo - new_path = os.path.normpath( os.path.join(os.path.dirname(__file__), "..", "..") ) sys.path.append(new_path) +from script.execute import execute from script.git.git_tool import GitTool _logger = logging.getLogger(__name__) @@ -26,9 +24,6 @@ def get_config(): :return: dict of config file settings and command line arguments """ - config = GitTool.get_project_config() - - # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="""\ @@ -52,6 +47,22 @@ def get_config(): action="store_true", help="Will remove odoo path, need this feature for OpenUpgrade when Odoo <= 13", ) + parser.add_argument( + "--from_backup_path", + help="Will read backup path and whitelist the repo", + ) + parser.add_argument( + "--from_backup_name", + help="Will read backup name and whitelist the repo", + ) + parser.add_argument( + "--add_repo", + help="Add repo, separate by ; for list", + ) + parser.add_argument( + "--database", + help="Add repo from module found into database.", + ) args = parser.parse_args() return args @@ -59,13 +70,63 @@ def get_config(): def main(): config = get_config() git_tool = GitTool() + execute_cmd = execute.Execute() + index_to_remove = len(os.getcwd()) + 1 filter_group = config.group if config.group else None + lst_whitelist = [] + if config.from_backup_path or config.from_backup_name: + # script/database/get_repo_from_backup.py + # --backup_name bpir_prod_5_dec_2025_2026-02-04_14h27m54s.zip + if config.from_backup_path: + cmd = f"./script/database/get_repo_from_backup.py --backup_path {config.from_backup_path}" + else: + cmd = f"./script/database/get_repo_from_backup.py --backup_name {config.from_backup_name}" + status, output = execute_cmd.exec_command_live( + cmd, + quiet=True, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + for line in output: + repo_name = line[index_to_remove:].strip() + lst_whitelist.append(repo_name) + + if config.database: + cmd = f"./script/database/get_module_list_from_database.py --database {config.database}" + status, output = execute_cmd.exec_command_live( + cmd, + quiet=True, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + str_module = ";".join(output) + cmd = f'./script/database/get_repo_from_module.py --module "{str_module}"' + status, output = execute_cmd.exec_command_live( + cmd, + quiet=True, + source_erplibre=False, + single_source_erplibre=True, + return_status_and_output=True, + ) + for line in output: + repo_name = line[index_to_remove:].strip() + lst_whitelist.append(repo_name) + + if config.add_repo: + lst_add_repo = [a.strip() for a in config.add_repo.split(";")] + else: + lst_add_repo = [] + git_tool.generate_generate_config( filter_group=filter_group, extra_path=config.extra_addons_path, ignore_odoo_path=config.ignore_odoo_path, + lst_add_repo=lst_add_repo, + lst_whitelist=lst_whitelist, ) diff --git a/script/git/git_tool.py b/script/git/git_tool.py index 47c1f14..73d360a 100644 --- a/script/git/git_tool.py +++ b/script/git/git_tool.py @@ -74,7 +74,7 @@ class GitTool: def get_transformed_repo_info_from_url( self, url: str, - repo_path: str = "./", + repo_path: str = ".", get_obj: bool = True, is_submodule: bool = True, organization_force: str = None, @@ -103,15 +103,16 @@ class GitTool: repo_name = repo_name[:-4] if is_submodule: if not sub_path or sub_path == ".": - path = f"{repo_name}" + path = repo_name else: - path = f"{sub_path}/{organization}_{repo_name}" + path = os.path.join(sub_path, f"{organization}_{repo_name}") else: - path = f"{repo_path}" - if repo_path[-1] == "/": - relative_path = f"{repo_path}{path}" - else: - relative_path = f"{repo_path}/{path}" + path = repo_path + relative_path = os.path.join(repo_path, path) + # if repo_path[-1] == "/": + # relative_path = f"{repo_path}{path}" + # else: + # relative_path = f"{repo_path}/{path}" relative_path = os.path.normpath(relative_path) original_organization = organization @@ -150,7 +151,7 @@ class GitTool: def get_repo_info( self, - repo_path: str = "./", + repo_path: str = ".", add_root: bool = False, is_manifest: bool = True, filter_group=None, @@ -166,7 +167,7 @@ class GitTool: ) def get_repo_info_submodule( - self, repo_path: str = "./", add_root: bool = False + self, repo_path: str = ".", add_root: bool = False ) -> list: """ Get information about submodule from repo_path @@ -182,7 +183,7 @@ class GitTool: "name": name of the submodule }] """ - filename = f"{repo_path}/.gitmodules" + filename = os.path.join(repo_path, ".gitmodules") lst_repo = [] with open(filename) as file: txt = file.readlines() @@ -200,7 +201,7 @@ class GitTool: "url_https": url_https, "url_git": url_git, "path": path, - "relative_path": f"{repo_path}/{path}", + "relative_path": os.path.join(repo_path, path), "name": name, } lst_repo.append(data) @@ -225,7 +226,7 @@ class GitTool: "url_https": url_https, "url_git": url_git, "path": path, - "relative_path": f"{repo_path}/{path}", + "relative_path": os.path.join(repo_path, path), "name": name, } lst_repo.append(data) @@ -248,7 +249,7 @@ class GitTool: return lst_repo def get_repo_info_manifest_xml( - self, repo_path: str = "./", add_root: bool = False, filter_group=None + self, repo_path: str = ".", add_root: bool = False, filter_group=None ) -> list: """ Get information about manifest of Repo from repo_path @@ -318,7 +319,7 @@ class GitTool: "url_https": url_https, "url_git": url_git, "path": path, - "relative_path": f"{repo_path}/{path}", + "relative_path": os.path.join(repo_path, path), "name": name, "group": groups, } @@ -351,7 +352,7 @@ class GitTool: return lst_repo def get_manifest_xml_info( - self, repo_path: str = "./", filename=None, add_root: bool = False + self, repo_path: str = ".", filename=None, add_root: bool = False ) -> list: """ Get contain of manifest @@ -388,7 +389,7 @@ class GitTool: return dct_remote, dct_project, default_remote @staticmethod - def get_project_config(repo_path="./"): + def get_project_config(repo_path="."): """ Get information about configuration in env_var.sh :param repo_path: path of repo to get information env_var.sh @@ -397,7 +398,7 @@ class GitTool: CST_EL_GITHUB_TOKEN: TOKEN, } """ - filename = f"{repo_path}env_var.sh" + filename = os.path.join(repo_path, "env_var.sh") with open(filename) as file: txt = file.readlines() txt = [a[:-1] for a in txt if "=" in a] @@ -421,17 +422,29 @@ class GitTool: def generate_generate_config( self, - repo_path="./", + repo_path=".", filter_group=None, extra_path=None, ignore_odoo_path=None, + lst_add_repo=None, + lst_whitelist=None, ): - filename_locally = f"{repo_path}script/generate_config.sh" + filename_locally = os.path.join(repo_path, "script/generate_config.sh") if not filter_group: filter_group = self.odoo_version_long - lst_repo = self.get_repo_info( + lst_repo_origin = self.get_repo_info( repo_path=repo_path, filter_group=filter_group ) + if lst_whitelist: + lst_repo = [] + for repo in lst_repo_origin: + if ( + repo.get("path") in lst_whitelist + or repo.get("path") in lst_add_repo + ): + lst_repo.append(repo) + else: + lst_repo = lst_repo_origin lst_result = [] if not lst_repo: print( @@ -444,7 +457,9 @@ class GitTool: continue # groups = repo.get("group") # Use variable instead of hardcoded path - if update_repo.startswith(f"{filter_group}/addons"): + if update_repo.startswith( + os.path.join(self.odoo_version_long, "addons") + ): lst_path = update_repo.split("/", 1) update_repo = f"${{EL_HOME_ODOO_PROJECT}}/" + lst_path[1] # str_repo = ( @@ -715,7 +730,7 @@ class GitTool: print(str_xml_text + "\n") def generate_git_modules( - self, lst_repo: List[Struct], repo_path: str = "./" + self, lst_repo: List[Struct], repo_path: str = "." ): lst_modules = [] for repo in lst_repo: @@ -727,10 +742,10 @@ class GitTool: ) # create file - with open(f"{repo_path}.gitmodules", mode="w") as file: + with open(os.path.join(repo_path, ".gitmodules"), mode="w") as file: file.writelines(lst_modules) - def get_source_repo_addons(self, repo_path="./", add_repo_root=False): + def get_source_repo_addons(self, repo_path=".", add_repo_root=False): """ Read file CST_FILE_SOURCE_REPO_ADDONS and return structure of data :param repo_path: path to find file CST_FILE_SOURCE_REPO_ADDONS @@ -745,7 +760,7 @@ class GitTool: "name": name of the submodule }] """ - file_name = f"{repo_path}{CST_FILE_SOURCE_REPO_ADDONS}" + file_name = os.path.join(repo_path, CST_FILE_SOURCE_REPO_ADDONS) lst_result = [] if add_repo_root: # TODO what to do if origin not exist? @@ -800,7 +815,7 @@ class GitTool: lst_result.append(repo_info) return lst_result - def get_manifest_file(self, repo_path: str = "./"): + def get_manifest_file(self, repo_path: str = "."): """ Find .repo and return default manifest file. :param repo_path: path to search .repo @@ -811,7 +826,7 @@ class GitTool: ) if os.path.exists(file): return file - file = f"{repo_path}/.repo/manifest.xml" + file = os.path.join(repo_path, ".repo/manifest.xml") if not os.path.exists(file): return "" with open(file) as xml: @@ -824,8 +839,8 @@ class GitTool: def get_matching_repo( self, - actual_repo="./", - repo_compare_to="./", + actual_repo=".", + repo_compare_to=".", force_normalize_compare=False, sync_with_submodule=False, ): diff --git a/script/install/install_OSX_dependency.sh b/script/install/install_OSX_dependency.sh index 7d6a49d..94568b6 100755 --- a/script/install/install_OSX_dependency.sh +++ b/script/install/install_OSX_dependency.sh @@ -12,7 +12,7 @@ EL_USER=${USER} #-------------------------------------------------- # Install PostgreSQL Server #-------------------------------------------------- -echo "\n---- Install PostgreSQL Server ----" +echo "\n---- Install PostgreSQL Server ----" if which psql >/dev/null 2>&1; then echo "postgresql is already installed, skipping" else @@ -21,39 +21,39 @@ else brew services start postgresql@15 fi -echo "\n---- Creating the ERPLibre PostgreSQL User ----" -sudo su - postgres -c "createuser -s ${EL_USER}" 2> /dev/null || true -sudo su - postgres -c "CREATE EXTENSION postgis;\nCREATE EXTENSION postgis_topology;" 2> /dev/null || true +echo "\n---- Creating the ERPLibre PostgreSQL User ----" +sudo su - postgres -c "createuser -s ${EL_USER}" 2>/dev/null || true +sudo su - postgres -c "CREATE EXTENSION postgis;\nCREATE EXTENSION postgis_topology;" 2>/dev/null || true #-------------------------------------------------- # Install Dependencies #-------------------------------------------------- -echo "\n--- Installing Python 3 + pip3 --" -#TODO is python@3.7 line here still usefull?? Should we get rid of it? -brew install git python@3.7 wget parallel mariadb +echo "\n--- Installing Python 3 + pip3 --" +brew install git wget parallel mariadb brew link git brew link wget -echo "\n--- Installing extra --" +echo "\n--- Installing extra --" brew install shfmt brew install parallel brew install swig brew install portaudio brew install xmlsec1 -echo "\n---- Installing nodeJS NPM and rtlcss for LTR support ----" +echo "\n---- Installing nodeJS NPM and rtlcss for LTR support ----" brew install nodejs npm openssl sudo npm install -g rtlcss sudo npm install -g less npm install -echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc +echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >>~/.zshrc #-------------------------------------------------- # Install Wkhtmltopdf if needed #-------------------------------------------------- -echo "\n---- Installing Wkhtmltopdf if needed ----" +echo "\n---- Installing Wkhtmltopdf if needed ----" if [ ! -f "wkhtmltox-0.12.6-2.macos-cocoa.pkg" ]; then sudo wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-2/wkhtmltox-0.12.6-2.macos-cocoa.pkg sudo sudo installer -pkg wkhtmltox-0.12.6-2.macos-cocoa.pkg -target / -else echo "Wkhtmltopdf already installed" +else + echo "Wkhtmltopdf already installed" fi diff --git a/script/manifest/update_manifest_local_dev.sh b/script/manifest/update_manifest_local_dev.sh index fdeccde..8b8c5f2 100755 --- a/script/manifest/update_manifest_local_dev.sh +++ b/script/manifest/update_manifest_local_dev.sh @@ -14,10 +14,16 @@ else MANIFEST_TARGET="$EL_MANIFEST_DEV" fi +if command -v nproc >/dev/null 2>&1; then + JOBS="$(nproc --all)" +else + JOBS="$(sysctl -n hw.ncpu)" +fi + # Generate local manifest .venv.erplibre/bin/python ./script/git/git_merge_repo_manifest.py --output .repo/local_manifests/erplibre_manifest.xml --with_OCA .venv.erplibre/bin/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${MANIFEST_TARGET} "$@" -.venv.erplibre/bin/repo sync -c -j $(nproc --all) -v -m ${MANIFEST_TARGET} +.venv.erplibre/bin/repo sync -c -j "$JOBS" -v -m ${MANIFEST_TARGET} kill ${DAEMON_PID} diff --git a/script/manifest/update_manifest_local_dev_code_generator.sh b/script/manifest/update_manifest_local_dev_code_generator.sh index 0aa689e..ec52a82 100755 --- a/script/manifest/update_manifest_local_dev_code_generator.sh +++ b/script/manifest/update_manifest_local_dev_code_generator.sh @@ -15,7 +15,13 @@ else MANIFEST_TARGET="$EL_MANIFEST_DEV" fi +if command -v nproc >/dev/null 2>&1; then + JOBS="$(nproc --all)" +else + JOBS="$(sysctl -n hw.ncpu)" +fi + .venv.erplibre/bin/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${MANIFEST_TARGET} -g base,code_generator -.venv.erplibre/bin/repo sync -c -j $(nproc --all) -v -m ${MANIFEST_TARGET} +.venv.erplibre/bin/repo sync -c -j "$JOBS" -v -m ${MANIFEST_TARGET} kill ${DAEMON_PID} diff --git a/script/manifest/update_manifest_local_mobile.sh b/script/manifest/update_manifest_local_mobile.sh index ece9d2e..32d588e 100755 --- a/script/manifest/update_manifest_local_mobile.sh +++ b/script/manifest/update_manifest_local_mobile.sh @@ -14,10 +14,16 @@ else MANIFEST_TARGET="$EL_MANIFEST_DEV" fi +if command -v nproc >/dev/null 2>&1; then + JOBS="$(nproc --all)" +else + JOBS="$(sysctl -n hw.ncpu)" +fi + # Generate local manifest .venv.erplibre/bin/python ./script/git/git_merge_repo_manifest.py --output .repo/local_manifests/erplibre_manifest.xml --with_mobile .venv.erplibre/bin/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${MANIFEST_TARGET} "$@" -.venv.erplibre/bin/repo sync -c -j $(nproc --all) -v -m ${MANIFEST_TARGET} +.venv.erplibre/bin/repo sync -c -j "$JOBS" -v -m ${MANIFEST_TARGET} kill ${DAEMON_PID} diff --git a/script/manifest/update_manifest_local_prod.sh b/script/manifest/update_manifest_local_prod.sh index c5a962a..66c029a 100755 --- a/script/manifest/update_manifest_local_prod.sh +++ b/script/manifest/update_manifest_local_prod.sh @@ -15,7 +15,13 @@ else MANIFEST_TARGET="$EL_MANIFEST_DEV" fi +if command -v nproc >/dev/null 2>&1; then + JOBS="$(nproc --all)" +else + JOBS="$(sysctl -n hw.ncpu)" +fi + .venv.erplibre/bin/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${MANIFEST_TARGET} -.venv.erplibre/bin/repo sync -c -j $(nproc --all) -v -m ${MANIFEST_TARGET} +.venv.erplibre/bin/repo sync -c -j "$JOBS" -v -m ${MANIFEST_TARGET} kill ${DAEMON_PID} diff --git a/script/manifest/update_manifest_prod.sh b/script/manifest/update_manifest_prod.sh index c2f8755..4a8e8e2 100755 --- a/script/manifest/update_manifest_prod.sh +++ b/script/manifest/update_manifest_prod.sh @@ -5,6 +5,12 @@ #EL_MANIFEST_PROD="./default.xml" #EL_MANIFEST_DEV="./manifest/default.dev.xml" +if command -v nproc >/dev/null 2>&1; then + JOBS="$(nproc --all)" +else + JOBS="$(sysctl -n hw.ncpu)" +fi + # Update git-repo .venv.erplibre/bin/repo init -u https://github.com/ERPLibre/ERPLibre -b $(git rev-parse --verify HEAD) -.venv.erplibre/bin/repo sync -v -j $(nproc --all) +.venv.erplibre/bin/repo sync -v -j "$JOBS" diff --git a/script/odoo/migration/README.md b/script/odoo/migration/README.md index 105e6e6..c4892c1 100644 --- a/script/odoo/migration/README.md +++ b/script/odoo/migration/README.md @@ -3,7 +3,7 @@ Run this script when doing database migration. Example : ```bash -source ./.venv.odoo15.0_python3.8.20/bin/activate && cat ./script/migration/fix_migration_odoo140_to_odoo150.py | ./odoo15.0/odoo/odoo-bin shell -d DATABASE +source ./.venv.odoo15.0_python3.8.20/bin/activate && cat ./script/odoo/migration/fix_migration_odoo140_to_odoo150.py | ./odoo15.0/odoo/odoo-bin shell -d DATABASE ``` Check [uninstall_module_list_odoo140_to_odoo150.txt](uninstall_module_list_odoo140_to_odoo150.txt) diff --git a/script/selenium/selenium_lib.py b/script/selenium/selenium_lib.py index 8f8452f..344c32d 100644 --- a/script/selenium/selenium_lib.py +++ b/script/selenium/selenium_lib.py @@ -13,6 +13,7 @@ import sys import tempfile import time import tkinter as tk +from pathlib import Path from tkinter import filedialog from pykeepass import PyKeePass @@ -226,10 +227,18 @@ class SeleniumLib(object): self.config.firefox_binary_path ) elif not self.config.use_network: - status_location = subprocess.check_output( - ["which", "firefox"], text=True - ).strip() - firefox_options.binary_location = status_location + # test snap firefox for Ubuntu first + snap_firefox_path = ( + "/snap/firefox/current/usr/lib/firefox/firefox" + ) + firefox_path_exist = Path(snap_firefox_path).exists() + if firefox_path_exist: + firefox_options.binary_location = snap_firefox_path + else: + status_location = subprocess.check_output( + ["which", "firefox"], text=True + ).strip() + firefox_options.binary_location = status_location firefox_profile = webdriver.FirefoxProfile() firefox_profile.set_preference( diff --git a/script/todo/todo.py b/script/todo/todo.py index 8bceb5c..4751b38 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -54,6 +54,8 @@ try: # import rich import todo_upgrade from pykeepass import PyKeePass + + from script.execute import execute except ModuleNotFoundError as e: humanize = None ENABLE_CRASH = True @@ -81,34 +83,9 @@ class TODO: def __init__(self): self.dir_path = None self.kdbx = None - self.init() self.file_path = None self.config_file = config_file.ConfigFile() - - def init(self): - # Get command - self.cmd_source_erplibre = "" - self.cmd_source_default = "" - exec_path_gnome_terminal = shutil.which("gnome-terminal") - if exec_path_gnome_terminal: - self.cmd_source_erplibre = ( - f"gnome-terminal -- bash -c 'source" - f" ./{cst_venv_erplibre}/bin/activate;%s'" - ) - self.cmd_source_default = "gnome-terminal -- bash -c '" f"%s'" - else: - exec_path_tell = shutil.which("osascript") - if exec_path_tell: - self.cmd_source_erplibre = ( - "osascript -e 'tell application \"Terminal\"'" - ) - self.cmd_source_erplibre += " -e 'tell application \"System Events\" to keystroke \"t\" using {command down}' -e 'delay 0.1' -e 'do script \"" - self.cmd_source_erplibre += f"cd {os.getcwd()}; source ./{cst_venv_erplibre}/bin/activate; %s\" in front window'" - self.cmd_source_erplibre += " -e 'end tell'" - else: - self.cmd_source_erplibre = ( - f"source ./{cst_venv_erplibre}/bin/activate;%s" - ) + self.execute = execute.Execute() def run(self): with open(self.config_file.get_logo_ascii_file_path()) as my_file: @@ -150,7 +127,7 @@ class TODO: # f" ./{cst_venv_erplibre}/bin/activate;make todo'" # ) cmd = "make todo" - self.executer_commande_live(cmd, source_erplibre=True) + self.execute.exec_command_live(cmd, source_erplibre=True) # elif status == "3" or status == "install": # print("install") else: @@ -229,6 +206,7 @@ class TODO: [5] Doc - Recherche de documentation [6] Database - Outils sur les bases de données [7] Process - Outils sur les exécutions +[8] Config - Traitement du fichier de configuration [0] Retour """ while True: @@ -264,6 +242,10 @@ class TODO: status = self.prompt_execute_process() if status is not False: return + elif status == "8": + status = self.prompt_execute_config() + if status is not False: + return else: print("Commande non trouvée 🤖!") @@ -280,7 +262,7 @@ class TODO: ) if first_installation_input == "y": cmd = "./script/version/update_env_version.py --install" - self.executer_commande_live(cmd, source_erplibre=True) + self.execute.exec_command_live(cmd, source_erplibre=True) print("Wait after OS installation before continue.") # First detect pycharm, need to be open before installation and close to increase speed @@ -312,7 +294,7 @@ class TODO: pycharm_bin = "pycharm" if has_pycharm else "pycharm-community" cmd = f"cd {os.getcwd()} && {pycharm_bin} ./" - self.executer_commande_live( + self.execute.exec_command_live( cmd, source_erplibre=False, single_source_erplibre=False, @@ -432,7 +414,7 @@ class TODO: makefile_cmd = dct_instance.get("makefile_cmd") if makefile_cmd and not ignore_makefile: - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"make {makefile_cmd}", source_erplibre=False, single_source_erplibre=True, @@ -459,10 +441,10 @@ class TODO: if callback: callback(dct_instance) - def fill_help_info(self, lst_instance): + def fill_help_info(self, lst_choice): help_info = "Commande :\n" help_end = "[0] Retour\n" - for i, dct_instance in enumerate(lst_instance): + for i, dct_instance in enumerate(lst_choice): help_info += ( f"[{i + 1}] " + dct_instance["prompt_description"] + "\n" ) @@ -473,16 +455,16 @@ class TODO: # TODO proposer le déploiement à distance # TODO proposer l'exécution de docker # TODO proposer la création de docker - lst_instance = self.config_file.get_config("instance") - init_len = len(lst_instance) + lst_choice = self.config_file.get_config("instance") + init_len = len(lst_choice) if os.path.exists(MOBILE_HOME_PATH): dct_upgrade_odoo_database = { "prompt_description": "Mobile - Compile and run software", "callback": self.callback_make_mobile_home, } - lst_instance.append(dct_upgrade_odoo_database) - help_info = self.fill_help_info(lst_instance) + lst_choice.append(dct_upgrade_odoo_database) + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) @@ -498,15 +480,15 @@ class TODO: status = click.confirm( "Voulez-vous une nouvelle instance?" ) - dct_instance = lst_instance[int_cmd - 1] + dct_instance = lst_choice[int_cmd - 1] self.execute_from_configuration( dct_instance, exec_run_db=True, ignore_makefile=not bool(status), ) - elif int_cmd <= len(lst_instance): + elif int_cmd <= len(lst_choice): # Execute dynamic instance - dct_instance = lst_instance[int_cmd - 1] + dct_instance = lst_choice[int_cmd - 1] self.execute_from_configuration( dct_instance, ) @@ -516,8 +498,8 @@ class TODO: print("Commande non trouvée 🤖!") def prompt_execute_fonction(self): - lst_instance = self.config_file.get_config("function") - help_info = self.fill_help_info(lst_instance) + lst_choice = self.config_file.get_config("function") + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) @@ -528,9 +510,9 @@ class TODO: cmd_no_found = True try: int_cmd = int(status) - if 0 < int_cmd <= len(lst_instance): + if 0 < int_cmd <= len(lst_choice): cmd_no_found = False - dct_instance = lst_instance[int_cmd - 1] + dct_instance = lst_choice[int_cmd - 1] self.execute_from_configuration(dct_instance) except ValueError: pass @@ -538,7 +520,7 @@ class TODO: print("Commande non trouvée 🤖!") def prompt_execute_update(self): - # self.executer_commande_live(f"make {makefile_cmd}") + # self.execute.exec_command_live(f"make {makefile_cmd}") print("🤖 Mise à jour du développement") # TODO détecter les modules en modification pour faire la mise à jour en cours # TODO demander sur quel BD faire la mise à jour @@ -548,34 +530,34 @@ class TODO: # TODO faire la mise à jour de ERPLibre # TODO faire l'upgrade d'un odoo vers un autre - lst_instance = self.config_file.get_config("update_from_makefile") + lst_choice = self.config_file.get_config("update_from_makefile") dct_upgrade_odoo_database = { "prompt_description": "Upgrade Odoo - Migration Database", } - lst_instance.append(dct_upgrade_odoo_database) + lst_choice.append(dct_upgrade_odoo_database) dct_upgrade_poetry = { "prompt_description": "Upgrade Poetry - Dependency of Odoo", } - lst_instance.append(dct_upgrade_poetry) - help_info = self.fill_help_info(lst_instance) + lst_choice.append(dct_upgrade_poetry) + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) print() if status == "0": return False - elif status == str(len(lst_instance) - 1): + elif status == str(len(lst_choice) - 1): upgrade = todo_upgrade.TodoUpgrade(self) upgrade.execute_odoo_upgrade() - elif status == str(len(lst_instance)): + elif status == str(len(lst_choice)): self.upgrade_poetry() else: cmd_no_found = True try: int_cmd = int(status) - 1 - if 0 < int_cmd <= len(lst_instance): + if 0 < int_cmd <= len(lst_choice): cmd_no_found = False - dct_instance = lst_instance[int_cmd - 1] + dct_instance = lst_choice[int_cmd - 1] self.execute_from_configuration(dct_instance) except ValueError: pass @@ -596,27 +578,36 @@ class TODO: # [0] Retour # """ - lst_instance = self.config_file.get_config("code_from_makefile") + lst_choice = self.config_file.get_config("code_from_makefile") + + dct_upgrade_odoo_database = { + "prompt_description": "Open SHELL", + } + lst_choice.append(dct_upgrade_odoo_database) + dct_upgrade_odoo_database = { "prompt_description": "Upgrade Module", } - lst_instance.append(dct_upgrade_odoo_database) - help_info = self.fill_help_info(lst_instance) + lst_choice.append(dct_upgrade_odoo_database) + + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) print() if status == "0": return False - elif status == str(len(lst_instance)): + elif status == str(len(lst_choice)): self.upgrade_module() + elif status == str(len(lst_choice) - 1): + self.open_shell_on_database() else: cmd_no_found = True try: int_cmd = int(status) - if 0 < int_cmd <= len(lst_instance): + if 0 < int_cmd <= len(lst_choice): cmd_no_found = False - dct_instance = lst_instance[int_cmd - 1] + dct_instance = lst_choice[int_cmd - 1] self.execute_from_configuration(dct_instance) except ValueError: pass @@ -625,13 +616,13 @@ class TODO: def prompt_execute_doc(self): print("🤖 Vous cherchez de la documentation?") - lst_instance = [ + lst_choice = [ {"prompt_description": "Migration module coverage"}, {"prompt_description": "What change between version"}, {"prompt_description": "OCA guidelines"}, {"prompt_description": "OCA migration Odoo 19 milestone"}, ] - help_info = self.fill_help_info(lst_instance) + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) @@ -674,13 +665,13 @@ class TODO: def prompt_execute_database(self): print("🤖 Faites des modifications sur les bases de données!") - lst_instance = [ + lst_choice = [ { "prompt_description": "Download database to create backup (.zip)" }, {"prompt_description": "Restore from backup (.zip)"}, ] - help_info = self.fill_help_info(lst_instance) + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) @@ -696,10 +687,10 @@ class TODO: def prompt_execute_process(self): print("🤖 Manipuler les processus d'exécution!") - lst_instance = [ + lst_choice = [ {"prompt_description": "Kill process from actual port"}, ] - help_info = self.fill_help_info(lst_instance) + help_info = self.fill_help_info(lst_choice) while True: status = click.prompt(help_info) @@ -711,6 +702,122 @@ class TODO: else: print("Commande non trouvée 🤖!") + def prompt_execute_config(self): + print("🤖 Manipuler la configuration ERPLibre et Odoo!") + lst_choice = [ + {"prompt_description": "Generate all configuration"}, + {"prompt_description": "Generate from pre-configuration"}, + {"prompt_description": "Generate from backup file"}, + {"prompt_description": "Generate from database"}, + ] + help_info = self.fill_help_info(lst_choice) + + while True: + status = click.prompt(help_info) + print() + if status == "0": + return False + elif status == "1": + self.generate_config() + elif status == "2": + self.generate_config_from_preconfiguration() + elif status == "3": + self.generate_config_from_backup() + elif status == "4": + self.generate_config_from_database() + else: + print("Commande non trouvée 🤖!") + + def generate_config(self, add_arg=None): + # Repeating to get all item before get group + cmd = ( + f"./script/git/git_repo_update_group.py;" + f"./script/generate_config.sh" + ) + if add_arg: + cmd += ( + f";./script/git/git_repo_update_group.py {add_arg};" + f"./script/generate_config.sh" + ) + self.execute.exec_command_live( + cmd, + source_erplibre=False, + single_source_erplibre=True, + ) + + def generate_config_from_preconfiguration(self): + lst_choice = [ + {"prompt_description": "base"}, + {"prompt_description": "base + code_generator"}, + {"prompt_description": "base + image_db"}, + {"prompt_description": "all"}, + # {"prompt_description": "base + migration"}, + ] + help_info = self.fill_help_info(lst_choice) + + while True: + status = click.prompt(help_info) + print() + if status == "0": + return False + elif status == "1": + group = "base" + str_group = f"--group {group}" + self.generate_config(add_arg=str_group) + elif status == "2": + group = "base,code_generator" + str_group = f"--group {group}" + self.generate_config(add_arg=str_group) + elif status == "3": + group = "base,image_db" + str_group = f"--group {group}" + self.generate_config(add_arg=str_group) + elif status == "4": + self.generate_config() + # elif status == "5": + # group = "base,migration" + # str_group = f"--group {group}" + # self.generate_config(add_arg=str_group) + else: + print("Commande non trouvée 🤖!") + + def generate_config_from_backup(self): + file_name = self.open_file_image_db() + add_arg = f"--from_backup_name {file_name} --add_repo odoo18.0/addons/MathBenTech_development" + self.generate_config(add_arg=add_arg) + + def generate_config_from_database(self): + database_name = self.select_database() + str_arg = f"--database {database_name}" + self.generate_config(add_arg=str_arg) + return False + + def select_database(self): + cmd_server = f"./odoo_bin.sh db --list" + status, lst_database = self.execute.exec_command_live( + cmd_server, + return_status_and_output=True, + source_erplibre=False, + single_source_erplibre=True, + ) + lst_choice = [{"prompt_description": a.strip()} for a in lst_database] + + help_info = self.fill_help_info(lst_choice) + + lst_str_choice = [str(a) for a in range(len(lst_choice) + 1) if a] + + while True: + status = click.prompt(help_info) + print() + if status == "0": + return False + elif status in lst_str_choice: + database_name = lst_database[int(status) - 1].strip() + print(database_name) + return database_name + else: + print("Commande non trouvée 🤖!") + def get_odoo_version(self): with open(VERSION_DATA_FILE) as txt: data_version = json.load(txt) @@ -776,13 +883,13 @@ class TODO: # f'parallel ::: "./run.sh -d {bd}" "sleep' # f' 3;./script/selenium/web_login.py{extra_cmd_web_login}"' # ) - # self.executer_commande_live(cmd) + # self.execute.exec_command_live(cmd) cmd_server = f"./run.sh -d {bd};bash" - self.executer_commande_live(cmd_server) + self.execute.exec_command_live(cmd_server) cmd_client = ( f"sleep 3;./script/selenium/web_login.py{extra_cmd_web_login};bash" ) - self.executer_commande_live(cmd_client) + self.execute.exec_command_live(cmd_client) def prompt_execute_selenium(self, command=None, extra_cmd_web_login=""): lst_cmd = [] @@ -798,132 +905,12 @@ class TODO: lst_cmd.append(cmd + extra_cmd_web_login) if len(lst_cmd) == 1: - self.executer_commande_live(lst_cmd[0]) + self.execute.exec_command_live(lst_cmd[0]) elif len(lst_cmd) > 1: new_cmd = "parallel ::: " for i, cmd in enumerate(lst_cmd): new_cmd += f' "sleep {1 * i};{cmd}"' - self.executer_commande_live(new_cmd) - - def executer_commande_live( - self, - commande, - source_erplibre=True, - quiet=False, - single_source_erplibre=False, - new_window=False, - single_source_odoo=False, - source_odoo="", - new_env=None, - return_status_and_command=False, - return_status_and_output=False, - return_status_and_output_and_command=False, - ): - """ - Exécute une commande et affiche la sortie en direct. - - Args: - commande (str): La commande à exécuter (sous forme de chaîne de caractères). - """ - - my_env = os.environ.copy() - if new_env: - my_env.update(new_env) - - process_start_time = time.time() - return_status = None - if source_erplibre: - # commande = f"source ./{cst_venv_erplibre}/bin/activate && " + commande - # cmd = ( - # f"gnome-terminal --tab -- bash -c 'source" - # f" ./{cst_venv_erplibre}/bin/activate;{commande}'" - # ) - commande = self.cmd_source_erplibre % commande - # os.system(f"./script/terminal/open_terminal.sh {commande}") - elif single_source_erplibre: - commande = ( - f"source ./{cst_venv_erplibre}/bin/activate && %s" % commande - ) - elif single_source_odoo: - if not source_odoo and os.path.exists("./.erplibre-version"): - with open("./.erplibre-version") as f: - source_odoo = f.read() - if not source_odoo: - _logger.error( - f"You cannot execute Odoo command if no version is installed. Command : {commande}" - ) - return -1 - commande = ( - f"source ./.venv.{source_odoo}/bin/activate && {commande}" - ) - elif new_window and self.cmd_source_default: - commande = self.cmd_source_default % commande - - print("🏠 ⬇ Execute command :") - print(commande) - lst_output = [] - - try: - process = subprocess.Popen( - commande, - shell=True, - executable="/bin/bash", - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, # Désactive la mise en tampon pour la sortie en direct - universal_newlines=True, # Pour traiter les sauts de lignes correctement - env=my_env, - ) - - while True: - ligne = process.stdout.readline() - if not ligne: - break - print(ligne, end="") - if ( - return_status_and_output - or return_status_and_output_and_command - ): - lst_output.append(ligne) - - process.wait() # Attendre la fin du process - return_status = process.returncode - if process.returncode != 0 and not quiet: - print( - "La commande a retourné un code d'erreur :" - f" {process.returncode}" - ) - - except FileNotFoundError: - if "password" in commande: - print( - f"Erreur : La commande '{commande.split(' ')[0]}'[...] n'a" - " pas été trouvée." - ) - else: - print( - f"Erreur : La commande '{commande}' n'a pas été trouvée." - ) - except Exception as e: - print(f"Une erreur s'est produite : {e}") - process_end_time = time.time() - duration_sec = process_end_time - process_start_time - if humanize: - duration_delta = datetime.timedelta(seconds=duration_sec) - humain_time = humanize.precisedelta(duration_delta) - print(f"🏠 ⬆ Executed ({humain_time}) :") - else: - print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :") - print(commande) - print() - if return_status_and_output_and_command: - return return_status, commande, lst_output - if return_status_and_command: - return return_status, commande - if return_status_and_output: - return return_status, lst_output - return return_status + self.execute.exec_command_live(new_cmd) def crash_diagnostic(self, e): # TODO show message at start if os.path.exists(file_error_path) @@ -948,7 +935,7 @@ class TODO: time.sleep(0.5) cmd = "./script/todo/source_todo.sh" # self.restart_script(e) - self.executer_commande_live(cmd, source_erplibre=True) + self.execute.exec_command_live(cmd, source_erplibre=True) sys.exit(1) if os.path.exists(cst_venv_erplibre): print("Import error : ") @@ -980,19 +967,30 @@ class TODO: from pykeepass import PyKeePass except ImportError: print("Rerun and exit") - self.executer_commande_live(cmd, source_erplibre=True) + self.execute.exec_command_live(cmd, source_erplibre=True) sys.exit(1) print("No error") else: self.prompt_install() + def open_shell_on_database(self): + database = self.select_database() + cmd_server = f"./odoo_bin.sh shell -d {database}" + status, lst_database = self.execute.exec_command_live( + cmd_server, + return_status_and_output=True, + source_erplibre=False, + single_source_erplibre=True, + new_window=True, + ) + def upgrade_module(self): upgrade = todo_upgrade.TodoUpgrade(self) upgrade.execute_module_upgrade() def upgrade_poetry(self): # Only show the version to the user - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"make version", source_erplibre=False, ) @@ -1001,7 +999,7 @@ class TODO: "💬 Would you like to fetch all your git repositories, you need it (y/Y) : " ) if git_repo_update_input.strip().lower() == "y": - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"./script/manifest/update_manifest_local_dev.sh", source_erplibre=False, ) @@ -1022,7 +1020,7 @@ class TODO: except Exception as e: pass - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"pip install -r requirement/erplibre_require-ments-poetry.txt && " f"./script/poetry/poetry_update.py -f", source_erplibre=False, @@ -1042,20 +1040,13 @@ class TODO: if status == "1": file_name = status else: - self.dir_path = "" - - file_browser = todo_file_browser.FileBrowser( - path_image_db, self.on_dir_selected - ) - file_browser.run_main_frame() - file_name = os.path.basename(self.dir_path) - print(file_name) + file_name = self.open_file_image_db database_name = input("💬 Database name : ") if not database_name: _logger.error("Missing database name") return - status, lst_output = self.executer_commande_live( + status, lst_output = self.execute.exec_command_live( f"python3 ./script/database/db_restore.py -d {database_name} --ignore_cache --image {file_name}", return_status_and_output=True, single_source_erplibre=True, @@ -1067,7 +1058,7 @@ class TODO: .lower() ) if status == "y": - status, lst_output = self.executer_commande_live( + status, lst_output = self.execute.exec_command_live( f"./script/addons/update_prod_to_dev.sh {database_name}", return_status_and_output=True, single_source_erplibre=True, @@ -1079,19 +1070,32 @@ class TODO: .lower() ) if status == "y": - status, lst_output = self.executer_commande_live( + status, lst_output = self.execute.exec_command_live( f"./script/addons/update_addons_all.sh {database_name}", return_status_and_output=True, single_source_erplibre=True, source_erplibre=False, ) + def open_file_image_db(self): + self.dir_path = "" + path_image_db = os.path.join(os.getcwd(), "image_db") + + # self.dir_path is over-write into on_dir_selected + file_browser = todo_file_browser.FileBrowser( + path_image_db, self.on_dir_selected + ) + file_browser.run_main_frame() + file_name = os.path.basename(self.dir_path) + print(file_name) + return file_name + def process_kill_from_port(self): cfg = configparser.ConfigParser() cfg.read("./config.conf") http_port = cfg.getint("options", "http_port") - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"./script/process/kill_process_by_port.py {http_port} --kill-tree --nb_parent 2", source_erplibre=False, ) @@ -1099,7 +1103,7 @@ class TODO: def download_database_backup_cli(self, show_remote_list=True): database_domain = input("Domain Odoo (ex. https://mondomain.com) : ") if show_remote_list: - status, lst_output = self.executer_commande_live( + status, lst_output = self.execute.exec_command_live( f"python3 ./script/database/list_remote.py --raw --odoo-url {database_domain}", return_status_and_output=True, single_source_erplibre=True, @@ -1134,7 +1138,7 @@ class TODO: my_env["DATABASE_NAME"] = database_name my_env["OUTPUT_FILE_PATH"] = output_path my_env["ODOO_URL"] = database_domain - status, cmd_executed = self.executer_commande_live( + status, cmd_executed = self.execute.exec_command_live( cmd, source_erplibre=False, return_status_and_command=True, @@ -1265,7 +1269,7 @@ class TODO: # Rename with script bash cmd_client = f'cd {MOBILE_HOME_PATH} && npx cap init "{project_name}" "{package_name}" && ./rename_android.sh "{project_name}" "{package_name}" && npx cap sync android' - self.executer_commande_live(cmd_client, source_erplibre=False) + self.execute.exec_command_live(cmd_client, source_erplibre=False) # dotenv_mobile = dotenv.dotenv_values(dotenv_file) # dotenv_mobile["VITE_TITLE"] = project_name @@ -1293,7 +1297,7 @@ class TODO: ) if do_change_picture_menu: - status = self.executer_commande_live( + status = self.execute.exec_command_live( f"cd {MOBILE_HOME_PATH} && npx cap open android;bash", source_erplibre=False, new_window=True, @@ -1305,11 +1309,11 @@ class TODO: "Did you finish to update image with Android-Studio ? Press to continue ..." ) cmd_client = "cp ./mobile/erplibre_home_mobile/android/app/src/main/ic_launcher-playstore.png ./mobile/erplibre_home_mobile/src/assets/company_logo.png" - self.executer_commande_live(cmd_client, source_erplibre=False) + self.execute.exec_command_live(cmd_client, source_erplibre=False) cmd_client = "cp ./mobile/erplibre_home_mobile/android/app/src/main/ic_launcher-playstore.png ./mobile/erplibre_home_mobile/src/assets/imgs/logo.png" - self.executer_commande_live(cmd_client, source_erplibre=False) + self.execute.exec_command_live(cmd_client, source_erplibre=False) - status = self.executer_commande_live( + status = self.execute.exec_command_live( "./mobile/compile_and_run.sh", source_erplibre=False ) diff --git a/script/todo/todo_upgrade.py b/script/todo/todo_upgrade.py index 4fb898c..9742be7 100755 --- a/script/todo/todo_upgrade.py +++ b/script/todo/todo_upgrade.py @@ -19,6 +19,7 @@ new_path = os.path.normpath( ) sys.path.append(new_path) +from script.execute import execute from script.git.git_tool import GitTool _logger = logging.getLogger(__name__) @@ -52,6 +53,7 @@ class TodoUpgrade: self.lst_command_executed = [] self.dct_module_per_version = {} self.dct_module_per_dct_version_path = {} + self.execute = execute.Execute() def write_config(self): if "date_create" not in self.dct_progression.keys(): @@ -1857,7 +1859,7 @@ class TodoUpgrade: get_output = True output = None if get_output: - status, cmd_executed, output = self.todo.executer_commande_live( + status, cmd_executed, output = self.execute.exec_command_live( cmd, source_erplibre=False, single_source_odoo=single_source_odoo, @@ -1866,7 +1868,7 @@ class TodoUpgrade: quiet=quiet, ) else: - status, cmd_executed = self.todo.executer_commande_live( + status, cmd_executed = self.execute.exec_command_live( cmd, source_erplibre=False, single_source_odoo=single_source_odoo,