From 8a048e689e47bf90ed9f7d4304b6b29672f44def Mon Sep 17 00:00:00 2001 From: dw-0 Date: Wed, 26 Jun 2024 21:27:11 +0200 Subject: [PATCH 1/5] feat: scaffold OE installer Signed-off-by: Dominik Willner --- kiauh/components/octoeverywhere/__init__.py | 29 +++ .../octoeverywhere/octoeverywhere.py | 72 ++++++ .../octoeverywhere/octoeverywhere_setup.py | 235 ++++++++++++++++++ kiauh/core/menus/install_menu.py | 9 +- kiauh/core/menus/main_menu.py | 7 +- kiauh/core/menus/remove_menu.py | 19 +- 6 files changed, 360 insertions(+), 11 deletions(-) create mode 100644 kiauh/components/octoeverywhere/__init__.py create mode 100644 kiauh/components/octoeverywhere/octoeverywhere.py create mode 100644 kiauh/components/octoeverywhere/octoeverywhere_setup.py diff --git a/kiauh/components/octoeverywhere/__init__.py b/kiauh/components/octoeverywhere/__init__.py new file mode 100644 index 0000000..f07e4ab --- /dev/null +++ b/kiauh/components/octoeverywhere/__init__.py @@ -0,0 +1,29 @@ +# ======================================================================= # +# 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 + +# repo +OE_REPO = "https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere.git" + +# directories +OE_DIR = Path.home().joinpath("octoeverywhere") +OE_ENV_DIR = Path.home().joinpath("octoeverywhere-env") +OE_STORE_DIR = OE_DIR.joinpath("octoeverywhere-store") + +# files +OE_REQ_FILE = OE_DIR.joinpath("requirements.txt") +OE_DEPS_JSON_FILE = OE_DIR.joinpath("moonraker-system-dependencies.json") +OE_INSTALL_SCRIPT = OE_DIR.joinpath("install.sh") +OE_UPDATE_SCRIPT = OE_DIR.joinpath("update.sh") +OE_REMOVE_SCRIPT = OE_DIR.joinpath("uninstall.sh") + +# filenames +OE_CFG_NAME = "octoeverywhere.conf" +OE_LOG_NAME = "octoeverywhere.log" +OE_SYS_CFG_NAME = "octoeverywhere-system.cfg" diff --git a/kiauh/components/octoeverywhere/octoeverywhere.py b/kiauh/components/octoeverywhere/octoeverywhere.py new file mode 100644 index 0000000..a4e530b --- /dev/null +++ b/kiauh/components/octoeverywhere/octoeverywhere.py @@ -0,0 +1,72 @@ +# ======================================================================= # +# 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 subprocess import CalledProcessError, run +from typing import List + +from components.octoeverywhere import ( + OE_CFG_NAME, + OE_DIR, + OE_ENV_DIR, + OE_INSTALL_SCRIPT, + OE_LOG_NAME, + OE_STORE_DIR, + OE_SYS_CFG_NAME, +) +from core.instance_manager.base_instance import BaseInstance +from utils.logger import Logger + +MODULE_PATH = Path(__file__).resolve().parent + + +# noinspection PyMethodMayBeStatic +class Octoeverywhere(BaseInstance): + @classmethod + def blacklist(cls) -> List[str]: + return ["None", "mcu"] + + def __init__(self, suffix: str = ""): + super().__init__(instance_type=self, suffix=suffix) + self.dir: Path = OE_DIR + self.env_dir: Path = OE_ENV_DIR + self.store_dir: Path = OE_STORE_DIR + self._cfg_file = self.cfg_dir.joinpath(OE_CFG_NAME) + self._sys_cfg_file = self.cfg_dir.joinpath(OE_SYS_CFG_NAME) + self._log = self.log_dir.joinpath(OE_LOG_NAME) + + @property + def cfg_file(self) -> Path: + return self._cfg_file + + @property + def log(self) -> Path: + return self._log + + def create(self) -> None: + Logger.print_status("Creating OctoEverywhere for Klipper Instance ...") + + try: + cmd = f"{OE_INSTALL_SCRIPT} {self.cfg_dir}/moonraker.conf" + print(cmd) + run(cmd, check=True, shell=True) + + except CalledProcessError as e: + Logger.print_error(f"Error creating instance: {e}") + raise + + def delete(self) -> None: + Logger.print_status("Removing OctoEverywhere for Klipper Instance ...") + + try: + cmd = f"OE_REMOVE_SCRIPT {self.cfg_dir}/moonraker.conf" + run(cmd, check=True, shell=True) + except CalledProcessError as e: + Logger.print_error(f"Error deleting instance: {e}") + raise diff --git a/kiauh/components/octoeverywhere/octoeverywhere_setup.py b/kiauh/components/octoeverywhere/octoeverywhere_setup.py new file mode 100644 index 0000000..4ee52dd --- /dev/null +++ b/kiauh/components/octoeverywhere/octoeverywhere_setup.py @@ -0,0 +1,235 @@ +# ======================================================================= # +# 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 # +# ======================================================================= # +import json +import shutil +from typing import List + +from components.moonraker.moonraker import Moonraker +from components.octoeverywhere import ( + OE_DEPS_JSON_FILE, + OE_DIR, + OE_ENV_DIR, + OE_INSTALL_SCRIPT, + OE_LOG_NAME, + OE_REPO, + OE_REQ_FILE, + OE_SYS_CFG_NAME, +) +from components.octoeverywhere.octoeverywhere import Octoeverywhere +from core.instance_manager.instance_manager import InstanceManager +from utils.common import check_install_dependencies, moonraker_exists +from utils.config_utils import ( + add_config_section, + remove_config_section, +) +from utils.fs_utils import remove_file +from utils.git_utils import git_clone_wrapper, git_pull_wrapper +from utils.input_utils import get_confirm +from utils.logger import DialogType, Logger +from utils.sys_utils import ( + cmd_sysctl_manage, + create_python_venv, + install_python_requirements, + parse_packages_from_file, +) + + +def install_octoeverywhere() -> None: + Logger.print_status("Installing OctoEverywhere for Klipper ...") + + # check if moonraker is installed. if not, notify the user and exit + if not moonraker_exists(): + return + + # if obico is already installed, ask if the user wants to repair an + # incomplete installation or link to the obico server + oe_im = InstanceManager(Octoeverywhere) + oe_instances: List[Octoeverywhere] = oe_im.instances + if oe_instances: + Logger.print_dialog( + DialogType.INFO, + [ + "OctoEverywhere is already installed!", + "It is save to run the installer again to link your " + "printer or repair any issues.", + ], + end="", + ) + if not get_confirm("Re-run OctoEverywhere installation?"): + Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") + return + else: + Logger.print_status("Re-Installing OctoEverywhere for Klipper ...") + + mr_im = InstanceManager(Moonraker) + mr_instances: List[Moonraker] = mr_im.instances + + mr_names = [f"● {moonraker.data_dir_name}" for moonraker in mr_instances] + if len(mr_names) > 1: + Logger.print_dialog( + DialogType.INFO, + [ + "The following Moonraker instances were found:", + *mr_names, + "\n\n", + "The setup will apply the same names to OctoEverywhere!", + ], + end="", + ) + + if not get_confirm( + "Continue OctoEverywhere for Klipper installation?", + default_choice=True, + allow_go_back=True, + ): + Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") + return + + try: + git_clone_wrapper(OE_REPO, OE_DIR) + install_oe_dependencies() + + for moonraker in mr_instances: + oe_im.current_instance = Octoeverywhere(suffix=moonraker.suffix) + oe_im.create_instance() + oe_im.enable_instance() + oe_im.start_instance() + + cmd_sysctl_manage("daemon-reload") + + # TODO: probably done by OE already? + # add to moonraker update manager + patch_moonraker_conf(mr_instances) + mr_im.restart_all_instance() + + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoEverywhere for Klipper successfully installed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error( + f"Error during OctoEverywhere for Klipper installation:\n{e}" + ) + + +def update_octoeverywhere() -> None: + Logger.print_status("Updating OctoEverywhere for Klipper ...") + try: + oe_im = InstanceManager(Octoeverywhere) + oe_im.stop_all_instance() + + git_pull_wrapper(OE_REPO, OE_DIR) + install_oe_dependencies() + + oe_im.start_all_instance() + Logger.print_ok("OctoEverywhere for Klipper successfully updated!") + + except Exception as e: + Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}") + + +def remove_octoeverywhere() -> None: + Logger.print_status("Removing OctoEverywhere for Klipper ...") + mr_im = InstanceManager(Moonraker) + mr_instances: List[Moonraker] = mr_im.instances + ob_im = InstanceManager(Octoeverywhere) + ob_instances: List[Octoeverywhere] = ob_im.instances + + try: + remove_oe_instances(ob_im, ob_instances) + remove_oe_dir() + remove_oe_env() + remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances) + delete_oe_logs(ob_instances) + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoEverywhere for Klipper successfully removed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}") + + +def install_oe_dependencies() -> None: + oe_deps = [] + if OE_DEPS_JSON_FILE.exists(): + with open(OE_DEPS_JSON_FILE, "r") as deps: + oe_deps = json.load(deps).get("debian", []) + elif OE_INSTALL_SCRIPT.exists(): + oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT) + + if not oe_deps: + raise ValueError("Error reading OctoEverywhere dependencies!") + + check_install_dependencies(oe_deps) + + # create virtualenv + create_python_venv(OE_ENV_DIR) + install_python_requirements(OE_ENV_DIR, OE_REQ_FILE) + + +def patch_moonraker_conf(instances: List[Moonraker]) -> None: + add_config_section(section=f"include {OE_SYS_CFG_NAME}", instances=instances) + + +def remove_oe_instances( + instance_manager: InstanceManager, + instance_list: List[Octoeverywhere], +) -> None: + if not instance_list: + Logger.print_info("No OctoEverywhere instances found. Skipped ...") + return + + for instance in instance_list: + Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...") + instance_manager.current_instance = instance + instance_manager.stop_instance() + instance_manager.disable_instance() + instance_manager.delete_instance() + + cmd_sysctl_manage("daemon-reload") + + +def remove_oe_dir() -> None: + if not OE_DIR.exists(): + Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...") + return + + try: + shutil.rmtree(OE_DIR) + except OSError as e: + Logger.print_error(f"Unable to delete '{OE_DIR}':\n{e}") + + +def remove_oe_env() -> None: + if not OE_ENV_DIR.exists(): + Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...") + return + + try: + shutil.rmtree(OE_ENV_DIR) + except OSError as e: + Logger.print_error(f"Unable to delete '{OE_ENV_DIR}':\n{e}") + + +def delete_oe_logs(instances: List[Octoeverywhere]) -> None: + Logger.print_status("Removing OctoEverywhere logs ...") + all_logfiles = [] + for instance in instances: + all_logfiles = list(instance.log_dir.glob(f"{OE_LOG_NAME}*")) + if not all_logfiles: + Logger.print_info("No OctoEverywhere logs found. Skipped ...") + return + + for log in all_logfiles: + Logger.print_status(f"Remove '{log}'") + remove_file(log) diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index 9458465..b538eb0 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -15,6 +15,7 @@ from components.klipper import klipper_setup from components.klipperscreen.klipperscreen import install_klipperscreen from components.mobileraker.mobileraker import install_mobileraker from components.moonraker import moonraker_setup +from components.octoeverywhere.octoeverywhere_setup import install_octoeverywhere 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 @@ -49,6 +50,7 @@ class InstallMenu(BaseMenu): "7": Option(method=self.install_klipperscreen, menu=False), "8": Option(method=self.install_mobileraker, menu=False), "9": Option(method=self.install_crowsnest, menu=False), + "10": Option(method=self.install_octoeverywhere, menu=False), } def print_menu(self): @@ -69,8 +71,8 @@ class InstallMenu(BaseMenu): ║ 4) [Fluidd] │ Webcam Streamer: ║ ║ │ 9) [Crowsnest] ║ ║ Client-Config: │ ║ - ║ 5) [Mainsail-Config] │ ║ - ║ 6) [Fluidd-Config] │ ║ + ║ 5) [Mainsail-Config] │ Remote Access: ║ + ║ 6) [Fluidd-Config] │ 10) [OctoEverywhere] ║ ║ │ ║ ╟───────────────────────────┴───────────────────────────╢ """ @@ -103,3 +105,6 @@ class InstallMenu(BaseMenu): def install_crowsnest(self, **kwargs): install_crowsnest() + + def install_octoeverywhere(self, **kwargs): + install_octoeverywhere() diff --git a/kiauh/core/menus/main_menu.py b/kiauh/core/menus/main_menu.py index 8de3a12..26f30d6 100644 --- a/kiauh/core/menus/main_menu.py +++ b/kiauh/core/menus/main_menu.py @@ -54,7 +54,7 @@ class MainMenu(BaseMenu): self.kl_status = self.kl_repo = self.mr_status = self.mr_repo = "" self.ms_status = self.fl_status = self.ks_status = self.mb_status = "" - self.cn_status = self.cc_status = "" + self.cn_status = self.cc_status = self.oe_status = "" self.init_status() def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: @@ -74,12 +74,12 @@ class MainMenu(BaseMenu): } def init_status(self) -> None: - status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn"] + status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn", "oe"] for var in status_vars: setattr( self, f"{var}_status", - f"{COLOR_RED}Not installed!{RESET_FORMAT}", + f"{COLOR_RED}Not installed{RESET_FORMAT}", ) def fetch_status(self) -> None: @@ -141,6 +141,7 @@ class MainMenu(BaseMenu): ║ │ ║ ║ Community: │ KlipperScreen: {self.ks_status:<{pad2}} ║ ║ E) [Extensions] │ Mobileraker: {self.mb_status:<{pad2}} ║ + ║ │ OctoEverywhere: {self.oe_status:<{pad2}} ║ ║ │ Crowsnest: {self.cn_status:<{pad2}} ║ ╟──────────────────┼────────────────────────────────────╢ ║ {footer1:^25} │ {footer2:^43} ║ diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index d43f998..6898ea2 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -17,6 +17,7 @@ from components.mobileraker.mobileraker import remove_mobileraker from components.moonraker.menus.moonraker_remove_menu import ( MoonrakerRemoveMenu, ) +from components.octoeverywhere.octoeverywhere_setup import remove_octoeverywhere 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 @@ -48,6 +49,7 @@ class RemoveMenu(BaseMenu): "5": Option(method=self.remove_klipperscreen, menu=True), "6": Option(method=self.remove_mobileraker, menu=True), "7": Option(method=self.remove_crowsnest, menu=True), + "8": Option(method=self.remove_octoeverywhere, menu=True), } def print_menu(self): @@ -61,14 +63,16 @@ class RemoveMenu(BaseMenu): ╟───────────────────────────────────────────────────────╢ ║ INFO: Configurations and/or any backups will be kept! ║ ╟───────────────────────────┬───────────────────────────╢ - ║ Firmware & API: │ Touchscreen GUI: ║ - ║ 1) [Klipper] │ 5) [KlipperScreen] ║ + ║ Firmware & API: │ Android / iOS: ║ + ║ 1) [Klipper] │ 6) [Mobileraker] ║ ║ 2) [Moonraker] │ ║ - ║ │ Android / iOS: ║ - ║ Klipper Webinterface: │ 6) [Mobileraker] ║ + ║ │ Webcam Streamer: ║ + ║ Klipper Webinterface: │ 7) [Crowsnest] ║ ║ 3) [Mainsail] │ ║ - ║ 4) [Fluidd] │ Webcam Streamer: ║ - ║ │ 7) [Crowsnest] ║ + ║ 4) [Fluidd] │ Remote Access: ║ + ║ │ 8) [OctoEverywhere] ║ + ║ Touchscreen GUI: │ ║ + ║ 5) [KlipperScreen] │ ║ ╟───────────────────────────┴───────────────────────────╢ """ )[1:] @@ -94,3 +98,6 @@ class RemoveMenu(BaseMenu): def remove_crowsnest(self, **kwargs): remove_crowsnest() + + def remove_octoeverywhere(self, **kwargs): + remove_octoeverywhere() From 83fdb715a2bc777ecc7c68544494d82e8c32d590 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 27 Jun 2024 18:09:06 +0200 Subject: [PATCH 2/5] refactor: remove redundant steps ocoeverywhere already takes care of Signed-off-by: Dominik Willner --- kiauh/components/octoeverywhere/octoeverywhere_setup.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kiauh/components/octoeverywhere/octoeverywhere_setup.py b/kiauh/components/octoeverywhere/octoeverywhere_setup.py index 4ee52dd..518288b 100644 --- a/kiauh/components/octoeverywhere/octoeverywhere_setup.py +++ b/kiauh/components/octoeverywhere/octoeverywhere_setup.py @@ -98,14 +98,7 @@ def install_octoeverywhere() -> None: for moonraker in mr_instances: oe_im.current_instance = Octoeverywhere(suffix=moonraker.suffix) oe_im.create_instance() - oe_im.enable_instance() - oe_im.start_instance() - cmd_sysctl_manage("daemon-reload") - - # TODO: probably done by OE already? - # add to moonraker update manager - patch_moonraker_conf(mr_instances) mr_im.restart_all_instance() Logger.print_dialog( From b19ba840ea441071c131d13b4e1064d84f1faddf Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 27 Jun 2024 20:15:29 +0200 Subject: [PATCH 3/5] refactor: add padding option to dialog Signed-off-by: Dominik Willner --- kiauh/components/crowsnest/crowsnest.py | 1 - kiauh/components/klipper/klipper_setup.py | 1 - kiauh/components/klipper/klipper_utils.py | 3 +-- .../menus/klipper_flash_menu.py | 1 - .../components/klipperscreen/klipperscreen.py | 1 - kiauh/components/mobileraker/mobileraker.py | 1 - .../components/webui_client/client_dialogs.py | 2 -- kiauh/core/menus/settings_menu.py | 1 - kiauh/core/settings/kiauh_settings.py | 1 - .../obico/moonraker_obico_extension.py | 4 ---- kiauh/utils/common.py | 1 - kiauh/utils/logger.py | 19 ++++++++----------- 12 files changed, 9 insertions(+), 27 deletions(-) diff --git a/kiauh/components/crowsnest/crowsnest.py b/kiauh/components/crowsnest/crowsnest.py index 99a30db..ed774c0 100644 --- a/kiauh/components/crowsnest/crowsnest.py +++ b/kiauh/components/crowsnest/crowsnest.py @@ -88,7 +88,6 @@ def print_multi_instance_warning(instances: List[Klipper]) -> None: "The following instances were found:", *_instances, ], - end="", ) diff --git a/kiauh/components/klipper/klipper_setup.py b/kiauh/components/klipper/klipper_setup.py index 4196f1f..85d7469 100644 --- a/kiauh/components/klipper/klipper_setup.py +++ b/kiauh/components/klipper/klipper_setup.py @@ -145,7 +145,6 @@ def update_klipper() -> None: "All Klipper instances will be restarted during the update process and " "ongoing prints WILL FAIL.", ], - end="", ) if not get_confirm("Update Klipper now?"): diff --git a/kiauh/components/klipper/klipper_utils.py b/kiauh/components/klipper/klipper_utils.py index d5c16ac..7ec7741 100644 --- a/kiauh/components/klipper/klipper_utils.py +++ b/kiauh/components/klipper/klipper_utils.py @@ -220,7 +220,6 @@ def check_user_groups(): "INFO:", "Relog required for group assignments to take effect!", ], - end="", ) if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"): @@ -272,7 +271,7 @@ def handle_disruptive_system_packages() -> None: "Please fix the problem manually. Otherwise, this may have " "undesirable effects on the operation of Klipper." ], - end="", + padding_bottom="", ) diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index eccd772..8af866c 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -353,7 +353,6 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu): "\n\n", "If you are unsure, stick to the default 250000!", ], - end="", ) self.flash_options.selected_baudrate = get_number_input( question="Please set the baud rate", diff --git a/kiauh/components/klipperscreen/klipperscreen.py b/kiauh/components/klipperscreen/klipperscreen.py index ae522c5..d64d500 100644 --- a/kiauh/components/klipperscreen/klipperscreen.py +++ b/kiauh/components/klipperscreen/klipperscreen.py @@ -62,7 +62,6 @@ def install_klipperscreen() -> None: "KlipperScreens update manager configuration for Moonraker " "will not be added to any moonraker.conf.", ], - end="", ) if not get_confirm( "Continue KlipperScreen installation?", diff --git a/kiauh/components/mobileraker/mobileraker.py b/kiauh/components/mobileraker/mobileraker.py index 17416a2..5fee326 100644 --- a/kiauh/components/mobileraker/mobileraker.py +++ b/kiauh/components/mobileraker/mobileraker.py @@ -58,7 +58,6 @@ def install_mobileraker() -> None: "Mobileraker's companion's update manager configuration for Moonraker " "will not be added to any moonraker.conf.", ], - end="", ) if not get_confirm( "Continue Mobileraker's companion installation?", diff --git a/kiauh/components/webui_client/client_dialogs.py b/kiauh/components/webui_client/client_dialogs.py index f737410..e723274 100644 --- a/kiauh/components/webui_client/client_dialogs.py +++ b/kiauh/components/webui_client/client_dialogs.py @@ -100,7 +100,6 @@ def print_install_client_config_dialog(client: BaseWebClient) -> None: "If you already use these macros skip this step. Otherwise you should " "consider to answer with 'Y' to download the recommended macros.", ], - end="", ) @@ -115,5 +114,4 @@ def print_ipv6_warning_dialog() -> None: "If you think this warning is a false alarm, and you are sure that " "IPv6 is disabled, you can continue with the installation.", ], - end="", ) diff --git a/kiauh/core/menus/settings_menu.py b/kiauh/core/menus/settings_menu.py index 579c098..322c80d 100644 --- a/kiauh/core/menus/settings_menu.py +++ b/kiauh/core/menus/settings_menu.py @@ -144,7 +144,6 @@ class SettingsMenu(BaseMenu): f"New {display_name} repository branch:", f"● {branch}", ], - end="", ) if get_confirm("Apply changes?", allow_go_back=True): diff --git a/kiauh/core/settings/kiauh_settings.py b/kiauh/core/settings/kiauh_settings.py index 7aff799..a813c0f 100644 --- a/kiauh/core/settings/kiauh_settings.py +++ b/kiauh/core/settings/kiauh_settings.py @@ -219,6 +219,5 @@ class KiauhSettings: "● default.kiauh.cfg", "● kiauh.cfg", ], - end="", ) kill() diff --git a/kiauh/extensions/obico/moonraker_obico_extension.py b/kiauh/extensions/obico/moonraker_obico_extension.py index bd70d79..2b18cf1 100644 --- a/kiauh/extensions/obico/moonraker_obico_extension.py +++ b/kiauh/extensions/obico/moonraker_obico_extension.py @@ -192,7 +192,6 @@ class ObicoExtension(BaseExtension): "http://server_ip:port", "For instance, 'http://192.168.0.5:3334'.", ], - end="", ) def _print_moonraker_instances(self, mr_instances) -> None: @@ -206,7 +205,6 @@ class ObicoExtension(BaseExtension): "\n\n", "The setup will apply the same names to Obico!", ], - end="", ) def _print_is_already_installed(self) -> None: @@ -221,7 +219,6 @@ class ObicoExtension(BaseExtension): "L) Link printer to the Obico server", "R) Repair installation", ], - end="", ) def _get_server_url(self) -> None: @@ -324,7 +321,6 @@ class ObicoExtension(BaseExtension): "If you don't want to link the printer now, you can restart the " "linking process later by running this installer again.", ], - end="", ) if not get_confirm("Do you want to link the printers now?"): Logger.print_info("Linking to Obico server skipped ...") diff --git a/kiauh/utils/common.py b/kiauh/utils/common.py index 28b0a6f..98d982b 100644 --- a/kiauh/utils/common.py +++ b/kiauh/utils/common.py @@ -150,7 +150,6 @@ def moonraker_exists(name: str = "") -> bool: "No Moonraker instances found!", f"{info}. Please install Moonraker first!", ], - end="", ) return False return True diff --git a/kiauh/utils/logger.py b/kiauh/utils/logger.py index ef25ee5..b4d7ae0 100644 --- a/kiauh/utils/logger.py +++ b/kiauh/utils/logger.py @@ -90,7 +90,8 @@ class Logger: center_content: bool = False, custom_title: str = None, custom_color: DialogCustomColor = None, - end: str = "\n", + padding_top: int = 1, + padding_bottom: int = 1, ) -> None: dialog_color = Logger._get_dialog_color(title, custom_color) dialog_title = Logger._get_dialog_title(title, custom_title) @@ -99,10 +100,12 @@ class Logger: top = Logger._format_top_border(dialog_color) bottom = Logger._format_bottom_border() + print("\n" * padding_top) print( f"{top}{dialog_title_formatted}{dialog_content}{bottom}", - end=end, + end="", ) + print("\n" * padding_bottom) @staticmethod def _get_dialog_title(title: DialogType, custom_title: str = None) -> str: @@ -120,18 +123,12 @@ class Logger: @staticmethod def _format_top_border(color: str) -> str: - return textwrap.dedent( - f""" - {color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - """ - )[1:-1] + return f"{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" @staticmethod def _format_bottom_border() -> str: - return textwrap.dedent( - f""" - ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - {RESET_FORMAT}""" + return ( + f"\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{RESET_FORMAT}" ) @staticmethod From 4df89c9917d1165ca52ef0a4f64a764dda84bca5 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 27 Jun 2024 20:23:34 +0200 Subject: [PATCH 4/5] refactor: oe uninstaller Signed-off-by: Dominik Willner --- .../octoeverywhere/octoeverywhere.py | 14 ++++++--- .../octoeverywhere/octoeverywhere_setup.py | 30 +++++++++---------- kiauh/utils/fs_utils.py | 24 +++++++++++++++ 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/kiauh/components/octoeverywhere/octoeverywhere.py b/kiauh/components/octoeverywhere/octoeverywhere.py index a4e530b..29b3c29 100644 --- a/kiauh/components/octoeverywhere/octoeverywhere.py +++ b/kiauh/components/octoeverywhere/octoeverywhere.py @@ -62,11 +62,17 @@ class Octoeverywhere(BaseInstance): raise def delete(self) -> None: - Logger.print_status("Removing OctoEverywhere for Klipper Instance ...") + service_file = self.get_service_file_name(extension=True) + service_file_path = self.get_service_file_path() + + Logger.print_status( + f"Deleting OctoEverywhere for Klipper Instance: {service_file}" + ) try: - cmd = f"OE_REMOVE_SCRIPT {self.cfg_dir}/moonraker.conf" - run(cmd, check=True, shell=True) + command = ["sudo", "rm", "-f", service_file_path] + run(command, check=True) + Logger.print_ok(f"Service file deleted: {service_file_path}") except CalledProcessError as e: - Logger.print_error(f"Error deleting instance: {e}") + Logger.print_error(f"Error deleting service file: {e}") raise diff --git a/kiauh/components/octoeverywhere/octoeverywhere_setup.py b/kiauh/components/octoeverywhere/octoeverywhere_setup.py index 518288b..61a3ba9 100644 --- a/kiauh/components/octoeverywhere/octoeverywhere_setup.py +++ b/kiauh/components/octoeverywhere/octoeverywhere_setup.py @@ -7,7 +7,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # import json -import shutil +from pathlib import Path from typing import List from components.moonraker.moonraker import Moonraker @@ -28,7 +28,7 @@ from utils.config_utils import ( add_config_section, remove_config_section, ) -from utils.fs_utils import remove_file +from utils.fs_utils import run_remove_routines from utils.git_utils import git_clone_wrapper, git_pull_wrapper from utils.input_utils import get_confirm from utils.logger import DialogType, Logger @@ -47,8 +47,6 @@ def install_octoeverywhere() -> None: if not moonraker_exists(): return - # if obico is already installed, ask if the user wants to repair an - # incomplete installation or link to the obico server oe_im = InstanceManager(Octoeverywhere) oe_instances: List[Octoeverywhere] = oe_im.instances if oe_instances: @@ -59,7 +57,6 @@ def install_octoeverywhere() -> None: "It is save to run the installer again to link your " "printer or repair any issues.", ], - end="", ) if not get_confirm("Re-run OctoEverywhere installation?"): Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") @@ -80,7 +77,6 @@ def install_octoeverywhere() -> None: "\n\n", "The setup will apply the same names to OctoEverywhere!", ], - end="", ) if not get_confirm( @@ -193,36 +189,40 @@ def remove_oe_instances( def remove_oe_dir() -> None: + Logger.print_status("Removing OctoEverywhere for Klipper directory ...") + if not OE_DIR.exists(): Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...") return - try: - shutil.rmtree(OE_DIR) - except OSError as e: - Logger.print_error(f"Unable to delete '{OE_DIR}':\n{e}") + run_remove_routines(OE_DIR) def remove_oe_env() -> None: + Logger.print_status("Removing OctoEverywhere for Klipper environment ...") + if not OE_ENV_DIR.exists(): Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...") return - try: - shutil.rmtree(OE_ENV_DIR) - except OSError as e: - Logger.print_error(f"Unable to delete '{OE_ENV_DIR}':\n{e}") + run_remove_routines(OE_ENV_DIR) def delete_oe_logs(instances: List[Octoeverywhere]) -> None: Logger.print_status("Removing OctoEverywhere logs ...") + all_logfiles = [] for instance in instances: all_logfiles = list(instance.log_dir.glob(f"{OE_LOG_NAME}*")) + + install_log = Path.home().joinpath("octoeverywhere-installer.log") + if install_log.exists(): + all_logfiles.append(install_log) + if not all_logfiles: Logger.print_info("No OctoEverywhere logs found. Skipped ...") return for log in all_logfiles: Logger.print_status(f"Remove '{log}'") - remove_file(log) + run_remove_routines(log) diff --git a/kiauh/utils/fs_utils.py b/kiauh/utils/fs_utils.py index 111294b..c2466c2 100644 --- a/kiauh/utils/fs_utils.py +++ b/kiauh/utils/fs_utils.py @@ -79,6 +79,30 @@ def remove_file(file_path: Path, sudo=False) -> None: raise +def run_remove_routines(file: Path) -> None: + try: + if not file.exists(): + Logger.print_info(f"File '{file}' does not exist. Skipped ...") + return + + if file.is_dir(): + shutil.rmtree(file) + elif file.is_file(): + file.unlink() + else: + raise OSError(f"File '{file}' is neither a file nor a directory!") + Logger.print_ok("Successfully removed!") + except OSError as e: + Logger.print_error(f"Unable to delete '{file}':\n{e}") + try: + Logger.print_info("Trying to remove with sudo ...") + remove_with_sudo(file) + Logger.print_ok("Successfully removed!") + except CalledProcessError as e: + Logger.print_error(f"Error deleting '{file}' with sudo:\n{e}") + Logger.print_error("Remove this directory manually!") + + def unzip(filepath: Path, target_dir: Path) -> None: """ Helper function to unzip a zip-archive into a target directory | From e06a4e3197b5d0e22b840be25821cf14ac72da73 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 27 Jun 2024 20:58:33 +0200 Subject: [PATCH 5/5] fix: add recursive removal of files Signed-off-by: Dominik Willner --- kiauh/utils/fs_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kiauh/utils/fs_utils.py b/kiauh/utils/fs_utils.py index c2466c2..7149aad 100644 --- a/kiauh/utils/fs_utils.py +++ b/kiauh/utils/fs_utils.py @@ -59,9 +59,9 @@ def create_symlink(source: Path, target: Path, sudo=False) -> None: raise -def remove_with_sudo(file_path: Path) -> None: +def remove_with_sudo(file: Path) -> None: try: - cmd = ["sudo", "rm", "-f", file_path] + cmd = ["sudo", "rm", "-rf", file] run(cmd, stderr=PIPE, check=True) except CalledProcessError as e: Logger.print_error(f"Failed to remove file: {e}")