Compare commits

...

4 Commits

Author SHA1 Message Date
dw-0
7f79f68209 refactor(Klipper): use warn dialog
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:29:53 +02:00
dw-0
a44508ead5 refactor: update dependency management
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:23:51 +02:00
dw-0
9342c94096 chore: cleanup and update toml, create editorconfig
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 20:18:09 +02:00
dw-0
ea78ba25e6 fix(crowsnest): fix multi instance steps
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 19:45:42 +02:00
10 changed files with 144 additions and 104 deletions

11
.editorconfig Normal file
View File

@@ -0,0 +1,11 @@
root = true
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.py]
max_line_length = 88

View File

@@ -1,20 +0,0 @@
[kiauh]
backup_before_update: False
[klipper]
repository_url: https://github.com/Klipper3d/klipper
branch: master
method: https
[moonraker]
repository_url: https://github.com/Arksine/moonraker
branch: master
method: https
[mainsail]
port: 80
unstable_releases: False
[fluidd]
port: 80
unstable_releases: False

View File

@@ -9,29 +9,30 @@
from __future__ import annotations from __future__ import annotations
import shutil import shutil
import textwrap import time
from pathlib import Path from pathlib import Path
from subprocess import run, CalledProcessError from subprocess import CalledProcessError, run
from typing import List, Dict, Literal, Union from typing import Dict, List, Literal, Union
from components.crowsnest import CROWSNEST_REPO, CROWSNEST_DIR, CROWSNEST_BACKUP_DIR from components.crowsnest import CROWSNEST_BACKUP_DIR, CROWSNEST_DIR, CROWSNEST_REPO
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from core.backup_manager.backup_manager import BackupManager from core.backup_manager.backup_manager import BackupManager
from core.instance_manager.instance_manager import InstanceManager from core.instance_manager.instance_manager import InstanceManager
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from utils.common import get_install_status, check_install_dependencies from utils.common import check_install_dependencies, get_install_status
from utils.constants import COLOR_CYAN, RESET_FORMAT, CURRENT_USER from utils.constants import CURRENT_USER
from utils.git_utils import ( from utils.git_utils import (
git_clone_wrapper,
get_repo_name,
get_local_commit, get_local_commit,
get_remote_commit, get_remote_commit,
get_repo_name,
git_clone_wrapper,
git_pull_wrapper, git_pull_wrapper,
) )
from utils.logger import Logger from utils.input_utils import get_confirm
from utils.logger import DialogType, Logger
from utils.sys_utils import ( from utils.sys_utils import (
parse_packages_from_file,
cmd_sysctl_service, cmd_sysctl_service,
parse_packages_from_file,
) )
@@ -44,14 +45,22 @@ def install_crowsnest() -> None:
# Step 3: Check for Multi Instance # Step 3: Check for Multi Instance
im = InstanceManager(Klipper) im = InstanceManager(Klipper)
instances: List[Klipper] = im.find_instances() instances: List[Klipper] = im.instances
if len(instances) > 1: if len(instances) > 1:
configure_multi_instance(instances) print_multi_instance_warning(instances)
if not get_confirm("Do you want to continue with the installation?"):
Logger.print_info("Crowsnest installation aborted!")
return
Logger.print_status("Launching crowsnest's install configurator ...")
time.sleep(3)
configure_multi_instance()
# Step 4: Launch crowsnest installer # Step 4: Launch crowsnest installer
print(f"{COLOR_CYAN}Installer will prompt you for sudo password!{RESET_FORMAT}")
Logger.print_status("Launching crowsnest installer ...") Logger.print_status("Launching crowsnest installer ...")
Logger.print_info("Installer will prompt you for sudo password!")
try: try:
run( run(
f"sudo make install BASE_USER={CURRENT_USER}", f"sudo make install BASE_USER={CURRENT_USER}",
@@ -64,20 +73,25 @@ def install_crowsnest() -> None:
return return
def configure_multi_instance(instances: List[Klipper]) -> None: def print_multi_instance_warning(instances: List[Klipper]) -> None:
Logger.print_status("Multi instance install detected ...") _instances = [f"{instance.data_dir_name}" for instance in instances]
info = textwrap.dedent(""" Logger.print_dialog(
Crowsnest is NOT designed to support multi instances. DialogType.WARNING,
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. "Multi instance install detected!",
Found the following instances: "\n\n",
""")[:-1] "Crowsnest is NOT designed to support multi instances. A workaround "
print(info, end="") "for this is to choose the most used instance as a 'master' and use "
for instance in instances: "this instance to set up your 'crowsnest.conf' and steering it's service.",
print(f"{instance.data_dir_name}") "\n\n",
"The following instances were found:",
*_instances,
],
end="",
)
Logger.print_status("\nLaunching crowsnest's configuration tool ...")
def configure_multi_instance() -> None:
config = Path(CROWSNEST_DIR).joinpath("tools/.config") config = Path(CROWSNEST_DIR).joinpath("tools/.config")
try: try:
run( run(
@@ -94,7 +108,6 @@ def configure_multi_instance(instances: List[Klipper]) -> None:
if not config.exists(): if not config.exists():
Logger.print_error("Generating .config failed, installation aborted") Logger.print_error("Generating .config failed, installation aborted")
return
def update_crowsnest() -> None: def update_crowsnest() -> None:

View File

@@ -74,6 +74,7 @@ def install_klipper() -> None:
try: try:
if not kl_im.instances: if not kl_im.instances:
check_install_dependencies(["git"])
setup_klipper_prerequesites() setup_klipper_prerequesites()
count = 0 count = 0

View File

@@ -12,7 +12,6 @@ import os
import re import re
import shutil import shutil
import subprocess import subprocess
import textwrap
from typing import List, Union, Literal, Dict, Optional from typing import List, Union, Literal, Dict, Optional
from components.klipper import ( from components.klipper import (
@@ -44,7 +43,7 @@ from utils.common import get_install_status_common
from utils.constants import CURRENT_USER from utils.constants import CURRENT_USER
from utils.git_utils import get_repo_name, get_remote_commit, get_local_commit from utils.git_utils import get_repo_name, get_remote_commit, get_local_commit
from utils.input_utils import get_confirm, get_string_input, get_number_input from utils.input_utils import get_confirm, get_string_input, get_number_input
from utils.logger import Logger from utils.logger import Logger, DialogType
from utils.sys_utils import cmd_sysctl_service from utils.sys_utils import cmd_sysctl_service
@@ -98,8 +97,9 @@ def update_name_scheme(
klipper_instances: List[Klipper], klipper_instances: List[Klipper],
moonraker_instances: List[Moonraker], moonraker_instances: List[Moonraker],
) -> NameScheme: ) -> NameScheme:
# if there are more moonraker instances installed than klipper, we # if there are more moonraker instances installed
# load their names into the name_dict, as we will detect and enforce that naming scheme # than klipper, we load their names into the name_dict,
# as we will detect and enforce that naming scheme
if len(moonraker_instances) > len(klipper_instances): if len(moonraker_instances) > len(klipper_instances):
update_name_dict(name_dict, moonraker_instances) update_name_dict(name_dict, moonraker_instances)
return detect_name_scheme(moonraker_instances) return detect_name_scheme(moonraker_instances)
@@ -141,7 +141,11 @@ def get_install_count() -> Union[int, None]:
""" """
kl_instances = InstanceManager(Klipper).instances kl_instances = InstanceManager(Klipper).instances
print_select_instance_count_dialog() print_select_instance_count_dialog()
question = f"Number of{' additional' if len(kl_instances) > 0 else ''} Klipper instances to set up" question = (
f"Number of"
f"{' additional' if len(kl_instances) > 0 else ''} "
f"Klipper instances to set up"
)
return get_number_input(question, 1, default=1, allow_go_back=True) return get_number_input(question, 1, default=1, allow_go_back=True)
@@ -193,7 +197,8 @@ def klipper_to_multi_conversion(new_name: str) -> None:
else: else:
Logger.print_info(f"Existing '{new_instance.data_dir}' found ...") Logger.print_info(f"Existing '{new_instance.data_dir}' found ...")
# patch the virtual_sdcard sections path value to match the new printer_data foldername # patch the virtual_sdcard sections path
# value to match the new printer_data foldername
cm = ConfigManager(new_instance.cfg_file) cm = ConfigManager(new_instance.cfg_file)
if cm.config.has_section("virtual_sdcard"): if cm.config.has_section("virtual_sdcard"):
cm.set_value("virtual_sdcard", "path", str(new_instance.gcodes_dir)) cm.set_value("virtual_sdcard", "path", str(new_instance.gcodes_dir))
@@ -260,14 +265,15 @@ def handle_disruptive_system_packages() -> None:
try: try:
cmd_sysctl_service(service, "mask") cmd_sysctl_service(service, "mask")
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
warn_msg = textwrap.dedent( Logger.print_dialog(
f""" DialogType.WARNING,
KIAUH was unable to mask the {service} system service. [
Please fix the problem manually. Otherwise, this may have f"KIAUH was unable to mask the {service} system service. "
undesirable effects on the operation of Klipper. "Please fix the problem manually. Otherwise, this may have "
""" "undesirable effects on the operation of Klipper."
)[1:] ],
Logger.print_warn(warn_msg) end="",
)
def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme: def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme:

View File

@@ -52,14 +52,17 @@ def install_klipperscreen() -> None:
mr_im = InstanceManager(Moonraker) mr_im = InstanceManager(Moonraker)
mr_instances = mr_im.instances mr_instances = mr_im.instances
if not mr_instances: if not mr_instances:
warn_msg = [ Logger.print_dialog(
"Moonraker not found! KlipperScreen will not properly work " DialogType.WARNING,
"without a working Moonraker installation.", [
"\n\n", "Moonraker not found! KlipperScreen will not properly work "
"KlipperScreens update manager configuration for Moonraker " "without a working Moonraker installation.",
"will not be added to any moonraker.conf.", "\n\n",
] "KlipperScreens update manager configuration for Moonraker "
Logger.print_dialog(DialogType.WARNING, warn_msg) "will not be added to any moonraker.conf.",
],
end="",
)
if not get_confirm( if not get_confirm(
"Continue KlipperScreen installation?", "Continue KlipperScreen installation?",
default_choice=False, default_choice=False,
@@ -67,7 +70,7 @@ def install_klipperscreen() -> None:
): ):
return return
package_list = ["wget", "curl", "unzip", "dfu-util"] package_list = ["git", "wget", "curl", "unzip", "dfu-util"]
check_install_dependencies(package_list) check_install_dependencies(package_list)
git_clone_wrapper(KLIPPERSCREEN_REPO, KLIPPERSCREEN_DIR) git_clone_wrapper(KLIPPERSCREEN_REPO, KLIPPERSCREEN_DIR)
@@ -80,7 +83,8 @@ def install_klipperscreen() -> None:
mr_im.restart_all_instance() mr_im.restart_all_instance()
else: else:
Logger.print_info( Logger.print_info(
"Moonraker is not installed! Cannot add KlipperScreen to update manager!" "Moonraker is not installed! Cannot add "
"KlipperScreen to update manager!"
) )
Logger.print_ok("KlipperScreen successfully installed!") Logger.print_ok("KlipperScreen successfully installed!")
except CalledProcessError as e: except CalledProcessError as e:

View File

@@ -52,13 +52,16 @@ def install_mobileraker() -> None:
mr_im = InstanceManager(Moonraker) mr_im = InstanceManager(Moonraker)
mr_instances = mr_im.instances mr_instances = mr_im.instances
if not mr_instances: if not mr_instances:
warn_msg = [ Logger.print_dialog(
"Moonraker not found! Mobileraker's companion will not properly work " DialogType.WARNING,
"without a working Moonraker installation.", [
"Mobileraker's companion's update manager configuration for Moonraker " "Moonraker not found! Mobileraker's companion will not properly work "
"will not be added to any moonraker.conf.", "without a working Moonraker installation.",
] "Mobileraker's companion's update manager configuration for Moonraker "
Logger.print_dialog(DialogType.WARNING, warn_msg) "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?",
default_choice=False, default_choice=False,
@@ -66,7 +69,7 @@ def install_mobileraker() -> None:
): ):
return return
package_list = ["wget", "curl", "unzip", "dfu-util"] package_list = ["git", "wget", "curl", "unzip", "dfu-util"]
check_install_dependencies(package_list) check_install_dependencies(package_list)
git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR) git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR)
@@ -79,7 +82,8 @@ def install_mobileraker() -> None:
mr_im.restart_all_instance() mr_im.restart_all_instance()
else: else:
Logger.print_info( Logger.print_info(
"Moonraker is not installed! Cannot add Mobileraker's companion to update manager!" "Moonraker is not installed! Cannot add Mobileraker's "
"companion to update manager!"
) )
Logger.print_ok("Mobileraker's companion successfully installed!") Logger.print_ok("Mobileraker's companion successfully installed!")
except CalledProcessError as e: except CalledProcessError as e:

