This commit is contained in:
dw-0
2024-06-27 20:58:34 +02:00
committed by GitHub
19 changed files with 394 additions and 40 deletions

View File

@@ -88,7 +88,6 @@ def print_multi_instance_warning(instances: List[Klipper]) -> None:
"The following instances were found:", "The following instances were found:",
*_instances, *_instances,
], ],
end="",
) )

View File

@@ -145,7 +145,6 @@ def update_klipper() -> None:
"All Klipper instances will be restarted during the update process and " "All Klipper instances will be restarted during the update process and "
"ongoing prints WILL FAIL.", "ongoing prints WILL FAIL.",
], ],
end="",
) )
if not get_confirm("Update Klipper now?"): if not get_confirm("Update Klipper now?"):

View File

@@ -220,7 +220,6 @@ def check_user_groups():
"INFO:", "INFO:",
"Relog required for group assignments to take effect!", "Relog required for group assignments to take effect!",
], ],
end="",
) )
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"): 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 " "Please fix the problem manually. Otherwise, this may have "
"undesirable effects on the operation of Klipper." "undesirable effects on the operation of Klipper."
], ],
end="", padding_bottom="",
) )

View File

@@ -353,7 +353,6 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
"\n\n", "\n\n",
"If you are unsure, stick to the default 250000!", "If you are unsure, stick to the default 250000!",
], ],
end="",
) )
self.flash_options.selected_baudrate = get_number_input( self.flash_options.selected_baudrate = get_number_input(
question="Please set the baud rate", question="Please set the baud rate",

View File

@@ -62,7 +62,6 @@ def install_klipperscreen() -> None:
"KlipperScreens update manager configuration for Moonraker " "KlipperScreens update manager configuration for Moonraker "
"will not be added to any moonraker.conf.", "will not be added to any moonraker.conf.",
], ],
end="",
) )
if not get_confirm( if not get_confirm(
"Continue KlipperScreen installation?", "Continue KlipperScreen installation?",

View File

@@ -58,7 +58,6 @@ def install_mobileraker() -> None:
"Mobileraker's companion's update manager configuration for Moonraker " "Mobileraker's companion's update manager configuration for Moonraker "
"will not be added to any moonraker.conf.", "will not be added to any moonraker.conf.",
], ],
end="",
) )
if not get_confirm( if not get_confirm(
"Continue Mobileraker's companion installation?", "Continue Mobileraker's companion installation?",

View File

@@ -0,0 +1,29 @@
# ======================================================================= #
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
# #
# 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"

View File

@@ -0,0 +1,78 @@
# ======================================================================= #
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
# #
# 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:
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:
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 service file: {e}")
raise

View File

@@ -0,0 +1,228 @@
# ======================================================================= #
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
# #
# 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
from pathlib import Path
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 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
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
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.",
],
)
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!",
],
)
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()
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:
Logger.print_status("Removing OctoEverywhere for Klipper directory ...")
if not OE_DIR.exists():
Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...")
return
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
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}'")
run_remove_routines(log)

View File

@@ -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 " "If you already use these macros skip this step. Otherwise you should "
"consider to answer with 'Y' to download the recommended macros.", "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 " "If you think this warning is a false alarm, and you are sure that "
"IPv6 is disabled, you can continue with the installation.", "IPv6 is disabled, you can continue with the installation.",
], ],
end="",
) )

View File

@@ -15,6 +15,7 @@ from components.klipper import klipper_setup
from components.klipperscreen.klipperscreen import install_klipperscreen from components.klipperscreen.klipperscreen import install_klipperscreen
from components.mobileraker.mobileraker import install_mobileraker from components.mobileraker.mobileraker import install_mobileraker
from components.moonraker import moonraker_setup 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 import client_setup
from components.webui_client.client_config import client_config_setup from components.webui_client.client_config import client_config_setup
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
@@ -49,6 +50,7 @@ class InstallMenu(BaseMenu):
"7": Option(method=self.install_klipperscreen, menu=False), "7": Option(method=self.install_klipperscreen, menu=False),
"8": Option(method=self.install_mobileraker, menu=False), "8": Option(method=self.install_mobileraker, menu=False),
"9": Option(method=self.install_crowsnest, menu=False), "9": Option(method=self.install_crowsnest, menu=False),
"10": Option(method=self.install_octoeverywhere, menu=False),
} }
def print_menu(self): def print_menu(self):
@@ -69,8 +71,8 @@ class InstallMenu(BaseMenu):
║ 4) [Fluidd] │ Webcam Streamer: ║ ║ 4) [Fluidd] │ Webcam Streamer: ║
║ │ 9) [Crowsnest] ║ ║ │ 9) [Crowsnest] ║
║ Client-Config: │ ║ ║ Client-Config: │ ║
║ 5) [Mainsail-Config] │ ║ 5) [Mainsail-Config] │ Remote Access:
║ 6) [Fluidd-Config] │ ║ 6) [Fluidd-Config] │ 10) [OctoEverywhere]
║ │ ║ ║ │ ║
╟───────────────────────────┴───────────────────────────╢ ╟───────────────────────────┴───────────────────────────╢
""" """
@@ -103,3 +105,6 @@ class InstallMenu(BaseMenu):
def install_crowsnest(self, **kwargs): def install_crowsnest(self, **kwargs):
install_crowsnest() install_crowsnest()
def install_octoeverywhere(self, **kwargs):
install_octoeverywhere()

