From 2d858cf2418519f4f3d8f69c90aee5d9441aa69e Mon Sep 17 00:00:00 2001 From: dw-0 Date: Wed, 1 May 2024 16:03:26 +0200 Subject: [PATCH] feat: add crowsnest install/remove WIP Signed-off-by: Dominik Willner --- kiauh/components/crowsnest/__init__.py | 13 +++ kiauh/components/crowsnest/crowsnest.py | 113 ++++++++++++++++++++++++ kiauh/core/menus/install_menu.py | 5 ++ kiauh/core/menus/remove_menu.py | 5 ++ 4 files changed, 136 insertions(+) create mode 100644 kiauh/components/crowsnest/__init__.py create mode 100644 kiauh/components/crowsnest/crowsnest.py diff --git a/kiauh/components/crowsnest/__init__.py b/kiauh/components/crowsnest/__init__.py new file mode 100644 index 0000000..c68284b --- /dev/null +++ b/kiauh/components/crowsnest/__init__.py @@ -0,0 +1,13 @@ +# ======================================================================= # +# 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 + +CROWSNEST_DIR = Path.home().joinpath("crowsnest") +CROWSNEST_REPO = "https://github.com/mainsail-crew/crowsnest.git" diff --git a/kiauh/components/crowsnest/crowsnest.py b/kiauh/components/crowsnest/crowsnest.py new file mode 100644 index 0000000..5ea960d --- /dev/null +++ b/kiauh/components/crowsnest/crowsnest.py @@ -0,0 +1,113 @@ +# ======================================================================= # +# 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 + +import shutil +import textwrap +from pathlib import Path +from subprocess import run, CalledProcessError +from typing import List + +from components.crowsnest import CROWSNEST_REPO, CROWSNEST_DIR +from components.klipper.klipper import Klipper +from core.instance_manager.instance_manager import InstanceManager +from utils.constants import COLOR_CYAN, RESET_FORMAT, CURRENT_USER +from utils.git_utils import git_clone_wrapper +from utils.input_utils import get_confirm +from utils.logger import Logger +from utils.system_utils import check_package_install, install_system_packages + + +def install_crowsnest() -> None: + # Step 1: Clone crowsnest repo + git_clone_wrapper(CROWSNEST_REPO, "master", CROWSNEST_DIR) + + # Step 2: Install dependencies + requirements: List[str] = check_package_install(["make"]) + if requirements: + install_system_packages(requirements) + + # Step 3: Check for Multi Instance + im = InstanceManager(Klipper) + instances: List[Klipper] = im.find_instances() + + if len(instances) > 1: + Logger.print_status("Multi instance install detected ...") + info = textwrap.dedent(""" + Crowsnest is NOT designed to support multi instances. + A workaround for this is to choose the most used instance as a 'master' + Use this instance to set up your 'crowsnest.conf' and steering it's service. + Found the following instances: + """)[:-1] + print(info, end="") + for instance in instances: + print(f"● {instance.data_dir_name}") + + Logger.print_status("\nLaunching crowsnest's configuration tool ...") + + if not get_confirm("Continue with configuration?", False, allow_go_back=True): + Logger.print_info("Installation aborted by user ... Exiting!") + return + + config = Path(CROWSNEST_DIR).joinpath("tools/.config") + try: + run( + "make config", + cwd=CROWSNEST_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Something went wrong! Please try again...\n{e}") + if config.exists(): + Path.unlink(config) + return + + if not config.exists(): + Logger.print_error("Generating .config failed, installation aborted") + return + + # Step 4: Launch crowsnest installer + print(f"{COLOR_CYAN}Installer will prompt you for sudo password!{RESET_FORMAT}") + Logger.print_status("Launching crowsnest installer ...") + try: + run( + f"sudo make install BASE_USER={CURRENT_USER}", + cwd=CROWSNEST_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Something went wrong! Please try again...\n{e}") + return + + +def update_crowsnest() -> None: + pass + + +def remove_crowsnest() -> None: + if not CROWSNEST_DIR.exists(): + Logger.print_info("Crowsnest does not seem to be installed! Skipping ...") + return + + try: + run( + "make uninstall", + cwd=CROWSNEST_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Something went wrong! Please try again...\n{e}") + return + + Logger.print_status("Removing crowsnest directory ...") + shutil.rmtree(CROWSNEST_DIR) + Logger.print_ok("Directory removed!") diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index ce42419..4d69b9e 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -10,6 +10,7 @@ import textwrap from typing import Type, Optional +from components.crowsnest.crowsnest import install_crowsnest from components.klipper import klipper_setup from components.moonraker import moonraker_setup from components.webui_client import client_setup @@ -44,6 +45,7 @@ class InstallMenu(BaseMenu): "4": Option(method=self.install_fluidd, menu=False), "5": Option(method=self.install_mainsail_config, menu=False), "6": Option(method=self.install_fluidd_config, menu=False), + "9": Option(method=self.install_crowsnest, menu=False), } def print_menu(self): @@ -88,3 +90,6 @@ class InstallMenu(BaseMenu): def install_fluidd_config(self, **kwargs): client_config_setup.install_client_config(FluiddData()) + + def install_crowsnest(self, **kwargs): + install_crowsnest() diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index d7ce2c0..b6f11b0 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -10,6 +10,7 @@ import textwrap from typing import Type, Optional +from components.crowsnest.crowsnest import remove_crowsnest from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu from components.moonraker.menus.moonraker_remove_menu import ( MoonrakerRemoveMenu, @@ -42,6 +43,7 @@ class RemoveMenu(BaseMenu): "2": Option(method=self.remove_moonraker, menu=True), "3": Option(method=self.remove_mainsail, menu=True), "4": Option(method=self.remove_fluidd, menu=True), + "6": Option(method=self.remove_crowsnest, menu=True), } def print_menu(self): @@ -78,3 +80,6 @@ class RemoveMenu(BaseMenu): def remove_fluidd(self, **kwargs): ClientRemoveMenu(previous_menu=self.__class__, client=FluiddData()).run() + + def remove_crowsnest(self, **kwargs): + remove_crowsnest()