View File

@@ -6,7 +6,7 @@
# # # #
# This file may be distributed under the terms of the GNU GPLv3 license # # This file may be distributed under the terms of the GNU GPLv3 license #
# ======================================================================= # # ======================================================================= #
import json
import subprocess import subprocess
from pathlib import Path from pathlib import Path
@@ -86,32 +86,40 @@ def install_moonraker() -> None:
instance_names.append(klipper_instances[index].suffix) instance_names.append(klipper_instances[index].suffix)
create_example_cfg = get_confirm("Create example moonraker.conf?") create_example_cfg = get_confirm("Create example moonraker.conf?")
setup_moonraker_prerequesites()
install_moonraker_polkit()
used_ports_map = { try:
instance.suffix: instance.port for instance in moonraker_instances check_install_dependencies(["git"])
} setup_moonraker_prerequesites()
for name in instance_names: install_moonraker_polkit()
current_instance = Moonraker(suffix=name)
mr_im.current_instance = current_instance used_ports_map = {
mr_im.create_instance() instance.suffix: instance.port for instance in moonraker_instances
mr_im.enable_instance() }
for name in instance_names:
current_instance = Moonraker(suffix=name)
if create_example_cfg: mr_im.current_instance = current_instance
# if a webclient and/or it's config is installed, patch its update section to the config mr_im.create_instance()
clients = get_existing_clients() mr_im.enable_instance()
create_example_moonraker_conf(current_instance, used_ports_map, clients)
mr_im.start_instance() if create_example_cfg:
# if a webclient and/or it's config is installed, patch
# its update section to the config
clients = get_existing_clients()
create_example_moonraker_conf(current_instance, used_ports_map, clients)
mr_im.reload_daemon() mr_im.start_instance()
# if mainsail is installed, and we installed mr_im.reload_daemon()
# multiple moonraker instances, we enable mainsails remote mode
if MainsailData().client_dir.exists() and len(mr_im.instances) > 1: # if mainsail is installed, and we installed
enable_mainsail_remotemode() # multiple moonraker instances, we enable mainsails remote mode
if MainsailData().client_dir.exists() and len(mr_im.instances) > 1:
enable_mainsail_remotemode()
except Exception as e:
Logger.print_error(f"Error while installing Moonraker: {e}")
return
def check_moonraker_install_requirements() -> bool: def check_moonraker_install_requirements() -> bool:
@@ -140,9 +148,19 @@ def setup_moonraker_prerequesites() -> None:
def install_moonraker_packages(moonraker_dir: Path) -> None: def install_moonraker_packages(moonraker_dir: Path) -> None:
script = moonraker_dir.joinpath("scripts/install-moonraker.sh") install_script = moonraker_dir.joinpath("scripts/install-moonraker.sh")
packages = parse_packages_from_file(script) deps_json = MOONRAKER_DIR.joinpath("scripts/system-dependencies.json")
check_install_dependencies(packages) moonraker_deps = []
if deps_json.exists():
moonraker_deps = json.load(deps_json).get("debian", [])
elif install_script.exists():
moonraker_deps = parse_packages_from_file(install_script)
if not moonraker_deps:
raise ValueError("Error reading Moonraker dependencies!")
check_install_dependencies(moonraker_deps)
def install_moonraker_polkit() -> None: def install_moonraker_polkit() -> None:

View File

@@ -119,7 +119,7 @@ def install_client(client: BaseWebClient) -> None:
) )
valid_port = is_valid_port(port, ports_in_use) valid_port = is_valid_port(port, ports_in_use)
check_install_dependencies(["nginx"]) check_install_dependencies(["nginx", "unzip"])
try: try:
download_client(client) download_client(client)

View File

@@ -1,6 +1,9 @@
[project] [project]
requires-python = ">=3.8" requires-python = ">=3.8"
[project.optional-dependencies]
dev=["ruff"]
[tool.ruff] [tool.ruff]
required-version = ">=0.3.4" required-version = ">=0.3.4"
respect-gitignore = true respect-gitignore = true