View File

@@ -54,7 +54,7 @@ class MainMenu(BaseMenu):
self.kl_status = self.kl_repo = self.mr_status = self.mr_repo = "" 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.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() self.init_status()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None:
@@ -74,12 +74,12 @@ class MainMenu(BaseMenu):
} }
def init_status(self) -> None: 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: for var in status_vars:
setattr( setattr(
self, self,
f"{var}_status", f"{var}_status",
f"{COLOR_RED}Not installed!{RESET_FORMAT}", f"{COLOR_RED}Not installed{RESET_FORMAT}",
) )
def fetch_status(self) -> None: def fetch_status(self) -> None:
@@ -141,6 +141,7 @@ class MainMenu(BaseMenu):
║ │ ║ ║ │ ║
║ Community: │ KlipperScreen: {self.ks_status:<{pad2}} ║ Community: │ KlipperScreen: {self.ks_status:<{pad2}}
║ E) [Extensions] │ Mobileraker: {self.mb_status:<{pad2}} ║ E) [Extensions] │ Mobileraker: {self.mb_status:<{pad2}}
║ │ OctoEverywhere: {self.oe_status:<{pad2}}
║ │ Crowsnest: {self.cn_status:<{pad2}} ║ │ Crowsnest: {self.cn_status:<{pad2}}
╟──────────────────┼────────────────────────────────────╢ ╟──────────────────┼────────────────────────────────────╢
{footer1:^25}{footer2:^43} {footer1:^25}{footer2:^43}

View File

@@ -17,6 +17,7 @@ from components.mobileraker.mobileraker import remove_mobileraker
from components.moonraker.menus.moonraker_remove_menu import ( from components.moonraker.menus.moonraker_remove_menu import (
MoonrakerRemoveMenu, MoonrakerRemoveMenu,
) )
from components.octoeverywhere.octoeverywhere_setup import remove_octoeverywhere
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from components.webui_client.menus.client_remove_menu import ClientRemoveMenu 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), "5": Option(method=self.remove_klipperscreen, menu=True),
"6": Option(method=self.remove_mobileraker, menu=True), "6": Option(method=self.remove_mobileraker, menu=True),
"7": Option(method=self.remove_crowsnest, menu=True), "7": Option(method=self.remove_crowsnest, menu=True),
"8": Option(method=self.remove_octoeverywhere, menu=True),
} }
def print_menu(self): def print_menu(self):
@@ -61,14 +63,16 @@ class RemoveMenu(BaseMenu):
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ INFO: Configurations and/or any backups will be kept! ║ ║ INFO: Configurations and/or any backups will be kept! ║
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢
║ Firmware & API: │ Touchscreen GUI: ║ Firmware & API: │ Android / iOS:
║ 1) [Klipper] │ 5) [KlipperScreen] ║ 1) [Klipper] │ 6) [Mobileraker]
║ 2) [Moonraker] │ ║ ║ 2) [Moonraker] │ ║
║ │ Android / iOS: ║ │ Webcam Streamer:
║ Klipper Webinterface: │ 6) [Mobileraker] ║ Klipper Webinterface: │ 7) [Crowsnest]
║ 3) [Mainsail] │ ║ ║ 3) [Mainsail] │ ║
║ 4) [Fluidd] │ Webcam Streamer: ║ 4) [Fluidd] │ Remote Access:
║ │ 7) [Crowsnest] ║ │ 8) [OctoEverywhere]
║ Touchscreen GUI: │ ║
║ 5) [KlipperScreen] │ ║
╟───────────────────────────┴───────────────────────────╢ ╟───────────────────────────┴───────────────────────────╢
""" """
)[1:] )[1:]
@@ -94,3 +98,6 @@ class RemoveMenu(BaseMenu):
def remove_crowsnest(self, **kwargs): def remove_crowsnest(self, **kwargs):
remove_crowsnest() remove_crowsnest()
def remove_octoeverywhere(self, **kwargs):
remove_octoeverywhere()

View File

