Release v6.0.0-alpha.14

fixes #607
fixes #615
fixes #618
fixes #619
fixes #620
fixes #623
fixes #627
This commit is contained in:
dw-0
2025-02-09 21:15:42 +01:00
committed by GitHub
13 changed files with 294 additions and 42 deletions

View File

@@ -27,7 +27,6 @@ from components.crowsnest import (
) )
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.constants import CURRENT_USER
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from core.types.component_status import ComponentStatus from core.types.component_status import ComponentStatus
@@ -73,7 +72,7 @@ def install_crowsnest() -> None:
Logger.print_info("Installer will prompt you for sudo password!") 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",
cwd=CROWSNEST_DIR, cwd=CROWSNEST_DIR,
shell=True, shell=True,
check=True, check=True,

View File

@@ -25,6 +25,7 @@ KLIPPER_SERVICE_NAME = "klipper.service"
# directories # directories
KLIPPER_DIR = Path.home().joinpath("klipper") KLIPPER_DIR = Path.home().joinpath("klipper")
KLIPPER_KCONFIGS_DIR = Path.home().joinpath("klipper-kconfigs")
KLIPPER_ENV_DIR = Path.home().joinpath("klippy-env") KLIPPER_ENV_DIR = Path.home().joinpath("klippy-env")
KLIPPER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("klipper-backups") KLIPPER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("klipper-backups")

View File

