diff --git a/kiauh/components/klipper/klipper_utils.py b/kiauh/components/klipper/klipper_utils.py index 51dd8bd..a4423fb 100644 --- a/kiauh/components/klipper/klipper_utils.py +++ b/kiauh/components/klipper/klipper_utils.py @@ -30,7 +30,7 @@ from components.klipper.klipper_dialogs import ( ) from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker_utils import moonraker_to_multi_conversion -from components.webui_client import ClientData +from components.webui_client.base_data import BaseWebClient from components.webui_client.client_config.client_config_setup import ( create_client_config_symlink, ) @@ -288,7 +288,7 @@ def get_highest_index(instance_list: List[Klipper]) -> int: def create_example_printer_cfg( - instance: Klipper, clients: Optional[List[ClientData]] = None + instance: Klipper, clients: Optional[List[BaseWebClient]] = None ) -> None: Logger.print_status(f"Creating example printer.cfg in '{instance.cfg_dir}'") if instance.cfg_file.is_file(): @@ -309,8 +309,8 @@ def create_example_printer_cfg( # include existing client configs in the example config if clients is not None and len(clients) > 0: for c in clients: - client_config = c.get("client_config") - section = client_config.get("printer_cfg_section") + client_config = c.client_config + section = client_config.config_section cm.config.add_section(section=section) create_client_config_symlink(client_config, [instance]) diff --git a/kiauh/components/moonraker/moonraker_setup.py b/kiauh/components/moonraker/moonraker_setup.py index ef8c5f6..100ae9e 100644 --- a/kiauh/components/moonraker/moonraker_setup.py +++ b/kiauh/components/moonraker/moonraker_setup.py @@ -11,11 +11,11 @@ import subprocess import sys from pathlib import Path -from components.webui_client import MAINSAIL_DIR from components.webui_client.client_utils import ( enable_mainsail_remotemode, get_existing_clients, ) +from components.webui_client.mainsail_data import MainsailData from kiauh import KIAUH_CFG from components.klipper.klipper import Klipper from components.moonraker import ( @@ -113,7 +113,7 @@ def install_moonraker() -> None: # if mainsail is installed, and we installed # multiple moonraker instances, we enable mainsails remote mode - if MAINSAIL_DIR.exists() and len(mr_im.instances) > 1: + if MainsailData().client_dir.exists() and len(mr_im.instances) > 1: enable_mainsail_remotemode() diff --git a/kiauh/components/moonraker/moonraker_utils.py b/kiauh/components/moonraker/moonraker_utils.py index f8be7be..ab63d49 100644 --- a/kiauh/components/moonraker/moonraker_utils.py +++ b/kiauh/components/moonraker/moonraker_utils.py @@ -19,8 +19,9 @@ from components.moonraker import ( MOONRAKER_DB_BACKUP_DIR, ) from components.moonraker.moonraker import Moonraker -from components.webui_client import MAINSAIL_DIR, ClientData +from components.webui_client.base_data import BaseWebClient from components.webui_client.client_utils import enable_mainsail_remotemode +from components.webui_client.mainsail_data import MainsailData from core.backup_manager.backup_manager import BackupManager from core.config_manager.config_manager import ConfigManager from core.instance_manager.instance_manager import InstanceManager @@ -52,7 +53,7 @@ def get_moonraker_status() -> ( def create_example_moonraker_conf( instance: Moonraker, ports_map: Dict[str, int], - clients: Optional[List[ClientData]] = None, + clients: Optional[List[BaseWebClient]] = None, ) -> None: Logger.print_status(f"Creating example moonraker.conf in '{instance.cfg_dir}'") if instance.cfg_file.is_file(): @@ -100,26 +101,26 @@ def create_example_moonraker_conf( if clients is not None and len(clients) > 0: for c in clients: # client part - c_section = f"update_manager {c.get('name')}" + c_section = f"update_manager {c.name}" c_options = [ ("type", "web"), ("channel", "stable"), - ("repo", c.get("mr_conf_repo")), - ("path", c.get("mr_conf_path")), + ("repo", c.repo_path), + ("path", c.client_dir), ] cm.config.add_section(section=c_section) for option in c_options: cm.config.set(c_section, option[0], option[1]) # client config part - c_config = c.get("client_config") - if c_config.get("dir").exists(): - c_config_section = f"update_manager {c_config.get('name')}" + c_config = c.client_config + if c_config.config_dir.exists(): + c_config_section = f"update_manager {c_config.name}" c_config_options = [ ("type", "git_repo"), ("primary_branch", "master"), - ("path", c_config.get("mr_conf_path")), - ("origin", c_config.get("mr_conf_origin")), + ("path", c_config.config_dir), + ("origin", c_config.repo_url), ("managed_services", "klipper"), ] cm.config.add_section(section=c_config_section) @@ -177,7 +178,7 @@ def moonraker_to_multi_conversion(new_name: str) -> None: im.start_instance() # if mainsail is installed, we enable mainsails remote mode - if MAINSAIL_DIR.exists() and len(im.instances) > 1: + if MainsailData().client_dir.exists() and len(im.instances) > 1: enable_mainsail_remotemode() diff --git a/kiauh/components/webui_client/__init__.py b/kiauh/components/webui_client/__init__.py index bb87c0e..e69de29 100644 --- a/kiauh/components/webui_client/__init__.py +++ b/kiauh/components/webui_client/__init__.py @@ -1,75 +0,0 @@ -# ======================================================================= # -# Copyright (C) 2020 - 2024 Dominik Willner # -# # -# This file is part of KIAUH - Klipper Installation And Update Helper # -# https://github.com/dw-0/kiauh # -# # -# This file may be distributed under the terms of the GNU GPLv3 license # -# ======================================================================= # - -from pathlib import Path -from typing import Literal, TypedDict - -from core.backup_manager import BACKUP_ROOT_DIR - -MODULE_PATH = Path(__file__).resolve().parent - -########### -# MAINSAIL -########### -MAINSAIL_DIR = Path.home().joinpath("mainsail") -MAINSAIL_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mainsail-backups") -MAINSAIL_CONFIG_DIR = Path.home().joinpath("mainsail-config") -MAINSAIL_CONFIG_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mainsail-config-backups") -MAINSAIL_CONFIG_REPO_URL = "https://github.com/mainsail-crew/mainsail-config.git" -MAINSAIL_CONFIG_JSON = MAINSAIL_DIR.joinpath("config.json") -MAINSAIL_URL = ( - "https://github.com/mainsail-crew/mainsail/releases/latest/download/mainsail.zip" -) -MAINSAIL_PRE_RLS_URL = ( - "https://github.com/mainsail-crew/mainsail/releases/download/%TAG%/mainsail.zip" -) -MAINSAIL_TAGS_URL = "https://api.github.com/repos/mainsail-crew/mainsail/tags" - -######### -# FLUIDD -######### -FLUIDD_DIR = Path.home().joinpath("fluidd") -FLUIDD_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("fluidd-backups") -FLUIDD_CONFIG_DIR = Path.home().joinpath("fluidd-config") -FLUIDD_CONFIG_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("fluidd-config-backups") -FLUIDD_CONFIG_REPO_URL = "https://github.com/fluidd-core/fluidd-config.git" -FLUIDD_URL = "https://github.com/fluidd-core/fluidd/releases/latest/download/fluidd.zip" -FLUIDD_PRE_RLS_URL = ( - "https://github.com/fluidd-core/fluidd/releases/download/%TAG%/fluidd.zip" -) -FLUIDD_TAGS_URL = "https://api.github.com/repos/fluidd-core/fluidd/tags" - -ClientName = Literal["mainsail", "fluidd"] -ClientConfigName = Literal["mainsail-config", "fluidd-config"] - - -class ClientData(TypedDict): - name: ClientName - display_name: str - dir: Path - backup_dir: Path - url: str - pre_release_url: str - tags_url: str - remote_mode: bool # required only for Mainsail - mr_conf_repo: str - mr_conf_path: str - client_config: "ClientConfigData" - - -class ClientConfigData(TypedDict): - name: ClientConfigName - display_name: str - cfg_filename: str - dir: Path - backup_dir: Path - url: str - printer_cfg_section: str - mr_conf_path: str - mr_conf_origin: str diff --git a/kiauh/components/webui_client/base_data.py b/kiauh/components/webui_client/base_data.py new file mode 100644 index 0000000..0fe34da --- /dev/null +++ b/kiauh/components/webui_client/base_data.py @@ -0,0 +1,117 @@ +# ======================================================================= # +# Copyright (C) 2020 - 2024 Dominik Willner # +# # +# This file is part of KIAUH - Klipper Installation And Update Helper # +# https://github.com/dw-0/kiauh # +# # +# This file may be distributed under the terms of the GNU GPLv3 license # +# ======================================================================= # + +from __future__ import annotations + +from abc import ABC, abstractmethod +from enum import Enum +from pathlib import Path + + +class WebClientType(Enum): + MAINSAIL: str = "mainsail" + FLUIDD: str = "fluidd" + + +class WebClientConfigType(Enum): + MAINSAIL: str = "mainsail-config" + FLUIDD: str = "fluidd-config" + + +class BaseWebClient(ABC): + """Base class for webclient data""" + + @property + @abstractmethod + def client(self) -> WebClientType: + raise NotImplementedError + + @property + @abstractmethod + def name(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def display_name(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def client_dir(self) -> Path: + raise NotImplementedError + + @property + @abstractmethod + def backup_dir(self) -> Path: + raise NotImplementedError + + @property + @abstractmethod + def repo_path(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def stable_url(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def unstable_url(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def client_config(self) -> BaseWebClientConfig: + raise NotImplementedError + + +class BaseWebClientConfig(ABC): + """Base class for webclient config data""" + + @property + @abstractmethod + def client_config(self) -> WebClientConfigType: + raise NotImplementedError + + @property + @abstractmethod + def name(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def display_name(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def config_filename(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def config_dir(self) -> Path: + raise NotImplementedError + + @property + @abstractmethod + def backup_dir(self) -> Path: + raise NotImplementedError + + @property + @abstractmethod + def repo_url(self) -> str: + raise NotImplementedError + + @property + @abstractmethod + def config_section(self) -> str: + raise NotImplementedError diff --git a/kiauh/components/webui_client/client_config/client_config_remove.py b/kiauh/components/webui_client/client_config/client_config_remove.py index e61c313..6a5a014 100644 --- a/kiauh/components/webui_client/client_config/client_config_remove.py +++ b/kiauh/components/webui_client/client_config/client_config_remove.py @@ -14,26 +14,26 @@ from typing import List from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker -from components.webui_client import ClientConfigData +from components.webui_client.base_data import BaseWebClientConfig from core.instance_manager.instance_manager import InstanceManager from utils.filesystem_utils import remove_file, remove_config_section from utils.logger import Logger def run_client_config_removal( - client_config: ClientConfigData, + client_config: BaseWebClientConfig, kl_instances: List[Klipper], mr_instances: List[Moonraker], ) -> None: remove_client_config_dir(client_config) remove_client_config_symlink(client_config) - remove_config_section(f"update_manager {client_config.get('name')}", mr_instances) - remove_config_section(client_config.get("printer_cfg_section"), kl_instances) + remove_config_section(f"update_manager {client_config.name}", mr_instances) + remove_config_section(client_config.config_section, kl_instances) -def remove_client_config_dir(client_config: ClientConfigData) -> None: - Logger.print_status(f"Removing {client_config.get('name')} ...") - client_config_dir = client_config.get("dir") +def remove_client_config_dir(client_config: BaseWebClientConfig) -> None: + Logger.print_status(f"Removing {client_config.name} ...") + client_config_dir = client_config.config_dir if not client_config_dir.exists(): Logger.print_info(f"'{client_config_dir}' does not exist. Skipping ...") return @@ -44,12 +44,12 @@ def remove_client_config_dir(client_config: ClientConfigData) -> None: Logger.print_error(f"Unable to delete '{client_config_dir}':\n{e}") -def remove_client_config_symlink(client_config: ClientConfigData) -> None: +def remove_client_config_symlink(client_config: BaseWebClientConfig) -> None: im = InstanceManager(Klipper) instances: List[Klipper] = im.instances for instance in instances: Logger.print_status(f"Removing symlink from '{instance.cfg_dir}' ...") - symlink = instance.cfg_dir.joinpath(client_config.get("cfg_filename")) + symlink = instance.cfg_dir.joinpath(client_config.config_filename) if not symlink.is_symlink(): Logger.print_info(f"'{symlink}' does not exist. Skipping ...") continue diff --git a/kiauh/components/webui_client/client_config/client_config_setup.py b/kiauh/components/webui_client/client_config/client_config_setup.py index 58d2f3b..348069d 100644 --- a/kiauh/components/webui_client/client_config/client_config_setup.py +++ b/kiauh/components/webui_client/client_config/client_config_setup.py @@ -12,15 +12,14 @@ import subprocess from pathlib import Path from typing import List +from components.webui_client.base_data import BaseWebClient, BaseWebClientConfig from kiauh import KIAUH_CFG from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker -from components.webui_client import ClientConfigData, ClientName, ClientData from components.webui_client.client_dialogs import ( print_client_already_installed_dialog, ) from components.webui_client.client_utils import ( - load_client_data, backup_client_config_data, config_for_other_client_exist, ) @@ -38,19 +37,18 @@ from utils.input_utils import get_confirm from utils.logger import Logger -def install_client_config(client_name: ClientName) -> None: - client: ClientData = load_client_data(client_name) - client_config: ClientConfigData = client.get("client_config") - d_name = client_config.get("display_name") +def install_client_config(client_data: BaseWebClient) -> None: + client_config: BaseWebClientConfig = client_data.client_config + display_name = client_config.display_name - if config_for_other_client_exist(client_name): + if config_for_other_client_exist(client_data.client): Logger.print_info("Another Client-Config is already installed! Skipped ...") return - if client_config.get("dir").exists(): - print_client_already_installed_dialog(d_name) - if get_confirm(f"Re-install {d_name}?", allow_go_back=True): - shutil.rmtree(client_config.get("dir")) + if client_config.config_dir.exists(): + print_client_already_installed_dialog(display_name) + if get_confirm(f"Re-install {display_name}?", allow_go_back=True): + shutil.rmtree(client_config.config_dir) else: return @@ -66,69 +64,74 @@ def install_client_config(client_name: ClientName) -> None: backup_printer_config_dir() add_config_section( - section=f"update_manager {client_config.get('name')}", + section=f"update_manager {client_config.name}", instances=mr_instances, options=[ ("type", "git_repo"), ("primary_branch", "master"), - ("path", client_config.get("mr_conf_path")), - ("origin", client_config.get("mr_conf_origin")), + ("path", str(client_config.config_dir)), + ("origin", str(client_config.repo_url)), ("managed_services", "klipper"), ], ) - add_config_section_at_top( - client_config.get("printer_cfg_section"), kl_instances - ) + add_config_section_at_top(client_config.config_section, kl_instances) kl_im.restart_all_instance() except Exception as e: - Logger.print_error(f"{d_name} installation failed!\n{e}") + Logger.print_error(f"{display_name} installation failed!\n{e}") return - Logger.print_ok(f"{d_name} installation complete!", start="\n") + Logger.print_ok(f"{display_name} installation complete!", start="\n") -def download_client_config(client_config: ClientConfigData) -> None: +def download_client_config(client_config: BaseWebClientConfig) -> None: try: - Logger.print_status(f"Downloading {client_config.get('display_name')} ...") + Logger.print_status(f"Downloading {client_config.display_name} ...") rm = RepoManager( - client_config.get("url"), target_dir=str(client_config.get("dir")) + client_config.repo_url, + target_dir=str(client_config.config_dir), ) rm.clone_repo() except Exception: - Logger.print_error(f"Downloading {client_config.get('display_name')} failed!") + Logger.print_error(f"Downloading {client_config.display_name} failed!") raise -def update_client_config(client: ClientData) -> None: - client_config: ClientConfigData = client.get("client_config") +def update_client_config(client: BaseWebClient) -> None: + client_config: BaseWebClientConfig = client.client_config - Logger.print_status(f"Updating {client_config.get('display_name')} ...") + Logger.print_status(f"Updating {client_config.display_name} ...") + + if not client_config.config_dir.exists(): + Logger.print_info( + f"Unable to update {client_config.display_name}. Directory does not exist! Skipping ..." + ) + return cm = ConfigManager(cfg_file=KIAUH_CFG) if cm.get_value("kiauh", "backup_before_update"): backup_client_config_data(client) repo_manager = RepoManager( - repo=client_config.get("url"), + repo=client_config.repo_url, branch="master", - target_dir=str(client_config.get("dir")), + target_dir=str(client_config.config_dir), ) repo_manager.pull_repo() - Logger.print_ok(f"Successfully updated {client_config.get('display_name')}.") - Logger.print_warn("Remember to restart Klipper to reload the configurations!") + Logger.print_ok(f"Successfully updated {client_config.display_name}.") + Logger.print_info("Restart Klipper to reload the configuration!") def create_client_config_symlink( - client_config: ClientConfigData, klipper_instances: List[Klipper] = None + client_config: BaseWebClientConfig, klipper_instances: List[Klipper] = None ) -> None: if klipper_instances is None: kl_im = InstanceManager(Klipper) klipper_instances = kl_im.instances - Logger.print_status(f"Create symlink for {client_config.get('cfg_filename')} ...") - source = Path(client_config.get("dir"), client_config.get("cfg_filename")) + Logger.print_status(f"Create symlink for {client_config.config_filename} ...") + source = Path(client_config.config_dir, client_config.config_filename) for instance in klipper_instances: target = instance.cfg_dir Logger.print_status(f"Linking {source} to {target}") diff --git a/kiauh/components/webui_client/client_dialogs.py b/kiauh/components/webui_client/client_dialogs.py index 5270c42..1fce323 100644 --- a/kiauh/components/webui_client/client_dialogs.py +++ b/kiauh/components/webui_client/client_dialogs.py @@ -10,7 +10,7 @@ import textwrap from typing import List -from components.webui_client import ClientData +from components.webui_client.base_data import BaseWebClient from core.menus.base_menu import print_back_footer from utils.constants import RESET_FORMAT, COLOR_YELLOW, COLOR_CYAN @@ -84,9 +84,9 @@ def print_client_port_select_dialog(name: str, port: str, ports_in_use: List[str print(dialog, end="") -def print_install_client_config_dialog(client: ClientData): - name = client.get("display_name") - url = client.get("client_config").get("url").replace(".git", "") +def print_install_client_config_dialog(client: BaseWebClient): + name = client.display_name + url = client.client_config.repo_url.replace(".git", "") line1 = f"have {name} fully functional and working." line2 = f"The recommended macros for {name} can be seen here:" dialog = textwrap.dedent( diff --git a/kiauh/components/webui_client/client_remove.py b/kiauh/components/webui_client/client_remove.py index a55a25f..4315aa1 100644 --- a/kiauh/components/webui_client/client_remove.py +++ b/kiauh/components/webui_client/client_remove.py @@ -13,7 +13,10 @@ from typing import List from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker -from components.webui_client import ClientData +from components.webui_client.base_data import ( + BaseWebClient, + WebClientType, +) from components.webui_client.client_config.client_config_remove import ( run_client_config_removal, ) @@ -29,7 +32,7 @@ from utils.logger import Logger def run_client_removal( - client: ClientData, + client: BaseWebClient, rm_client: bool, rm_client_config: bool, backup_ms_config_json: bool, @@ -39,11 +42,11 @@ def run_client_removal( kl_im = InstanceManager(Klipper) kl_instances: List[Klipper] = kl_im.instances - if backup_ms_config_json and client.get("name") == "mainsail": + if backup_ms_config_json and client.client == WebClientType.MAINSAIL: backup_mainsail_config_json() if rm_client: - client_name = client.get("name") + client_name = client.name remove_client_dir(client) remove_nginx_config(client_name) remove_nginx_logs(client_name) @@ -53,16 +56,16 @@ def run_client_removal( if rm_client_config: run_client_config_removal( - client.get("client_config"), + client.client_config, kl_instances, mr_instances, ) -def remove_client_dir(client: ClientData) -> None: - Logger.print_status(f"Removing {client.get('display_name')} ...") - client_dir = client.get("dir") - if not client.get("dir").exists(): +def remove_client_dir(client: BaseWebClient) -> None: + Logger.print_status(f"Removing {client.display_name} ...") + client_dir = client.client_dir + if not client.client_dir.exists(): Logger.print_info(f"'{client_dir}' does not exist. Skipping ...") return diff --git a/kiauh/components/webui_client/client_setup.py b/kiauh/components/webui_client/client_setup.py index 9a148a3..41a749b 100644 --- a/kiauh/components/webui_client/client_setup.py +++ b/kiauh/components/webui_client/client_setup.py @@ -11,12 +11,13 @@ from pathlib import Path from typing import List from components.klipper.klipper import Klipper -from components.webui_client import ( - ClientName, - ClientData, -) from components.moonraker.moonraker import Moonraker +from components.webui_client.base_data import ( + WebClientType, + BaseWebClient, + BaseWebClientConfig, +) from components.webui_client.client_config.client_config_setup import ( install_client_config, ) @@ -30,7 +31,6 @@ from components.webui_client.client_utils import ( restore_mainsail_config_json, enable_mainsail_remotemode, symlink_webui_nginx_log, - load_client_data, config_for_other_client_exist, ) from core.config_manager.config_manager import ConfigManager @@ -60,17 +60,13 @@ from utils.system_utils import ( ) -def install_client(client_name: ClientName) -> None: - client: ClientData = load_client_data(client_name) - d_name = client.get("display_name") - +def install_client(client: BaseWebClient) -> None: if client is None: - Logger.print_error("Missing parameter client_name!") - return + raise ValueError("Missing parameter client_data!") - if client.get("dir").exists(): + if client.client_dir.exists(): Logger.print_info( - f"{client.get('display_name')} seems to be already installed! Skipped ..." + f"{client.display_name} seems to be already installed! Skipped ..." ) return @@ -81,31 +77,35 @@ def install_client(client_name: ClientName) -> None: if not mr_instances: print_moonraker_not_found_dialog() if not get_confirm( - f"Continue {d_name} installation?", + f"Continue {client.display_name} installation?", allow_go_back=True, ): return # if moonraker is not installed or multiple instances # are installed we enable mainsails remote mode - if client.get("remote_mode") and not mr_instances or len(mr_instances) > 1: + if ( + client.client == WebClientType.MAINSAIL + and not mr_instances + or len(mr_instances) > 1 + ): enable_remotemode = True kl_im = InstanceManager(Klipper) kl_instances = kl_im.instances install_client_cfg = False - client_config = client.get("client_config") + client_config: BaseWebClientConfig = client.client_config if ( kl_instances - and not client_config.get("dir").exists() - and not config_for_other_client_exist(client_to_ignore=client.get("name")) + and not client_config.config_dir.exists() + and not config_for_other_client_exist(client_to_ignore=client.client) ): print_install_client_config_dialog(client) - question = f"Download the recommended {client_config.get('display_name')}?" + question = f"Download the recommended {client_config.display_name}?" install_client_cfg = get_confirm(question, allow_go_back=False) cm = ConfigManager(cfg_file=KIAUH_CFG) - default_port = cm.get_value(client.get("name"), "port") + default_port = cm.get_value(client.name, "port") client_port = default_port if default_port and default_port.isdigit() else "80" ports_in_use = read_ports_from_nginx_configs() @@ -113,10 +113,10 @@ def install_client(client_name: ClientName) -> None: valid_port = is_valid_port(client_port, ports_in_use) while not valid_port: next_port = get_next_free_port(ports_in_use) - print_client_port_select_dialog(d_name, next_port, ports_in_use) + print_client_port_select_dialog(client.display_name, next_port, ports_in_use) client_port = str( get_number_input( - f"Configure {d_name} for port", + f"Configure {client.display_name} for port", min_count=int(next_port), default=next_port, ) @@ -127,22 +127,22 @@ def install_client(client_name: ClientName) -> None: try: download_client(client) - if enable_remotemode and client.get("name") == "mainsail": + if enable_remotemode and client.client == WebClientType.MAINSAIL: enable_mainsail_remotemode() if mr_instances: add_config_section( - section=f"update_manager {client.get('name')}", + section=f"update_manager {client.name}", instances=mr_instances, options=[ ("type", "web"), ("channel", "stable"), - ("repo", client.get("mr_conf_repo")), - ("path", client.get("mr_conf_path")), + ("repo", str(client.repo_path)), + ("path", str(client.client_dir)), ], ) mr_im.restart_all_instance() if install_client_cfg and kl_instances: - install_client_config(client.get("name")) + install_client_config(client) copy_upstream_nginx_cfg() copy_common_vars_nginx_cfg() @@ -152,24 +152,24 @@ def install_client(client_name: ClientName) -> None: control_systemd_service("nginx", "restart") except Exception as e: - Logger.print_error(f"{d_name} installation failed!\n{e}") + Logger.print_error(f"{client.display_name} installation failed!\n{e}") return - log = f"Open {d_name} now on: http://{get_ipv4_addr()}:{client_port}" - Logger.print_ok(f"{d_name} installation complete!", start="\n") + log = f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{client_port}" + Logger.print_ok(f"{client.display_name} installation complete!", start="\n") Logger.print_ok(log, prefix=False, end="\n\n") -def download_client(client: ClientData) -> None: - zipfile = f"{client.get('name').lower()}.zip" +def download_client(client: BaseWebClient) -> None: + zipfile = f"{client.name.lower()}.zip" target = Path().home().joinpath(zipfile) try: Logger.print_status(f"Downloading {zipfile} ...") - download_file(client.get("url"), target, True) + download_file(client.stable_url, target, True) Logger.print_ok("Download complete!") Logger.print_status(f"Extracting {zipfile} ...") - unzip(target, client.get("dir")) + unzip(target, client.client_dir) target.unlink(missing_ok=True) Logger.print_ok("OK!") @@ -178,29 +178,35 @@ def download_client(client: ClientData) -> None: raise -def update_client(client: ClientData) -> None: - Logger.print_status(f"Updating {client.get('display_name')} ...") - if client.get("name") == "mainsail": +def update_client(client: BaseWebClient) -> None: + Logger.print_status(f"Updating {client.display_name} ...") + if not client.client_dir.exists(): + Logger.print_info( + f"Unable to update {client.display_name}. Directory does not exist! Skipping ..." + ) + return + + if client.client == WebClientType.MAINSAIL: backup_mainsail_config_json(is_temp=True) download_client(client) - if client.get("name") == "mainsail": + if client.client == WebClientType.MAINSAIL: restore_mainsail_config_json() -def create_client_nginx_cfg(client: ClientData, port: int) -> None: - d_name = client.get("display_name") - root_dir = client.get("dir") - source = NGINX_SITES_AVAILABLE.joinpath(client.get("name")) - target = NGINX_SITES_ENABLED.joinpath(client.get("name")) +def create_client_nginx_cfg(client: BaseWebClient, port: int) -> None: + display_name = client.display_name + root_dir = client.client_dir + source = NGINX_SITES_AVAILABLE.joinpath(client.name) + target = NGINX_SITES_ENABLED.joinpath(client.name) try: - Logger.print_status(f"Creating NGINX config for {d_name} ...") + Logger.print_status(f"Creating NGINX config for {display_name} ...") remove_file(Path("/etc/nginx/sites-enabled/default"), True) - create_nginx_cfg(client.get("name"), port, root_dir) + create_nginx_cfg(client.name, port, root_dir) create_symlink(source, target, True) set_nginx_permissions() - Logger.print_ok(f"NGINX config for {d_name} successfully created.") + Logger.print_ok(f"NGINX config for {display_name} successfully created.") except Exception: - Logger.print_error(f"Creating NGINX config for {d_name} failed!") + Logger.print_error(f"Creating NGINX config for {display_name} failed!") raise diff --git a/kiauh/components/webui_client/client_utils.py b/kiauh/components/webui_client/client_utils.py index 23d34e3..2d1d5bd 100644 --- a/kiauh/components/webui_client/client_utils.py +++ b/kiauh/components/webui_client/client_utils.py @@ -9,117 +9,43 @@ import json import shutil -from json import JSONDecodeError from pathlib import Path -from typing import List, Optional, Dict, Literal, Union, get_args +from typing import List, Dict, Literal, Union, get_args -import urllib.request from components.klipper.klipper import Klipper -from components.webui_client import ( - MAINSAIL_CONFIG_JSON, - MAINSAIL_DIR, - MAINSAIL_BACKUP_DIR, - FLUIDD_PRE_RLS_URL, - FLUIDD_BACKUP_DIR, - FLUIDD_URL, - FLUIDD_DIR, - ClientData, - FLUIDD_CONFIG_REPO_URL, - FLUIDD_CONFIG_DIR, - ClientConfigData, - MAINSAIL_PRE_RLS_URL, - MAINSAIL_URL, - MAINSAIL_CONFIG_REPO_URL, - MAINSAIL_CONFIG_DIR, - ClientName, - MAINSAIL_TAGS_URL, - FLUIDD_TAGS_URL, - FLUIDD_CONFIG_BACKUP_DIR, - MAINSAIL_CONFIG_BACKUP_DIR, +from components.webui_client.base_data import ( + WebClientType, + BaseWebClient, + BaseWebClientConfig, ) +from components.webui_client.mainsail_data import MainsailData from core.backup_manager.backup_manager import BackupManager from core.repo_manager.repo_manager import RepoManager from utils import NGINX_SITES_AVAILABLE, NGINX_CONFD from utils.common import get_install_status_webui from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW +from utils.git_utils import get_latest_tag from utils.logger import Logger -def load_client_data(client_name: ClientName) -> Optional[ClientData]: - client_data = None - - if client_name == "mainsail": - client_config_data = ClientConfigData( - name="mainsail-config", - display_name="Mainsail-Config", - cfg_filename="mainsail.cfg", - dir=MAINSAIL_CONFIG_DIR, - backup_dir=MAINSAIL_CONFIG_BACKUP_DIR, - url=MAINSAIL_CONFIG_REPO_URL, - printer_cfg_section="include mainsail.cfg", - mr_conf_path="~/mainsail-config", - mr_conf_origin=MAINSAIL_CONFIG_REPO_URL, - ) - client_data = ClientData( - name=client_name, - display_name=client_name.capitalize(), - dir=MAINSAIL_DIR, - backup_dir=MAINSAIL_BACKUP_DIR, - url=MAINSAIL_URL, - pre_release_url=MAINSAIL_PRE_RLS_URL, - tags_url=MAINSAIL_TAGS_URL, - remote_mode=True, - mr_conf_repo="mainsail-crew/mainsail", - mr_conf_path="~/mainsail", - client_config=client_config_data, - ) - elif client_name == "fluidd": - client_config_data = ClientConfigData( - name="fluidd-config", - display_name="Fluidd-Config", - cfg_filename="fluidd.cfg", - dir=FLUIDD_CONFIG_DIR, - backup_dir=FLUIDD_CONFIG_BACKUP_DIR, - url=FLUIDD_CONFIG_REPO_URL, - printer_cfg_section="include fluidd.cfg", - mr_conf_path="~/fluidd-config", - mr_conf_origin=FLUIDD_CONFIG_REPO_URL, - ) - client_data = ClientData( - name=client_name, - display_name=client_name.capitalize(), - dir=FLUIDD_DIR, - backup_dir=FLUIDD_BACKUP_DIR, - url=FLUIDD_URL, - pre_release_url=FLUIDD_PRE_RLS_URL, - tags_url=FLUIDD_TAGS_URL, - remote_mode=False, - mr_conf_repo="fluidd-core/fluidd", - mr_conf_path="~/fluidd", - client_config=client_config_data, - ) - - return client_data - - -def get_client_status(client: ClientData) -> str: +def get_client_status(client: BaseWebClient) -> str: return get_install_status_webui( - client.get("dir"), - NGINX_SITES_AVAILABLE.joinpath(client.get("name")), + client.client_dir, + NGINX_SITES_AVAILABLE.joinpath(client.name), NGINX_CONFD.joinpath("upstreams.conf"), NGINX_CONFD.joinpath("common_vars.conf"), ) def get_client_config_status( - client: ClientData, + client: BaseWebClient, ) -> Dict[ Literal["repo", "local", "remote"], Union[str, int], ]: - client_config = client.get("client_config") - client_config = client_config.get("dir") + client_config = client.client_config + client_config = client_config.config_dir return { "repo": RepoManager.get_repo_name(client_config), @@ -128,44 +54,47 @@ def get_client_config_status( } -def get_current_client_config(clients: List[ClientData]) -> str: +def get_current_client_config(clients: List[BaseWebClient]) -> str: installed = [] for client in clients: - client_config = client.get("client_config") - if client_config.get("dir").exists(): + client_config = client.client_config + if client_config.config_dir.exists(): installed.append(client) if len(installed) > 1: return f"{COLOR_YELLOW}Conflict!{RESET_FORMAT}" elif len(installed) == 1: - cfg = installed[0].get("client_config") - return f"{COLOR_CYAN}{cfg.get('display_name')}{RESET_FORMAT}" + cfg = installed[0].client_config + return f"{COLOR_CYAN}{cfg.display_name}{RESET_FORMAT}" return f"{COLOR_CYAN}-{RESET_FORMAT}" def backup_mainsail_config_json(is_temp=False) -> None: - Logger.print_status(f"Backup '{MAINSAIL_CONFIG_JSON}' ...") + c_json = MainsailData().client_dir.joinpath("config.json") + Logger.print_status(f"Backup '{c_json}' ...") bm = BackupManager() if is_temp: fn = Path.home().joinpath("config.json.kiauh.bak") - bm.backup_file(MAINSAIL_CONFIG_JSON, custom_filename=fn) + bm.backup_file(c_json, custom_filename=fn) else: - bm.backup_file(MAINSAIL_CONFIG_JSON) + bm.backup_file(c_json) def restore_mainsail_config_json() -> None: try: - Logger.print_status(f"Restore '{MAINSAIL_CONFIG_JSON}' ...") + c_json = MainsailData().client_dir.joinpath("config.json") + Logger.print_status(f"Restore '{c_json}' ...") source = Path.home().joinpath("config.json.kiauh.bak") - shutil.copy(source, MAINSAIL_CONFIG_JSON) + shutil.copy(source, c_json) except OSError: Logger.print_info("Unable to restore config.json. Skipped ...") def enable_mainsail_remotemode() -> None: Logger.print_status("Enable Mainsails remote mode ...") - with open(MAINSAIL_CONFIG_JSON, "r") as f: + c_json = MainsailData().client_dir.joinpath("config.json") + with open(c_json, "r") as f: config_data = json.load(f) if config_data["instancesDB"] == "browser": @@ -175,7 +104,7 @@ def enable_mainsail_remotemode() -> None: Logger.print_status("Setting instance storage location to 'browser' ...") config_data["instancesDB"] = "browser" - with open(MAINSAIL_CONFIG_JSON, "w") as f: + with open(c_json, "w") as f: json.dump(config_data, f, indent=4) Logger.print_ok("Mainsails remote mode enabled!") @@ -195,8 +124,8 @@ def symlink_webui_nginx_log(klipper_instances: List[Klipper]) -> None: desti_error.symlink_to(error_log) -def get_local_client_version(client: ClientData) -> str: - relinfo_file = client.get("dir").joinpath("release_info.json") +def get_local_client_version(client: BaseWebClient) -> str: + relinfo_file = client.client_dir.joinpath("release_info.json") if not relinfo_file.is_file(): return "-" @@ -204,19 +133,19 @@ def get_local_client_version(client: ClientData) -> str: return json.load(f)["version"] -def get_remote_client_version(client: ClientData) -> str: +def get_remote_client_version(client: BaseWebClient) -> str: try: - with urllib.request.urlopen(client.get("tags_url")) as response: - data = json.loads(response.read()) - return data[0]["name"] - except (JSONDecodeError, TypeError): + if (tag := get_latest_tag(client.repo_path)) != "": + return tag + return "ERROR" + except Exception: return "ERROR" -def backup_client_data(client: ClientData) -> None: - name = client.get("name") - src = client.get("dir") - dest = client.get("backup_dir") +def backup_client_data(client: BaseWebClient) -> None: + name = client.name + src = client.client_dir + dest = client.backup_dir with open(src.joinpath(".version"), "r") as v: version = v.readlines()[0] @@ -224,43 +153,42 @@ def backup_client_data(client: ClientData) -> None: bm = BackupManager() bm.backup_directory(f"{name}-{version}", src, dest) if name == "mainsail": - bm.backup_file(MAINSAIL_CONFIG_JSON, dest) + c_json = MainsailData().client_dir.joinpath("config.json") + bm.backup_file(c_json, dest) bm.backup_file(NGINX_SITES_AVAILABLE.joinpath(name), dest) -def backup_client_config_data(client: ClientData) -> None: - client_config = client.get("client_config") - name = client_config.get("name") - source = client_config.get("dir") - target = client_config.get("backup_dir") +def backup_client_config_data(client: BaseWebClient) -> None: + client_config = client.client_config + name = client_config.name + source = client_config.config_dir + target = client_config.backup_dir bm = BackupManager() bm.backup_directory(name, source, target) -def get_existing_clients() -> List[ClientData]: - clients = list(get_args(ClientName)) - installed_clients: List[ClientData] = [] - for c in clients: - c_data: ClientData = load_client_data(c) - if c_data.get("dir").exists(): - installed_clients.append(c_data) +def get_existing_clients() -> List[BaseWebClient]: + clients = list(get_args(WebClientType)) + installed_clients: List[BaseWebClient] = [] + for client in clients: + if client.client_dir.exists(): + installed_clients.append(client) return installed_clients -def get_existing_client_config() -> List[ClientData]: - clients = list(get_args(ClientName)) - installed_client_configs: List[ClientData] = [] - for c in clients: - c_data: ClientData = load_client_data(c) - c_config_data: ClientConfigData = c_data.get("client_config") - if c_config_data.get("dir").exists(): - installed_client_configs.append(c_data) +def get_existing_client_config() -> List[BaseWebClient]: + clients = list(get_args(WebClientType)) + installed_client_configs: List[BaseWebClient] = [] + for client in clients: + c_config_data: BaseWebClientConfig = client.client_config + if c_config_data.config_dir.exists(): + installed_client_configs.append(client) return installed_client_configs -def config_for_other_client_exist(client_to_ignore: ClientName) -> bool: +def config_for_other_client_exist(client_to_ignore: WebClientType) -> bool: """ Check if any other client configs are present on the system. It is usually not harmful, but chances are they can conflict each other. @@ -269,7 +197,7 @@ def config_for_other_client_exist(client_to_ignore: ClientName) -> bool: :return: True, if other client configs were found, else False """ - clients = set([c["name"] for c in get_existing_client_config()]) - clients = clients - {client_to_ignore} + clients = set([c.name for c in get_existing_client_config()]) + clients = clients - {client_to_ignore.value} return True if len(clients) > 0 else False diff --git a/kiauh/components/webui_client/fluidd_data.py b/kiauh/components/webui_client/fluidd_data.py new file mode 100644 index 0000000..4099eeb --- /dev/null +++ b/kiauh/components/webui_client/fluidd_data.py @@ -0,0 +1,65 @@ +# ======================================================================= # +# Copyright (C) 2020 - 2024 Dominik Willner # +# # +# This file is part of KIAUH - Klipper Installation And Update Helper # +# https://github.com/dw-0/kiauh # +# # +# This file may be distributed under the terms of the GNU GPLv3 license # +# ======================================================================= # + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +from components.webui_client.base_data import ( + BaseWebClientConfig, + WebClientConfigType, + WebClientType, + BaseWebClient, +) +from core.backup_manager import BACKUP_ROOT_DIR +from utils.git_utils import get_latest_unstable_tag + + +@dataclass(frozen=True) +class FluiddConfigWeb(BaseWebClientConfig): + client_config: WebClientConfigType = WebClientConfigType.FLUIDD + name: str = client_config.value + display_name: str = name.title() + config_dir: Path = Path.home().joinpath("fluidd-config") + config_filename: str = "fluidd.cfg" + config_section: str = f"include {config_filename}" + backup_dir: Path = BACKUP_ROOT_DIR.joinpath("fluidd-config-backups") + repo_url: str = "https://github.com/fluidd-core/fluidd-config.git" + + +@dataclass(frozen=True) +class FluiddData(BaseWebClient): + BASE_DL_URL = "https://github.com/fluidd-core/fluidd/releases" + + client: WebClientType = WebClientType.FLUIDD + name: str = client.value + display_name: str = name.capitalize() + client_dir: Path = Path.home().joinpath("fluidd") + backup_dir: Path = BACKUP_ROOT_DIR.joinpath("fluidd-backups") + repo_path: str = "fluidd-core/fluidd" + + @property + def stable_url(self) -> str: + return f"{self.BASE_DL_URL}/latest/download/fluidd.zip" + + @property + def unstable_url(self) -> str: + try: + unstable_tag = get_latest_unstable_tag(self.repo_path) + if unstable_tag != "": + return f"{self.BASE_DL_URL}/download/{unstable_tag}/fluidd.zip" + else: + raise Exception + except Exception: + return self.stable_url + + @property + def client_config(self) -> BaseWebClientConfig: + return FluiddConfigWeb() diff --git a/kiauh/components/webui_client/mainsail_data.py b/kiauh/components/webui_client/mainsail_data.py new file mode 100644 index 0000000..208ce2c --- /dev/null +++ b/kiauh/components/webui_client/mainsail_data.py @@ -0,0 +1,65 @@ +# ======================================================================= # +# Copyright (C) 2020 - 2024 Dominik Willner # +# # +# This file is part of KIAUH - Klipper Installation And Update Helper # +# https://github.com/dw-0/kiauh # +# # +# This file may be distributed under the terms of the GNU GPLv3 license # +# ======================================================================= # + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +from components.webui_client.base_data import ( + BaseWebClientConfig, + WebClientConfigType, + WebClientType, + BaseWebClient, +) +from core.backup_manager import BACKUP_ROOT_DIR +from utils.git_utils import get_latest_unstable_tag + + +@dataclass(frozen=True) +class MainsailConfigWeb(BaseWebClientConfig): + client_config: WebClientConfigType = WebClientConfigType.MAINSAIL + name: str = client_config.value + display_name: str = name.title() + config_dir: Path = Path.home().joinpath("mainsail-config") + config_filename: str = "mainsail.cfg" + config_section: str = f"include {config_filename}" + backup_dir: Path = BACKUP_ROOT_DIR.joinpath("mainsail-config-backups") + repo_url: str = "https://github.com/mainsail-crew/mainsail-config.git" + + +@dataclass(frozen=True) +class MainsailData(BaseWebClient): + BASE_DL_URL: str = "https://github.com/mainsail-crew/mainsail/releases" + + client: WebClientType = WebClientType.MAINSAIL + name: str = WebClientType.MAINSAIL.value + display_name: str = name.capitalize() + client_dir: Path = Path.home().joinpath("mainsail") + backup_dir: Path = BACKUP_ROOT_DIR.joinpath("mainsail-backups") + repo_path: str = "mainsail-crew/mainsail" + + @property + def stable_url(self) -> str: + return f"{self.BASE_DL_URL}/latest/download/mainsail.zip" + + @property + def unstable_url(self) -> str: + try: + unstable_tag = get_latest_unstable_tag(self.repo_path) + if unstable_tag != "": + return f"{self.BASE_DL_URL}/download/{unstable_tag}/mainsail.zip" + else: + raise Exception + except Exception: + return self.stable_url + + @property + def client_config(self) -> BaseWebClientConfig: + return MainsailConfigWeb() diff --git a/kiauh/components/webui_client/menus/client_remove_menu.py b/kiauh/components/webui_client/menus/client_remove_menu.py index ac32298..f6e99a0 100644 --- a/kiauh/components/webui_client/menus/client_remove_menu.py +++ b/kiauh/components/webui_client/menus/client_remove_menu.py @@ -10,14 +10,15 @@ import textwrap from typing import Callable, Dict -from components.webui_client import client_remove, ClientData +from components.webui_client import client_remove +from components.webui_client.base_data import BaseWebClient, WebClientType from core.menus.base_menu import BaseMenu from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN # noinspection PyUnusedLocal class ClientRemoveMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu, client: ClientData): + def __init__(self, previous_menu: BaseMenu, client: BaseWebClient): super().__init__() self.previous_menu = previous_menu self.options = self.get_options(client) @@ -27,22 +28,22 @@ class ClientRemoveMenu(BaseMenu): self.rm_client_config = False self.backup_mainsail_config_json = False - def get_options(self, client: ClientData) -> Dict[str, Callable]: + def get_options(self, client: BaseWebClient) -> Dict[str, Callable]: options = { "0": self.toggle_all, "1": self.toggle_rm_client, "2": self.toggle_rm_client_config, "c": self.run_removal_process, } - if client.get("name") == "mainsail": + if client.client == WebClientType.MAINSAIL: options["3"] = self.toggle_backup_mainsail_config_json return options def print_menu(self) -> None: - client_name = self.client.get("display_name") - client_config = self.client.get("client_config") - client_config_name = client_config.get("display_name") + client_name = self.client.display_name + client_config = self.client.client_config + client_config_name = client_config.display_name header = f" [ Remove {client_name} ] " color = COLOR_RED @@ -66,7 +67,7 @@ class ClientRemoveMenu(BaseMenu): """ )[1:] - if self.client.get("name") == "mainsail": + if self.client.client == WebClientType.MAINSAIL: o3 = checked if self.backup_mainsail_config_json else unchecked menu += textwrap.dedent( f""" diff --git a/kiauh/core/menus/backup_menu.py b/kiauh/core/menus/backup_menu.py index 0c473fc..72f1735 100644 --- a/kiauh/core/menus/backup_menu.py +++ b/kiauh/core/menus/backup_menu.py @@ -16,9 +16,10 @@ from components.moonraker.moonraker_utils import ( ) from components.webui_client.client_utils import ( backup_client_data, - load_client_data, backup_client_config_data, ) +from components.webui_client.fluidd_data import FluiddData +from components.webui_client.mainsail_data import MainsailData from core.menus.base_menu import BaseMenu from utils.common import backup_printer_config_dir from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW @@ -81,16 +82,16 @@ class BackupMenu(BaseMenu): backup_moonraker_db_dir() def backup_mainsail(self, **kwargs): - backup_client_data(load_client_data("mainsail")) + backup_client_data(MainsailData().get()) def backup_fluidd(self, **kwargs): - backup_client_data(load_client_data("fluidd")) + backup_client_data(FluiddData().get()) def backup_mainsail_config(self, **kwargs): - backup_client_config_data(load_client_data("mainsail")) + backup_client_config_data(MainsailData().get()) def backup_fluidd_config(self, **kwargs): - backup_client_config_data(load_client_data("fluidd")) + backup_client_config_data(FluiddData().get()) def backup_klipperscreen(self, **kwargs): pass diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index ad2024a..5db3846 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -13,6 +13,8 @@ from components.klipper import klipper_setup from components.moonraker import moonraker_setup from components.webui_client import client_setup from components.webui_client.client_config import client_config_setup +from components.webui_client.fluidd_data import FluiddData +from components.webui_client.mainsail_data import MainsailData from core.menus.base_menu import BaseMenu from utils.constants import COLOR_GREEN, RESET_FORMAT @@ -69,13 +71,13 @@ class InstallMenu(BaseMenu): moonraker_setup.install_moonraker() def install_mainsail(self, **kwargs): - client_setup.install_client(client_name="mainsail") + client_setup.install_client(MainsailData()) def install_mainsail_config(self, **kwargs): - client_config_setup.install_client_config(client_name="mainsail") + client_config_setup.install_client_config(MainsailData()) def install_fluidd(self, **kwargs): - client_setup.install_client(client_name="fluidd") + client_setup.install_client(FluiddData()) def install_fluidd_config(self, **kwargs): - client_config_setup.install_client_config(client_name="fluidd") + client_config_setup.install_client_config(FluiddData()) diff --git a/kiauh/core/menus/main_menu.py b/kiauh/core/menus/main_menu.py index 66d4cfd..317584b 100644 --- a/kiauh/core/menus/main_menu.py +++ b/kiauh/core/menus/main_menu.py @@ -14,9 +14,10 @@ from components.log_uploads.menus.log_upload_menu import LogUploadMenu from components.moonraker.moonraker_utils import get_moonraker_status from components.webui_client.client_utils import ( get_client_status, - load_client_data, get_current_client_config, ) +from components.webui_client.fluidd_data import FluiddData +from components.webui_client.mainsail_data import MainsailData from core.menus import FooterType from core.menus.advanced_menu import AdvancedMenu from core.menus.backup_menu import BackupMenu @@ -92,15 +93,11 @@ class MainMenu(BaseMenu): self.mr_status = self.format_status_by_code(mr_code, mr_status, mr_instances) self.mr_repo = f"{COLOR_CYAN}{moonraker_status.get('repo')}{RESET_FORMAT}" # mainsail - mainsail_client_data = load_client_data("mainsail") - self.ms_status = get_client_status(mainsail_client_data) + self.ms_status = get_client_status(MainsailData()) # fluidd - fluidd_client_data = load_client_data("fluidd") - self.fl_status = get_client_status(fluidd_client_data) + self.fl_status = get_client_status(FluiddData()) # client-config - self.cc_status = get_current_client_config( - [mainsail_client_data, fluidd_client_data] - ) + self.cc_status = get_current_client_config([MainsailData(), FluiddData()]) def format_status_by_code(self, code: int, status: str, count: str) -> str: if code == 1: diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index 16d1c42..0ba8992 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -13,7 +13,8 @@ from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu from components.moonraker.menus.moonraker_remove_menu import ( MoonrakerRemoveMenu, ) -from components.webui_client.client_utils import load_client_data +from components.webui_client.fluidd_data import FluiddData +from components.webui_client.mainsail_data import MainsailData from components.webui_client.menus.client_remove_menu import ClientRemoveMenu from core.menus.base_menu import BaseMenu from utils.constants import COLOR_RED, RESET_FORMAT @@ -76,7 +77,7 @@ class RemoveMenu(BaseMenu): MoonrakerRemoveMenu(previous_menu=self).run() def remove_mainsail(self, **kwargs): - ClientRemoveMenu(previous_menu=self, client=load_client_data("mainsail")).run() + ClientRemoveMenu(previous_menu=self, client=MainsailData()).run() def remove_fluidd(self, **kwargs): - ClientRemoveMenu(previous_menu=self, client=load_client_data("fluidd")).run() + ClientRemoveMenu(previous_menu=self, client=FluiddData()).run() diff --git a/kiauh/core/menus/update_menu.py b/kiauh/core/menus/update_menu.py index 146e1ad..4917ebf 100644 --- a/kiauh/core/menus/update_menu.py +++ b/kiauh/core/menus/update_menu.py @@ -22,9 +22,10 @@ from components.webui_client.client_setup import update_client from components.webui_client.client_utils import ( get_local_client_version, get_remote_client_version, - load_client_data, get_client_config_status, ) +from components.webui_client.fluidd_data import FluiddData +from components.webui_client.mainsail_data import MainsailData from core.menus.base_menu import BaseMenu from utils.constants import ( COLOR_GREEN, @@ -69,6 +70,9 @@ class UpdateMenu(BaseMenu): self.fc_local = f"{COLOR_WHITE}{RESET_FORMAT}" self.fc_remote = f"{COLOR_WHITE}{RESET_FORMAT}" + self.mainsail_cient = MainsailData() + self.fluidd_client = FluiddData() + def print_menu(self): self.fetch_update_status() @@ -114,16 +118,16 @@ class UpdateMenu(BaseMenu): update_moonraker() def update_mainsail(self, **kwargs): - update_client(load_client_data("mainsail")) + update_client(self.mainsail_cient) def update_mainsail_config(self, **kwargs): - update_client_config(load_client_data("mainsail")) + update_client_config(self.mainsail_cient) def update_fluidd(self, **kwargs): - update_client(load_client_data("fluidd")) + update_client(self.fluidd_client) def update_fluidd_config(self, **kwargs): - update_client_config(load_client_data("fluidd")) + update_client_config(self.fluidd_client) def update_klipperscreen(self, **kwargs): ... @@ -153,25 +157,23 @@ class UpdateMenu(BaseMenu): self.mr_local = f"{COLOR_YELLOW}{self.mr_local}{RESET_FORMAT}" self.mr_remote = f"{COLOR_GREEN}{self.mr_remote}{RESET_FORMAT}" # mainsail - mainsail_client_data = load_client_data("mainsail") - self.ms_local = get_local_client_version(mainsail_client_data) - self.ms_remote = get_remote_client_version(mainsail_client_data) + self.ms_local = get_local_client_version(self.mainsail_cient) + self.ms_remote = get_remote_client_version(self.mainsail_cient) if self.ms_local == self.ms_remote: self.ms_local = f"{COLOR_GREEN}{self.ms_local}{RESET_FORMAT}" else: self.ms_local = f"{COLOR_YELLOW}{self.ms_local}{RESET_FORMAT}" self.ms_remote = f"{COLOR_GREEN if self.ms_remote != 'ERROR' else COLOR_RED}{self.ms_remote}{RESET_FORMAT}" # fluidd - fluidd_client_data = load_client_data("fluidd") - self.fl_local = get_local_client_version(fluidd_client_data) - self.fl_remote = get_remote_client_version(fluidd_client_data) + self.fl_local = get_local_client_version(self.fluidd_client) + self.fl_remote = get_remote_client_version(self.fluidd_client) if self.fl_local == self.fl_remote: self.fl_local = f"{COLOR_GREEN}{self.fl_local}{RESET_FORMAT}" else: self.fl_local = f"{COLOR_YELLOW}{self.fl_local}{RESET_FORMAT}" self.fl_remote = f"{COLOR_GREEN if self.fl_remote != 'ERROR' else COLOR_RED}{self.fl_remote}{RESET_FORMAT}" # mainsail-config - mc_status = get_client_config_status(load_client_data("mainsail")) + mc_status = get_client_config_status(self.mainsail_cient) self.mc_local = mc_status.get("local") self.mc_remote = mc_status.get("remote") if self.mc_local == self.mc_remote: @@ -180,7 +182,7 @@ class UpdateMenu(BaseMenu): self.mc_local = f"{COLOR_YELLOW}{self.mc_local}{RESET_FORMAT}" self.mc_remote = f"{COLOR_GREEN}{self.mc_remote}{RESET_FORMAT}" # fluidd-config - fc_status = get_client_config_status(load_client_data("fluidd")) + fc_status = get_client_config_status(self.fluidd_client) self.fc_local = fc_status.get("local") self.fc_remote = fc_status.get("remote") if self.fc_local == self.mc_remote: diff --git a/kiauh/utils/git_utils.py b/kiauh/utils/git_utils.py new file mode 100644 index 0000000..2ed9a4d --- /dev/null +++ b/kiauh/utils/git_utils.py @@ -0,0 +1,57 @@ +import json +import urllib.request +from http.client import HTTPResponse +from json import JSONDecodeError +from typing import List + +from utils.logger import Logger + + +def get_tags(repo_path: str) -> List[str]: + try: + url = f"https://api.github.com/repos/{repo_path}/tags" + with urllib.request.urlopen(url) as r: + response: HTTPResponse = r + if response.getcode() != 200: + Logger.print_error( + f"Error retrieving tags: HTTP status code {response.getcode()}" + ) + return [] + + data = json.loads(response.read()) + return [item["name"] for item in data] + except (JSONDecodeError, TypeError) as e: + Logger.print_error(f"Error while processing the response: {e}") + raise + + +def get_latest_tag(repo_path: str) -> str: + """ + Gets the latest stable tag of a GitHub repostiory + :param repo_path: path of the GitHub repository - e.g. `/` + :return: tag or empty string + """ + try: + if len(latest_tag := get_tags(repo_path)) > 0: + return latest_tag[0] + else: + return "" + except Exception: + Logger.print_error("Error while getting the latest tag") + raise + + +def get_latest_unstable_tag(repo_path: str) -> str: + """ + Gets the latest unstable (alpha, beta, rc) tag of a GitHub repository + :param repo_path: path of the GitHub repository - e.g. `/` + :return: tag or empty string + """ + try: + if len(unstable_tags := [t for t in get_tags(repo_path) if "-" in t]) > 0: + return unstable_tags[0] + else: + return "" + except Exception: + Logger.print_error("Error while getting the latest unstable tag") + raise