diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py new file mode 100644 index 0000000..6597237 --- /dev/null +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -0,0 +1,18 @@ +# ======================================================================= # +# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # +# https://github.com/Staubgeborener/klipper-backup # +# # +# 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 + +EXT_MODULE_NAME = "klipper_backup_extension.py" +MODULE_PATH = Path(__file__).resolve().parent +MOONRAKER_CONF = Path.home().joinpath("printer_data", "config", "moonraker.conf") +KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup") +KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup") +KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py new file mode 100644 index 0000000..e10b0ce --- /dev/null +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -0,0 +1,150 @@ +# ======================================================================= # +# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # +# https://github.com/Staubgeborener/klipper-backup # +# # +# 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 os +import shutil +import subprocess + +from extensions.base_extension import BaseExtension +from extensions.klipper_backup import ( + KLIPPERBACKUP_REPO_URL, + KLIPPERBACKUP_DIR, + KLIPPERBACKUP_CONFIG_DIR, + MOONRAKER_CONF, +) + +from utils.filesystem_utils import check_file_exist +from utils.input_utils import get_confirm +from utils.logger import Logger + + +# noinspection PyMethodMayBeStatic +class KlipperbackupExtension(BaseExtension): + def install_extension(self, **kwargs) -> None: + if not KLIPPERBACKUP_DIR.exists(): + subprocess.run(["git", "clone", str(KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) + #subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) # Only for testing + subprocess.run(["chmod", "+x", str(KLIPPERBACKUP_DIR / "install.sh")]) + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh")]) + + def update_extension(self, **kwargs) -> None: + extension_installed = check_file_exist(KLIPPERBACKUP_DIR) + if not extension_installed: + Logger.print_info("Extension does not seem to be installed! Skipping ...") + return + else: + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"]) + + def remove_extension(self, **kwargs) -> None: + def is_service_installed(service_name): + command = ["systemctl", "status", service_name] + result = subprocess.run(command, capture_output=True, text=True) + # Doesn't matter whether the service is active or not, what matters is whether it is installed. So let's search for "Loaded:" in stdout + if "Loaded:" in result.stdout: + return True + else: + return False + + def uninstall_service(service_name): + try: + subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) + subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) + subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True) + service_path = f'/etc/systemd/system/{service_name}' + os.system(f'sudo rm {service_path}') + return True + except subprocess.CalledProcessError: + return False + + def check_crontab_entry(entry): + try: + crontab_content = subprocess.check_output(["crontab", "-l"], stderr=subprocess.DEVNULL, text=True) + except subprocess.CalledProcessError: + return False + for line in crontab_content.splitlines(): + if entry in line: + return True + return False + + extension_installed = check_file_exist(KLIPPERBACKUP_DIR) + if not extension_installed: + Logger.print_info("Extension does not seem to be installed! Skipping ...") + return + + def remove_moonraker_entry(): + original_file_path = MOONRAKER_CONF + comparison_file_path = os.path.join(str(KLIPPERBACKUP_DIR), 'install-files', 'moonraker.conf') + if not os.path.exists(original_file_path) or not os.path.exists(comparison_file_path): + return False + with open(original_file_path, 'r') as original_file, open(comparison_file_path, 'r') as comparison_file: + original_content = original_file.read() + comparison_content = comparison_file.read() + if comparison_content in original_content: + modified_content = original_content.replace(comparison_content, '').strip() + modified_content = "\n".join(line for line in modified_content.split("\n") if line.strip()) + with open(original_file_path, 'w') as original_file: + original_file.write(modified_content) + return True + else: + return False + + question = "Do you really want to remove the extension?" + if get_confirm(question, True, False): + + # Remove Klipper-Backup services + service_names = ["klipper-backup-on-boot.service", "klipper-backup-filewatch.service"] + for service_name in service_names: + try: + Logger.print_status(f"Check whether the service {service_name} is installed ...") + if is_service_installed(service_name): + Logger.print_info(f"Service {service_name} detected.") + if uninstall_service(service_name): + Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + else: + Logger.print_error(f"Error uninstalling the service {service_name}.") + else: + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") + except: + Logger.print_error(f"Unable to remove the service {service_name}") + + # Remove Klipper-Backup cron + Logger.print_status("Check for Klipper-Backup cron entry ...") + entry_to_check = "/klipper-backup/script.sh" + try: + if check_crontab_entry(entry_to_check): + crontab_content = subprocess.check_output(["crontab", "-l"], text=True) + modified_content = "\n".join(line for line in crontab_content.splitlines() if entry_to_check not in line) + subprocess.run(["crontab", "-"], input=modified_content, text=True, check=True) + Logger.print_ok("The Klipper-Backup entry has been removed from the crontab.") + else: + Logger.print_info("The Klipper-Backup entry is not present in the crontab. Skipping ...") + except: + Logger.print_error("Unable to remove the Klipper-Backup cron entry") + + # Remove Moonraker entry + Logger.print_status(f"Check for Klipper-Backup moonraker entry ...") + try: + if remove_moonraker_entry(): + Logger.print_ok("Klipper-Backup entry in moonraker.conf removed") + else: + Logger.print_info("Klipper-Backup entry not found in moonraker.conf. Skipping ...") + except: + Logger.print_error("Unknown error, either the moonraker.conf is not found or the Klipper-Backup entry under ~/klipper-backup/install-files/moonraker.conf. Skipping ...") + + # Remove Klipper-Backup + Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") + try: + shutil.rmtree(KLIPPERBACKUP_DIR) + config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) + if config_backup_exists: + shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) + Logger.print_ok("Extension Klipper-Backup successfully removed!") + except OSError as e: + Logger.print_error(f"Unable to remove extension: {e}") diff --git a/kiauh/extensions/klipper_backup/metadata.json b/kiauh/extensions/klipper_backup/metadata.json new file mode 100644 index 0000000..9e5ef7e --- /dev/null +++ b/kiauh/extensions/klipper_backup/metadata.json @@ -0,0 +1,10 @@ +{ + "metadata": { + "index": 3, + "module": "klipper_backup_extension", + "maintained_by": "Staubgeborener", + "display_name": "Klipper-Backup", + "description": "Backup all your klipper files in GitHub", + "updates": true + } +}