@@ -7,6 +7,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 re import re
from pathlib import Path
from subprocess import ( from subprocess import (
DEVNULL, DEVNULL,
PIPE, PIPE,
@@ -138,6 +139,7 @@ def start_flash_process(flash_options: FlashOptions) -> None:
if flash_options.flash_method is FlashMethod.REGULAR: if flash_options.flash_method is FlashMethod.REGULAR:
cmd = [ cmd = [
"make", "make",
f"KCONFIG_CONFIG={flash_options.selected_kconfig}",
flash_options.flash_command.value, flash_options.flash_command.value,
f"FLASH_DEVICE={flash_options.selected_mcu}", f"FLASH_DEVICE={flash_options.selected_mcu}",
] ]
@@ -165,17 +167,17 @@ def start_flash_process(flash_options: FlashOptions) -> None:
if rc != 0: if rc != 0:
raise Exception(f"Flashing failed with returncode: {rc}") raise Exception(f"Flashing failed with returncode: {rc}")
else: else:
Logger.print_ok("Flashing successfull!", start="\n", end="\n\n") Logger.print_ok("Flashing successful!", start="\n", end="\n\n")
except (Exception, CalledProcessError): except (Exception, CalledProcessError):
Logger.print_error("Flashing failed!", start="\n") Logger.print_error("Flashing failed!", start="\n")
Logger.print_error("See the console output above!", end="\n\n") Logger.print_error("See the console output above!", end="\n\n")
def run_make_clean() -> None: def run_make_clean(kconfig=Path(KLIPPER_DIR.joinpath(".config"))) -> None:
try: try:
run( run(
"make clean", f"make KCONFIG_CONFIG={kconfig} clean",
cwd=KLIPPER_DIR, cwd=KLIPPER_DIR,
shell=True, shell=True,
check=True, check=True,
@@ -185,10 +187,10 @@ def run_make_clean() -> None:
raise raise
def run_make_menuconfig() -> None: def run_make_menuconfig(kconfig=Path(KLIPPER_DIR.joinpath(".config"))) -> None:
try: try:
run( run(
"make PYTHON=python3 menuconfig", f"make PYTHON=python3 KCONFIG_CONFIG={kconfig} menuconfig",
cwd=KLIPPER_DIR, cwd=KLIPPER_DIR,
shell=True, shell=True,
check=True, check=True,
@@ -198,10 +200,10 @@ def run_make_menuconfig() -> None:
raise raise
def run_make() -> None: def run_make(kconfig=Path(KLIPPER_DIR.joinpath(".config"))) -> None:
try: try:
run( run(
"make PYTHON=python3", f"make PYTHON=python3 KCONFIG_CONFIG={kconfig}",
cwd=KLIPPER_DIR, cwd=KLIPPER_DIR,
shell=True, shell=True,
check=True, check=True,

View File

@@ -39,6 +39,7 @@ class FlashOptions:
_selected_mcu: str = "" _selected_mcu: str = ""
_selected_board: str = "" _selected_board: str = ""
_selected_baudrate: int = 250000 _selected_baudrate: int = 250000
_selected_kconfig: str = ".config"
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if not cls._instance: if not cls._instance:
@@ -104,3 +105,11 @@ class FlashOptions:
@selected_baudrate.setter @selected_baudrate.setter
def selected_baudrate(self, value: int) -> None: def selected_baudrate(self, value: int) -> None:
self._selected_baudrate = value self._selected_baudrate = value
@property
def selected_kconfig(self) -> str:
return self._selected_kconfig
@selected_kconfig.setter
def selected_kconfig(self, value: str) -> None:
self._selected_kconfig = value

View File

@@ -9,18 +9,22 @@
from __future__ import annotations from __future__ import annotations
import textwrap import textwrap
from pathlib import Path
from shutil import copyfile
from typing import List, Set, Type from typing import List, Set, Type
from components.klipper import KLIPPER_DIR from components.klipper import KLIPPER_DIR, KLIPPER_KCONFIGS_DIR
from components.klipper_firmware.firmware_utils import ( from components.klipper_firmware.firmware_utils import (
run_make, run_make,
run_make_clean, run_make_clean,
run_make_menuconfig, run_make_menuconfig,
) )
from core.logger import Logger from components.klipper_firmware.flash_options import FlashOptions
from core.logger import DialogType, Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color from core.types.color import Color
from utils.input_utils import get_confirm, get_string_input
from utils.sys_utils import ( from utils.sys_utils import (
check_package_install, check_package_install,
install_system_packages, install_system_packages,
@@ -30,14 +34,25 @@ from utils.sys_utils import (
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperBuildFirmwareMenu(BaseMenu): class KlipperKConfigMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Build Firmware Menu" self.title = "Firmware Config Menu"
self.title_color = Color.CYAN self.title_color = Color.CYAN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.deps: Set[str] = {"build-essential", "dpkg-dev", "make"} self.flash_options = FlashOptions()
self.missing_deps: List[str] = check_package_install(self.deps) self.kconfigs_dirname = KLIPPER_KCONFIGS_DIR
self.kconfig_default = KLIPPER_DIR.joinpath(".config")
self.configs: List[Path] = []
self.kconfig = (
self.kconfig_default if not Path(self.kconfigs_dirname).is_dir() else None
)
def run(self) -> None:
if not self.kconfig:
super().run()
else:
self.flash_options.selected_kconfig = self.kconfig
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.advanced_menu import AdvancedMenu from core.menus.advanced_menu import AdvancedMenu
@@ -47,16 +62,107 @@ class KlipperBuildFirmwareMenu(BaseMenu):
) )
def set_options(self) -> None: def set_options(self) -> None:
if len(self.missing_deps) == 0: if not Path(self.kconfigs_dirname).is_dir():
self.input_label_txt = "Press ENTER to continue" return
self.default_option = Option(method=self.start_build_process)
else: self.input_label_txt = "Select config or action to continue (default=N)"
self.input_label_txt = "Press ENTER to install dependencies" self.default_option = Option(
self.default_option = Option(method=self.install_missing_deps) method=self.select_config, opt_data=self.kconfig_default
)
option_index = 1
for kconfig in Path(self.kconfigs_dirname).iterdir():
if not kconfig.name.endswith(".config"):
continue
kconfig_path = self.kconfigs_dirname.joinpath(kconfig)
if Path(kconfig_path).is_file():
self.configs += [kconfig]
self.options[str(option_index)] = Option(
method=self.select_config, opt_data=kconfig_path
)
option_index += 1
self.options["n"] = Option(
method=self.select_config, opt_data=self.kconfig_default
)
def print_menu(self) -> None: def print_menu(self) -> None:
cfg_found_str = Color.apply(
"Previously saved firmware configs found!", Color.GREEN
)
menu = textwrap.dedent( menu = textwrap.dedent(
f"""
╟───────────────────────────────────────────────────────╢
{cfg_found_str:^62}
║ ║
║ Select an existing config or create a new one. ║
╟───────────────────────────────────────────────────────╢
║ Available firmware configs: ║
""" """
)[1:]
start_index = 1
for i, s in enumerate(self.configs):
line = f"{start_index + i}) {s.name}"
menu += f"{line:<54}\n"
new_config = Color.apply("N) Create new firmware config", Color.GREEN)
menu += "║ ║\n"
menu += f"{new_config:<62}\n"
menu += "╟───────────────────────────────────────────────────────╢\n"
print(menu, end="")
def select_config(self, **kwargs) -> None:
selection: str | None = kwargs.get("opt_data", None)
if selection is None:
raise Exception("opt_data is None")
if not Path(selection).is_file() and selection != self.kconfig_default:
raise Exception("opt_data does not exists")
self.kconfig = selection
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class KlipperBuildFirmwareMenu(BaseMenu):
def __init__(
self, kconfig: str | None = None, previous_menu: Type[BaseMenu] | None = None
):
super().__init__()
self.title = "Build Firmware Menu"
self.title_color = Color.CYAN
self.previous_menu: Type[BaseMenu] | None = previous_menu
self.deps: Set[str] = {"build-essential", "dpkg-dev", "make"}
self.missing_deps: List[str] = check_package_install(self.deps)
self.flash_options = FlashOptions()
self.kconfigs_dirname = KLIPPER_KCONFIGS_DIR
self.kconfig_default = KLIPPER_DIR.joinpath(".config")
self.kconfig = self.flash_options.selected_kconfig
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.advanced_menu import AdvancedMenu
self.previous_menu = (
previous_menu if previous_menu is not None else AdvancedMenu
)
def set_options(self) -> None:
self.input_label_txt = "Press ENTER to install dependencies"
self.default_option = Option(method=self.install_missing_deps)
def run(self):
# immediately start the build process if all dependencies are met
if len(self.missing_deps) == 0:
self.start_build_process()
else:
super().run()
def print_menu(self) -> None:
txt = Color.apply("Dependencies are missing!", Color.RED)
menu = textwrap.dedent(
f"""
╟───────────────────────────────────────────────────────╢
{txt:^62}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ The following dependencies are required: ║ ║ The following dependencies are required: ║
║ ║ ║ ║
@@ -67,19 +173,11 @@ class KlipperBuildFirmwareMenu(BaseMenu):
status_ok = Color.apply("*INSTALLED*", Color.GREEN) status_ok = Color.apply("*INSTALLED*", Color.GREEN)
status_missing = Color.apply("*MISSING*", Color.RED) status_missing = Color.apply("*MISSING*", Color.RED)
status = status_missing if d in self.missing_deps else status_ok status = status_missing if d in self.missing_deps else status_ok
padding = 39 - len(d) + len(status) + (len(status_ok) - len(status)) padding = 40 - len(d) + len(status) + (len(status_ok) - len(status))
d = Color.apply(f"{d}", Color.CYAN) d = Color.apply(f"{d}", Color.CYAN)
menu += f"{d}{status:>{padding}}\n" menu += f"{d}{status:>{padding}}\n"
menu += "║ ║\n" menu += "║ ║\n"
color = Color.GREEN if len(self.missing_deps) == 0 else Color.RED
txt = (
"All dependencies are met!"
if len(self.missing_deps) == 0
else "Dependencies are missing!"
)
menu += f"{Color.apply(txt, color):<62}\n"
menu += "╟───────────────────────────────────────────────────────╢\n" menu += "╟───────────────────────────────────────────────────────╢\n"
print(menu, end="") print(menu, end="")
@@ -98,13 +196,16 @@ class KlipperBuildFirmwareMenu(BaseMenu):
def start_build_process(self, **kwargs) -> None: def start_build_process(self, **kwargs) -> None:
try: try:
run_make_clean() run_make_clean(self.kconfig)
run_make_menuconfig() run_make_menuconfig(self.kconfig)
run_make() run_make(self.kconfig)
Logger.print_ok("Firmware successfully built!") Logger.print_ok("Firmware successfully built!")
Logger.print_ok(f"Firmware file located in '{KLIPPER_DIR}/out'!") Logger.print_ok(f"Firmware file located in '{KLIPPER_DIR}/out'!")
if self.kconfig == self.kconfig_default:
self.save_firmware_config()
except Exception as e: except Exception as e:
Logger.print_error(e) Logger.print_error(e)
Logger.print_error("Building Klipper Firmware failed!") Logger.print_error("Building Klipper Firmware failed!")
@@ -112,3 +213,62 @@ class KlipperBuildFirmwareMenu(BaseMenu):
finally: finally:
if self.previous_menu is not None: if self.previous_menu is not None:
self.previous_menu().run() self.previous_menu().run()
def save_firmware_config(self) -> None:
Logger.print_dialog(
DialogType.CUSTOM,
[
"You can save the firmware build configs for multiple MCUs,"
" and use them to update the firmware after a Klipper version upgrade"
],
custom_title="Save firmware config",
)
if not get_confirm(
"Do you want to save firmware config?", default_choice=False
):
return
filename = self.kconfig_default
while True:
Logger.print_dialog(
DialogType.CUSTOM,
[
"Allowed characters: a-z, 0-9 and '-'",
"The name must not contain the following:",
"\n\n",
"● Any special characters",
"● No leading or trailing '-'",
],
)
input_name = get_string_input(
"Enter the new firmware config name",
regex=r"^[a-z0-9]+([a-z0-9-]*[a-z0-9])?$",
)
filename = self.kconfigs_dirname.joinpath(f"{input_name}.config")
if Path(filename).is_file():
if get_confirm(
f"Firmware config {input_name} already exists, overwrite?",
default_choice=False,
):
break
if Path(filename).is_dir():
Logger.print_error(f"Path {filename} exists and it's a directory")
if not Path(filename).exists():
break
if not get_confirm(
f"Save firmware config to '{filename}'?", default_choice=True
):
Logger.print_info("Aborted saving firmware config ...")
return
if not Path(self.kconfigs_dirname).exists():
Path(self.kconfigs_dirname).mkdir()
copyfile(self.kconfig_default, filename)
Logger.print_ok()
Logger.print_ok(f"Firmware config successfully saved to {filename}")

View File

@@ -10,6 +10,7 @@ from __future__ import annotations
import textwrap import textwrap
import time import time
from pathlib import Path
from typing import Type from typing import Type
from components.klipper_firmware.firmware_utils import ( from components.klipper_firmware.firmware_utils import (
@@ -420,6 +421,7 @@ class KlipperFlashOverviewMenu(BaseMenu):
mcu = self.flash_options.selected_mcu.split("/")[-1] mcu = self.flash_options.selected_mcu.split("/")[-1]
board = self.flash_options.selected_board board = self.flash_options.selected_board
baudrate = self.flash_options.selected_baudrate baudrate = self.flash_options.selected_baudrate
kconfig = Path(self.flash_options.selected_kconfig).name
color = Color.CYAN color = Color.CYAN
subheader = f"[{Color.apply('Overview', color)}]" subheader = f"[{Color.apply('Overview', color)}]"
menu = textwrap.dedent( menu = textwrap.dedent(
@@ -452,6 +454,13 @@ class KlipperFlashOverviewMenu(BaseMenu):
""" """
)[1:] )[1:]
if self.flash_options.flash_method is FlashMethod.REGULAR:
menu += textwrap.dedent(
f"""
║ Firmware config: {Color.apply(f"{kconfig:<36}", color)}
"""
)[1:]
menu += textwrap.dedent( menu += textwrap.dedent(
""" """
║ ║ ║ ║

View File

@@ -8,7 +8,6 @@
# ======================================================================= # # ======================================================================= #
from __future__ import annotations from __future__ import annotations
import json
import subprocess import subprocess
from typing import List from typing import List
@@ -31,6 +30,7 @@ from components.moonraker.moonraker_dialogs import print_moonraker_overview
from components.moonraker.moonraker_utils import ( from components.moonraker.moonraker_utils import (
backup_moonraker_dir, backup_moonraker_dir,
create_example_moonraker_conf, create_example_moonraker_conf,
parse_sysdeps_file,
) )
from components.webui_client.client_utils import ( from components.webui_client.client_utils import (
enable_mainsail_remotemode, enable_mainsail_remotemode,
@@ -53,6 +53,8 @@ from utils.sys_utils import (
cmd_sysctl_manage, cmd_sysctl_manage,
cmd_sysctl_service, cmd_sysctl_service,
create_python_venv, create_python_venv,
get_distro_name,
get_distro_version,
install_python_requirements, install_python_requirements,
parse_packages_from_file, parse_packages_from_file,
) )
@@ -157,9 +159,35 @@ def install_moonraker_packages() -> None:
moonraker_deps = [] moonraker_deps = []
if MOONRAKER_DEPS_JSON_FILE.exists(): if MOONRAKER_DEPS_JSON_FILE.exists():
with open(MOONRAKER_DEPS_JSON_FILE, "r") as deps: Logger.print_status(
moonraker_deps = json.load(deps).get("debian", []) f"Parsing system dependencies from {MOONRAKER_DEPS_JSON_FILE.name} ..."
)
parsed_sysdeps = parse_sysdeps_file(MOONRAKER_DEPS_JSON_FILE)
distro_name = get_distro_name().lower()
distro_version = get_distro_version()
for dep in parsed_sysdeps.get(distro_name, []):
pkg = dep[0].strip()
comparator = dep[1].strip()
req_version = dep[2].strip()
comparisons = {
"": lambda x, y: True,
"<": lambda x, y: x < y,
">": lambda x, y: x > y,
"<=": lambda x, y: x <= y,
">=": lambda x, y: x >= y,
"==": lambda x, y: x == y,
"!=": lambda x, y: x != y,
}
if comparisons[comparator](float(distro_version), float(req_version or 0)):
moonraker_deps.append(pkg)
elif MOONRAKER_INSTALL_SCRIPT.exists(): elif MOONRAKER_INSTALL_SCRIPT.exists():
Logger.print_status(
f"Parsing system dependencies from {MOONRAKER_INSTALL_SCRIPT.name} ..."
)
moonraker_deps = parse_packages_from_file(MOONRAKER_INSTALL_SCRIPT) moonraker_deps = parse_packages_from_file(MOONRAKER_INSTALL_SCRIPT)
if not moonraker_deps: if not moonraker_deps:

View File

@@ -6,9 +6,11 @@
# # # #
# 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 re
import shutil import shutil
from typing import Dict, List, Optional from pathlib import Path
from typing import Dict, List, Optional, Tuple
from components.moonraker import ( from components.moonraker import (
MODULE_PATH, MODULE_PATH,
@@ -138,3 +140,34 @@ def backup_moonraker_db_dir() -> None:
bm.backup_directory( bm.backup_directory(
name, source=instance.db_dir, target=MOONRAKER_DB_BACKUP_DIR name, source=instance.db_dir, target=MOONRAKER_DB_BACKUP_DIR
) )
# This function is from sync_dependencies.py script from the Moonraker project on GitHub:
# https://github.com/Arksine/moonraker/blob/master/scripts/sync_dependencies.py
# Thanks to Arksine for his work on this project!
def parse_sysdeps_file(sysdeps_file: Path) -> Dict[str, List[Tuple[str, str, str]]]:
"""
Parses the system dependencies file and returns a dictionary with the parsed dependencies.
:param sysdeps_file: The path to the system dependencies file.
:return: A dictionary with the parsed dependencies in the format {distro: [(package, comparator, version)]}.
"""
base_deps: Dict[str, List[str]] = json.loads(sysdeps_file.read_bytes())
parsed_deps: Dict[str, List[Tuple[str, str, str]]] = {}
for distro, pkgs in base_deps.items():
parsed_deps[distro] = []
for dep in pkgs:
parts = dep.split(";", maxsplit=1)
if len(parts) == 1:
parsed_deps[distro].append((dep.strip(), "", ""))
else:
pkg_name = parts[0].strip()
dep_parts = re.split(r"(==|!=|<=|>=|<|>)", parts[1].strip())
comp_var = dep_parts[0].strip().lower()
if len(dep_parts) != 3 or comp_var != "distro_version":
continue
operator = dep_parts[1].strip()
req_version = dep_parts[2].strip()
parsed_deps[distro].append((pkg_name, operator, req_version))
return parsed_deps

View File

@@ -144,7 +144,7 @@ def install_client(
custom_color=Color.GREEN, custom_color=Color.GREEN,
center_content=True, center_content=True,
content=[ content=[
f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{port}", f"Open {client.display_name} now on: http://{get_ipv4_addr()}{'' if port == 80 else f':{port}'}",
], ],
) )

View File

@@ -15,6 +15,7 @@ from components.klipper import KLIPPER_DIR
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from components.klipper_firmware.menus.klipper_build_menu import ( from components.klipper_firmware.menus.klipper_build_menu import (
KlipperBuildFirmwareMenu, KlipperBuildFirmwareMenu,
KlipperKConfigMenu,
) )
from components.klipper_firmware.menus.klipper_flash_menu import ( from components.klipper_firmware.menus.klipper_flash_menu import (
KlipperFlashMethodMenu, KlipperFlashMethodMenu,
@@ -76,12 +77,15 @@ class AdvancedMenu(BaseMenu):
rollback_repository(MOONRAKER_DIR, Moonraker) rollback_repository(MOONRAKER_DIR, Moonraker)
def build(self, **kwargs) -> None: def build(self, **kwargs) -> None:
KlipperKConfigMenu().run()
KlipperBuildFirmwareMenu(previous_menu=self.__class__).run() KlipperBuildFirmwareMenu(previous_menu=self.__class__).run()
def flash(self, **kwargs) -> None: def flash(self, **kwargs) -> None:
KlipperKConfigMenu().run()
KlipperFlashMethodMenu(previous_menu=self.__class__).run() KlipperFlashMethodMenu(previous_menu=self.__class__).run()
def build_flash(self, **kwargs) -> None: def build_flash(self, **kwargs) -> None:
KlipperKConfigMenu().run()
KlipperBuildFirmwareMenu(previous_menu=KlipperFlashMethodMenu).run() KlipperBuildFirmwareMenu(previous_menu=KlipperFlashMethodMenu).run()
KlipperFlashMethodMenu(previous_menu=self.__class__).run() KlipperFlashMethodMenu(previous_menu=self.__class__).run()

View File

@@ -31,7 +31,7 @@ def change_system_hostname() -> None:
"http://<hostname>.local", "http://<hostname>.local",
"\n\n", "\n\n",
"Example: If you set your hostname to 'my-printer', you can access an " "Example: If you set your hostname to 'my-printer', you can access an "
"installed webinterface by tyoing 'http://my-printer.local' in the " "installed webinterface by typing 'http://my-printer.local' in the "
"browser.", "browser.",
], ],
custom_title="CHANGE SYSTEM HOSTNAME", custom_title="CHANGE SYSTEM HOSTNAME",

View File

@@ -539,3 +539,10 @@ def get_service_file_path(instance_type: type, suffix: str) -> Path:
file_path: Path = SYSTEMD.joinpath(f"{name}.service") file_path: Path = SYSTEMD.joinpath(f"{name}.service")
return file_path return file_path
def get_distro_name() -> str:
return check_output(["lsb_release", "-is"]).decode().strip()
def get_distro_version() -> str:
return check_output(["lsb_release", "-rs"]).decode().strip()

View File

@@ -105,7 +105,7 @@ function install_crowsnest(){
pushd "${HOME}/crowsnest" &> /dev/null || exit 1 pushd "${HOME}/crowsnest" &> /dev/null || exit 1
title_msg "Installer will prompt you for sudo password!" title_msg "Installer will prompt you for sudo password!"
status_msg "Launching crowsnest installer ..." status_msg "Launching crowsnest installer ..."
if ! sudo make install BASE_USER=$USER; then if ! sudo make install; then
error_msg "Something went wrong! Please try again..." error_msg "Something went wrong! Please try again..."
exit 1 exit 1
fi fi