From 340a55e68533d4be5a66ed6b56ba5e4b43c81ad1 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Sun, 22 Mar 2026 22:21:16 -0400 Subject: [PATCH 1/4] [IMP] todo: git patch --- script/todo/todo.json | 4 ++++ script/todo/todo_i18n.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/script/todo/todo.json b/script/todo/todo.json index 27255b0..5a052f9 100644 --- a/script/todo/todo.json +++ b/script/todo/todo.json @@ -56,6 +56,10 @@ { "prompt_description_key": "Configure git local editor to vim", "bash_command": "git config --global core.editor \"vim\"" + }, + { + "prompt_description_key": "Generate git patch to /tmp", + "bash_command": "PATCH=\"/tmp/patch_$(date +%Y%m%d_%H%M%S).patch\"; git -C \"$(pwd)\" diff HEAD > \"$PATCH\" && printf \"\\n✓ Patch created: %s\\n\\n=== Guide to apply the patch ===\\n Check compatibility : git apply --check %s\\n Apply (git) : git apply %s\\n Apply (patch) : patch -p1 < %s\\n Revert (git) : git apply -R %s\\n\" \"$PATCH\" \"$PATCH\" \"$PATCH\" \"$PATCH\" \"$PATCH\"" } ] } diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index 1e9846e..ec9ace0 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -617,6 +617,10 @@ TRANSLATIONS = { "fr": "Configuration git local par vim", "en": "Configure git local editor to vim", }, + "Generate git patch to /tmp": { + "fr": "Générer une patch git dans /tmp", + "en": "Generate git patch to /tmp", + }, "Git editor configured to vim successfully!": { "fr": "Éditeur git configuré sur vim avec succès!", "en": "Git editor configured to vim successfully!", From c8025eb2a089468745385ff07a6342b9c5a6b361 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Sun, 22 Mar 2026 22:28:24 -0400 Subject: [PATCH 2/4] [IMP] todo: add sshfs configuration command Add interactive sshfs mount setup to the system configuration menu. Users can enter SSH host details manually or select from ~/.ssh/config, then configure a persistent mount with fstab. Includes i18n strings for all new UI text. Generated by Claude Code 2.1.81 model claude-sonnet-4-6 --- script/todo/todo.py | 107 +++++++++++++++++++++++++++++++++++++++ script/todo/todo_i18n.py | 56 ++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/script/todo/todo.py b/script/todo/todo.py index b099569..9bc2923 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -619,6 +619,7 @@ class TODO: print(f"🤖 {t('Deploy ERPLibre to a local directory!')}") choices = [ {"prompt_description": t("Clone ERPLibre locally (git clone)")}, + {"prompt_description": t("Configure sshfs")}, ] help_info = self.fill_help_info(choices) @@ -629,6 +630,8 @@ class TODO: return False elif status == "1": self._deploy_clone_erplibre() + elif status == "2": + self._configure_sshfs() else: print(t("Command not found !")) @@ -655,6 +658,110 @@ class TODO: except Exception as e: print(f"{t('Error cloning ERPLibre: ')}{e}") + def _configure_sshfs(self): + import getpass + import re + from datetime import datetime + + print(f"\n{t('SSH address input method')}") + print(f"[1] {t('Manual entry')}") + print(f"[2] {t('From ~/.ssh/config')}") + choice = input(t("Your choice (1/2): ")).strip() + + user = None + hostname = None + ssh_name = None + + if choice == "2": + ssh_config_path = os.path.expanduser("~/.ssh/config") + hosts = [] + if os.path.exists(ssh_config_path): + current_host = None + current_info = {} + with open(ssh_config_path) as f: + for line in f: + line = line.strip() + if line.lower().startswith("host "): + host_val = line.split(None, 1)[1].strip() + if host_val != "*": + if current_host: + hosts.append((current_host, current_info)) + current_host = host_val + current_info = {} + elif current_host: + key = line.split(None, 1) + if len(key) == 2: + k = key[0].lower() + v = key[1].strip() + if k == "hostname": + current_info["hostname"] = v + elif k == "user": + current_info["user"] = v + if current_host: + hosts.append((current_host, current_info)) + + if not hosts: + print(t("No SSH hosts found in ~/.ssh/config")) + return + + print() + for i, (host, info) in enumerate(hosts, 1): + hn = info.get("hostname", host) + u = info.get("user", "") + desc = host + if hn != host: + desc += f" ({hn})" + if u: + desc += f" [{u}]" + print(f"[{i}] {desc}") + + sel = input(t("Select SSH host number: ")).strip() + try: + idx = int(sel) - 1 + if idx < 0 or idx >= len(hosts): + print(t("Invalid selection!")) + return + except ValueError: + print(t("Invalid selection!")) + return + + host_name, host_info = hosts[idx] + hostname = host_info.get("hostname", host_name) + user = host_info.get("user", getpass.getuser()) + ssh_name = host_name + target = f"{host_name}:/" + else: + ssh_host = input( + t("SSH host (e.g.: user@192.168.1.100): ") + ).strip() + if not ssh_host: + print(t("SSH host is required!")) + return + if "@" in ssh_host: + user, hostname = ssh_host.split("@", 1) + else: + hostname = ssh_host + user = getpass.getuser() + ssh_name = hostname + target = f"{user}@{hostname}:/" + + safe_name = re.sub(r"[^a-zA-Z0-9_-]", "_", ssh_name) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + mount_point = f"/tmp/sshfs_{safe_name}_{timestamp}" + os.makedirs(mount_point, exist_ok=True) + + cmd = f"sshfs {target} {mount_point}" + print(f"{t('Mounting sshfs on: ')}{mount_point}") + print(f"{t('Will execute:')} {cmd}") + try: + self.execute.exec_command_live(cmd, source_erplibre=False) + print(f"{t('Mounted on: ')}{mount_point}") + print(f"mount | grep sshfs") + print(f"{t('To unmount: ')}" f"fusermount -u {mount_point}") + print(f"nautilus {mount_point}/home/{user}") + except Exception as e: + print(f"{t('Error mounting sshfs: ')}{e}") + def prompt_execute_code(self): print(f"🤖 {t('What do you need for development?')}") # help_info = """Commande : diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index ec9ace0..5922b4c 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -137,6 +137,62 @@ TRANSLATIONS = { "fr": "Erreur lors du clonage d'ERPLibre : ", "en": "Error cloning ERPLibre: ", }, + "Configure sshfs": { + "fr": "Configurer sshfs", + "en": "Configure sshfs", + }, + "SSH address input method": { + "fr": "Méthode de saisie de l'adresse SSH", + "en": "SSH address input method", + }, + "Manual entry": { + "fr": "Saisie manuelle", + "en": "Manual entry", + }, + "From ~/.ssh/config": { + "fr": "Depuis ~/.ssh/config", + "en": "From ~/.ssh/config", + }, + "Your choice (1/2): ": { + "fr": "Votre choix (1/2) : ", + "en": "Your choice (1/2): ", + }, + "No SSH hosts found in ~/.ssh/config": { + "fr": "Aucun hôte SSH trouvé dans ~/.ssh/config", + "en": "No SSH hosts found in ~/.ssh/config", + }, + "Select SSH host number: ": { + "fr": "Numéro de l'hôte SSH à sélectionner : ", + "en": "Select SSH host number: ", + }, + "Invalid selection!": { + "fr": "Sélection invalide!", + "en": "Invalid selection!", + }, + "SSH host (e.g.: user@192.168.1.100): ": { + "fr": "Hôte SSH (ex: user@192.168.1.100) : ", + "en": "SSH host (e.g.: user@192.168.1.100): ", + }, + "SSH host is required!": { + "fr": "L'hôte SSH est requis!", + "en": "SSH host is required!", + }, + "Mounting sshfs on: ": { + "fr": "Montage sshfs sur : ", + "en": "Mounting sshfs on: ", + }, + "Mounted on: ": { + "fr": "Monté sur : ", + "en": "Mounted on: ", + }, + "To unmount: ": { + "fr": "Pour démonter : ", + "en": "To unmount: ", + }, + "Error mounting sshfs: ": { + "fr": "Erreur lors du montage sshfs : ", + "en": "Error mounting sshfs: ", + }, # RTK (Rust Token Killer) "Manage RTK (Rust Token Killer) for token optimization!": { "fr": "Gérer RTK (Rust Token Killer) pour optimiser les tokens!", From 9f2068133de6dd5c8886f0d62f1c2f700e08dd69 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Mon, 23 Mar 2026 02:59:29 -0400 Subject: [PATCH 3/4] [ADD] mobile test script --- mobile/run_tests.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 mobile/run_tests.sh diff --git a/mobile/run_tests.sh b/mobile/run_tests.sh new file mode 100755 index 0000000..d790193 --- /dev/null +++ b/mobile/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +if [[ ! -d "./mobile/erplibre_home_mobile" ]]; then + echo "Please, run installation ./mobile/install_mobile_dev.sh before run this script ./mobile/run_tests.sh" + exit 1 +fi + +cd mobile/erplibre_home_mobile + +npm install +npm test + +cd - From 4972add36f746494c6a76b5491902271fa11d253 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Thu, 26 Mar 2026 15:45:44 -0400 Subject: [PATCH 4/4] [FIX] todo upgrade wrong method get_odoo_version --- script/todo/todo_upgrade.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/script/todo/todo_upgrade.py b/script/todo/todo_upgrade.py index 0c806c9..a83f199 100755 --- a/script/todo/todo_upgrade.py +++ b/script/todo/todo_upgrade.py @@ -10,6 +10,7 @@ import shutil import sys import zipfile from uuid import uuid4 +from script.todo.version_manager import get_odoo_version import click import todo_file_browser @@ -530,7 +531,7 @@ class TodoUpgrade: # print("What is your actual Odoo version?") lst_version, lst_version_installed, odoo_installed_version = ( - self.todo.get_odoo_version() + get_odoo_version() ) lst_odoo_version = [ @@ -1815,7 +1816,7 @@ class TodoUpgrade: # Expect odoo_version like 12.0 lst_version, lst_version_installed, odoo_installed_version = ( - self.todo.get_odoo_version() + get_odoo_version() ) if odoo_installed_version != f"odoo{int_odoo_version}.0": print( @@ -1838,7 +1839,7 @@ class TodoUpgrade: # if os.path.exists(venv_oca_path): # return lst_version, lst_version_installed, odoo_installed_version = ( - self.todo.get_odoo_version() + get_odoo_version() ) extract_version = f"{next_version}.0" dct_erplibre_info = [