@@ -144,7 +144,6 @@ class SettingsMenu(BaseMenu):
f"New {display_name} repository branch:", f"New {display_name} repository branch:",
f"{branch}", f"{branch}",
], ],
end="",
) )
if get_confirm("Apply changes?", allow_go_back=True): if get_confirm("Apply changes?", allow_go_back=True):

View File

@@ -219,6 +219,5 @@ class KiauhSettings:
"● default.kiauh.cfg", "● default.kiauh.cfg",
"● kiauh.cfg", "● kiauh.cfg",
], ],
end="",
) )
kill() kill()

View File

@@ -192,7 +192,6 @@ class ObicoExtension(BaseExtension):
"http://server_ip:port", "http://server_ip:port",
"For instance, 'http://192.168.0.5:3334'.", "For instance, 'http://192.168.0.5:3334'.",
], ],
end="",
) )
def _print_moonraker_instances(self, mr_instances) -> None: def _print_moonraker_instances(self, mr_instances) -> None:
@@ -206,7 +205,6 @@ class ObicoExtension(BaseExtension):
"\n\n", "\n\n",
"The setup will apply the same names to Obico!", "The setup will apply the same names to Obico!",
], ],
end="",
) )
def _print_is_already_installed(self) -> None: def _print_is_already_installed(self) -> None:
@@ -221,7 +219,6 @@ class ObicoExtension(BaseExtension):
"L) Link printer to the Obico server", "L) Link printer to the Obico server",
"R) Repair installation", "R) Repair installation",
], ],
end="",
) )
def _get_server_url(self) -> None: 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 " "If you don't want to link the printer now, you can restart the "
"linking process later by running this installer again.", "linking process later by running this installer again.",
], ],
end="",
) )
if not get_confirm("Do you want to link the printers now?"): if not get_confirm("Do you want to link the printers now?"):
Logger.print_info("Linking to Obico server skipped ...") Logger.print_info("Linking to Obico server skipped ...")

View File

@@ -150,7 +150,6 @@ def moonraker_exists(name: str = "") -> bool:
"No Moonraker instances found!", "No Moonraker instances found!",
f"{info}. Please install Moonraker first!", f"{info}. Please install Moonraker first!",
], ],
end="",
) )
return False return False
return True return True

View File

@@ -59,9 +59,9 @@ def create_symlink(source: Path, target: Path, sudo=False) -> None:
raise raise
def remove_with_sudo(file_path: Path) -> None: def remove_with_sudo(file: Path) -> None:
try: try:
cmd = ["sudo", "rm", "-f", file_path] cmd = ["sudo", "rm", "-rf", file]
run(cmd, stderr=PIPE, check=True) run(cmd, stderr=PIPE, check=True)
except CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"Failed to remove file: {e}") Logger.print_error(f"Failed to remove file: {e}")
@@ -79,6 +79,30 @@ def remove_file(file_path: Path, sudo=False) -> None:
raise 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: def unzip(filepath: Path, target_dir: Path) -> None:
""" """
Helper function to unzip a zip-archive into a target directory | Helper function to unzip a zip-archive into a target directory |

View File

@@ -90,7 +90,8 @@ class Logger:
center_content: bool = False, center_content: bool = False,
custom_title: str = None, custom_title: str = None,
custom_color: DialogCustomColor = None, custom_color: DialogCustomColor = None,
end: str = "\n", padding_top: int = 1,
padding_bottom: int = 1,
) -> None: ) -> None:
dialog_color = Logger._get_dialog_color(title, custom_color) dialog_color = Logger._get_dialog_color(title, custom_color)
dialog_title = Logger._get_dialog_title(title, custom_title) dialog_title = Logger._get_dialog_title(title, custom_title)
@@ -99,10 +100,12 @@ class Logger:
top = Logger._format_top_border(dialog_color) top = Logger._format_top_border(dialog_color)
bottom = Logger._format_bottom_border() bottom = Logger._format_bottom_border()
print("\n" * padding_top)
print( print(
f"{top}{dialog_title_formatted}{dialog_content}{bottom}", f"{top}{dialog_title_formatted}{dialog_content}{bottom}",
end=end, end="",
) )
print("\n" * padding_bottom)
@staticmethod @staticmethod
def _get_dialog_title(title: DialogType, custom_title: str = None) -> str: def _get_dialog_title(title: DialogType, custom_title: str = None) -> str:
@@ -120,18 +123,12 @@ class Logger:
@staticmethod @staticmethod
def _format_top_border(color: str) -> str: def _format_top_border(color: str) -> str:
return textwrap.dedent( return f"{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
f"""
{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
"""
)[1:-1]
@staticmethod @staticmethod
def _format_bottom_border() -> str: def _format_bottom_border() -> str:
return textwrap.dedent( return (
f""" f"\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{RESET_FORMAT}"
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
{RESET_FORMAT}"""
) )
@staticmethod @staticmethod