Compare commits

..

9 Commits

Author SHA1 Message Date
dw-0
0ee0fa3325 feat: KIAUH v6 - full rewrite of KIAUH in Python (#428) 2024-08-31 19:16:52 +02:00
Henrik Fransson
8547942986 readme: fix broken OctoApp plugin link (#494) 2024-08-06 16:41:18 +02:00
dw-0
d33ac6b15a fix: parse moonraker dependencies from system-dependencies.json (#492)
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-08-03 13:30:34 +02:00
dw-0
6cd9133a15 fix: detect RatOS 2.1+ as operating system and exit (#490)
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-07-31 20:30:33 +02:00
Beans Baxter
a929c6983d refactor: don't check spoolman version if not installed (#487) 2024-06-28 23:07:20 +02:00
breakheart
bce92001a6 fix: use correct Spoolman directory name (#486) 2024-06-28 17:46:19 +02:00
Alessandro Maggi
7993b98ee1 fix: replace jq by grep to check Spoolman update (#482)
* fix: remove extra space in remove menu

* fix(spoolman): replace jq with grep
jq isn't included in some minimal installations
2024-06-26 07:04:09 +02:00
Alessandro Maggi
62296e112e feat: add Spoolman support (#477) 2024-06-25 20:10:14 +02:00
Justin Otherguy
a374ac8fac fix: add unzip to dependencies for Mainsail and Fluidd 2024-05-09 20:56:16 +02:00
22 changed files with 147 additions and 61 deletions

View File

@@ -2,13 +2,54 @@
This document covers possible important changes to KIAUH.
### 2024-08-31 (v6.0.0-alpha.1)
Long time no see, but here we are again!
A lot has happened in the background, but now it is time to take it out into the wild.
#### KIAUH has now reached version 6! Well, at least in an alpha state...
The project has seen a complete rewrite of the script from scratch in Python.
It requires Python 3.8 or newer to run. Because this update is still in an alpha state, bugs may or will occur.
During startup, you will be asked if you want to start the new version 6 or the old version 5.
As long as version 6 is in a pre-release state, version 5 will still be available. If there are any critical issues
with the new version that were overlooked, you can always switch back to the old version.
In case you selected not to get asked about which version to start (option 3 or 4 in the startup dialog) and you want to
revert that decision, you will find a line called `version_to_launch=` within the `.kiauh.ini` file in your home directory.
Just delete that line, save the file and restart KIAUH. KIAUH will then ask you again which version you want to start.
Here is a list of the most important changes to KIAUH in regard to version 6:
- The majority of features available in KIAUH v5 are still available; they just got migrated from Bash to Python.
- It is now possible to add new/remove instances to/from existing multi-instance installations of Klipper and Moonraker
- KIAUH now has an Extension-System. This allows contributors to add new installers to KIAUH without having to modify the main script.
- You will now find some of the features that were previously available in the Installer-Menu in the Extensions-Menu.
- The current extensions are:
- G-Code Shell Command (previously found in the Advanced-Menu)
- Mainsail Theme Installer (previously found in the Advanced-Menu)
- Klipper-Backup (new in v6!)
- Moonraker Telegram Bot (previously found in the Installer-Menu)
- PrettyGCode for Klipper (previously found in the Installer-Menu)
- Obico for Klipper (previously found in the Installer-Menu)
- The following additional extensions are planned, but not yet available:
- Spoolman (available in v5 in the Installer-Menu)
- OctoApp (available in v5 in the Installer-Menu)
- KIAUH has its own config file now
- The file has some default values for the currently supported options
- There might be more options in the future
- It is located in KIAUH's root directory and is called `default.kiauh.cfg`
- DO NOT EDIT the default file directly, instead make a copy of it and call it `kiauh.cfg`
- Settings changed via the Advanced-Menu will be written to the `kiauh.cfg`
- Support for OctoPrint was removed
Feel free to give version 6 a try and report any bugs or issues you encounter! Every feedback is appreciated.
### 2023-06-17
KIAUH has now added support for installing Mobileraker's companion!
KIAUH has now added support for installing Mobileraker's companion!
Mobileraker is a free and Open Source Android and iOS App for Klipper, utilizing the Moonraker API, allowing you
to control your printer. Thank you to [Clon1998](https://github.com/Clon1998) for adding this feature!
### 2023-02-03
The installer for MJPG-Streamer got replaced by crowsnest. It is an improved webcam service, utilizing ustreamer.
The installer for MJPG-Streamer got replaced by crowsnest. It is an improved webcam service, utilizing ustreamer.
Please have a look here for additional info about crowsnest and how to configure it: https://github.com/mainsail-crew/crowsnest \
It's unsure if the previous MJPG-Streamer installer will be updated and make its way back into KIAUH.
A big thanks to [KwadFan](https://github.com/KwadFan) for writing the crowsnest implementation.
@@ -115,7 +156,7 @@ membership for example caused issues when installing mjpg-streamer while not usi
Other issues could occur when trying to flash an MCU on Debian or Ubuntu distributions where a user might not be part
of the dialout group by default. A check for the tty group is also done. The tty group is needed for setting
up a linux MCU (currently not yet supported by KIAUH).
* There is an issue when trying to install Mainsail or Fluidd on Ubuntu 21.10. Permissions on that distro seem to have seen a rework
* There is an issue when trying to install Mainsail or Fluidd on Ubuntu 21.10. Permissions on that distro seem to have seen a rework
in comparison to 20.04 and users will be greeted with an "Error 403 - Permission denied" message after installing one of Klippers webinterfaces.
I still have to figure out a viable solution for that.

View File

@@ -164,7 +164,6 @@ function main() {
fi
}
check_if_ratos
check_euid
init_logfile
@@ -173,4 +172,3 @@ kiauh_update_dialog
read_kiauh_ini
init_ini
main

View File

@@ -234,8 +234,6 @@ def display_moonraker_info(moonraker_list: List[Moonraker]) -> bool:
"The following Klipper instances will be installed:",
*[f"● klipper-{m.suffix}" for m in moonraker_list],
],
padding_top=0,
padding_bottom=0,
)
_input: bool = get_confirm("Proceed with installation?")
return _input

View File

@@ -103,7 +103,6 @@ def check_user_groups() -> None:
"INFO:",
"Relog required for group assignments to take effect!",
],
padding_bottom=0,
)
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):

View File

@@ -32,7 +32,7 @@ class KlipperNoFirmwareErrorMenu(BaseMenu):
self.previous_menu = previous_menu
def set_options(self) -> None:
self.default_option = Option(self.go_back, False)
self.default_option = Option(method=self.go_back)
def print_menu(self) -> None:
header = "!!! NO FIRMWARE FILE FOUND !!!"
@@ -79,7 +79,7 @@ class KlipperNoBoardTypesErrorMenu(BaseMenu):
self.previous_menu = previous_menu
def set_options(self) -> None:
self.default_option = Option(self.go_back, False)
self.default_option = Option(method=self.go_back)
def print_menu(self) -> None:
header = "!!! ERROR GETTING BOARD LIST !!!"

View File

@@ -260,8 +260,7 @@ class KlipperSelectMcuIdMenu(BaseMenu):
def set_options(self) -> None:
self.options = {
f"{i}": Option(self.flash_mcu, False, f"{i}")
for i in range(len(self.mcu_list))
f"{i}": Option(self.flash_mcu, f"{i}") for i in range(len(self.mcu_list))
}
def print_menu(self) -> None:
@@ -323,7 +322,7 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
def set_options(self) -> None:
self.options = {
f"{i}": Option(self.board_select, False, f"{i}")
f"{i}": Option(self.board_select, f"{i}")
for i in range(len(self.available_boards))
}

View File

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

View File

@@ -32,7 +32,7 @@ class LogUploadMenu(BaseMenu):
def set_options(self) -> None:
self.options = {
f"{index}": Option(self.upload, False, opt_index=f"{index}")
f"{index}": Option(self.upload, opt_index=f"{index}")
for index in range(len(self.logfile_list))
}

View File

@@ -63,8 +63,6 @@ def install_octoeverywhere() -> None:
"It is safe to run the installer again to link your "
"printer or repair any issues.",
],
padding_top=0,
padding_bottom=0,
)
if not get_confirm("Re-run OctoEverywhere installation?"):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
@@ -85,8 +83,6 @@ def install_octoeverywhere() -> None:
"\n\n",
"The setup will apply the same names to OctoEverywhere!",
],
padding_top=0,
padding_bottom=0,
)
if not get_confirm(

View File

@@ -24,8 +24,6 @@ def print_moonraker_not_found_dialog() -> None:
"another machine in your network. Otherwise Mainsail will NOT work "
"correctly.",
],
padding_top=0,
padding_bottom=0,
)
@@ -36,8 +34,6 @@ def print_client_already_installed_dialog(name: str) -> None:
f"{name} seems to be already installed!",
f"If you continue, your current {name} installation will be overwritten.",
],
padding_top=0,
padding_bottom=0,
)
@@ -57,8 +53,6 @@ def print_client_port_select_dialog(
"The following ports were found to be in use already:",
*[f"{port}" for port in ports_in_use],
],
padding_top=0,
padding_bottom=0,
)
@@ -77,8 +71,6 @@ def print_install_client_config_dialog(client: BaseWebClient) -> None:
"If you already use these macros skip this step. Otherwise you should "
"consider to answer with 'Y' to download the recommended macros.",
],
padding_top=0,
padding_bottom=0,
)

View File

@@ -38,7 +38,7 @@ from core.types import ComponentStatus
from utils.common import get_install_status
from utils.fs_utils import create_symlink, remove_file
from utils.git_utils import (
get_latest_tag,
get_latest_remote_tag,
get_latest_unstable_tag,
)
@@ -137,7 +137,7 @@ def get_local_client_version(client: BaseWebClient) -> str | None:
def get_remote_client_version(client: BaseWebClient) -> str | None:
try:
if (tag := get_latest_tag(client.repo_path)) != "":
if (tag := get_latest_remote_tag(client.repo_path)) != "":
return str(tag)
return None
except Exception:

View File

@@ -92,8 +92,8 @@ class Logger:
center_content: bool = False,
custom_title: str | None = None,
custom_color: DialogCustomColor | None = None,
padding_top: int = 1,
padding_bottom: int = 1,
margin_top: int = 0,
margin_bottom: int = 0,
) -> None:
"""
Prints a dialog with the given title and content.
@@ -106,8 +106,8 @@ class Logger:
:param center_content: Whether to center the content or not.
:param custom_title: A custom title for the dialog.
:param custom_color: A custom color for the dialog.
:param padding_top: The number of empty lines to print before the dialog.
:param padding_bottom: The number of empty lines to print after the dialog.
:param margin_top: The number of empty lines to print before the dialog.
:param margin_bottom: The number of empty lines to print after the dialog.
"""
dialog_color = Logger._get_dialog_color(title, custom_color)
dialog_title = Logger._get_dialog_title(title, custom_title)
@@ -116,12 +116,12 @@ class Logger:
top = Logger._format_top_border(dialog_color)
bottom = Logger._format_bottom_border()
print("\n" * padding_top)
print("\n" * margin_top)
print(
f"{top}{dialog_title_formatted}{dialog_content}{bottom}",
end="",
)
print("\n" * padding_bottom)
print("\n" * margin_bottom)
@staticmethod
def _get_dialog_title(

View File

@@ -10,7 +10,7 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable
from typing import Any, Callable, Type
@dataclass
@@ -22,7 +22,7 @@ class Option:
:param opt_data: Can be used to pass any additional data to the menu option
"""
method: Callable | None = None
method: Type[Callable] | None = None
opt_index: str = ""
opt_data: Any = None

View File

@@ -44,6 +44,7 @@ from core.menus.settings_menu import SettingsMenu
from core.menus.update_menu import UpdateMenu
from core.types import ComponentStatus, StatusMap, StatusText
from extensions.extensions_menu import ExtensionsMenu
from utils.common import get_kiauh_version
# noinspection PyUnusedLocal
@@ -55,6 +56,7 @@ class MainMenu(BaseMenu):
self.header: bool = True
self.footer_type: FooterType = FooterType.QUIT
self.version = ""
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.cn_status = self.cc_status = self.oe_status = ""
@@ -86,6 +88,7 @@ class MainMenu(BaseMenu):
)
def _fetch_status(self) -> None:
self.version = get_kiauh_version()
self._get_component_status("kl", get_klipper_status)
self._get_component_status("mr", get_moonraker_status)
self._get_component_status("ms", get_client_status, MainsailData())
@@ -125,7 +128,7 @@ class MainMenu(BaseMenu):
self._fetch_status()
header = " [ Main Menu ] "
footer1 = f"{COLOR_CYAN}KIAUH v6.0.0{RESET_FORMAT}"
footer1 = f"{COLOR_CYAN}{self.version}{RESET_FORMAT}"
footer2 = f"Changelog: {COLOR_MAGENTA}https://git.io/JnmlX{RESET_FORMAT}"
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)

View File

@@ -281,8 +281,6 @@ class UpdateMenu(BaseMenu):
DialogType.CUSTOM,
["The following packages will be upgraded:", "\n\n", pkgs],
custom_title="UPGRADABLE SYSTEM UPDATES",
padding_top=0,
padding_bottom=0,
)
if not get_confirm("Continue?"):
return

View File

@@ -38,9 +38,7 @@ class ExtensionsMenu(BaseMenu):
def set_options(self) -> None:
self.options = {
i: Option(
self.extension_submenu, menu=True, opt_data=self.extensions.get(i)
)
i: Option(self.extension_submenu, opt_data=self.extensions.get(i))
for i in self.extensions
}

View File

@@ -93,7 +93,7 @@ class MainsailThemeInstallMenu(BaseMenu):
def set_options(self) -> None:
self.options = {
f"{index}": Option(self.install_theme, False, opt_index=f"{index}")
f"{index}": Option(self.install_theme, opt_index=f"{index}")
for index in range(len(self.themes))
}

View File

@@ -49,8 +49,6 @@ class TelegramBotExtension(BaseExtension):
"Moonraker Telegram Bot requires Moonraker to be installed. "
"Please install Moonraker first!",
],
padding_top=0,
padding_bottom=0,
)
return
@@ -65,8 +63,6 @@ class TelegramBotExtension(BaseExtension):
"\n\n",
"The setup will apply the same names to Telegram Bot!",
],
padding_top=0,
padding_bottom=0,
)
if not get_confirm(
"Continue Moonraker Telegram Bot installation?",
@@ -88,8 +84,6 @@ class TelegramBotExtension(BaseExtension):
instance = MoonrakerTelegramBot(suffix=name)
instance.create()
print(instance)
cmd_sysctl_service(instance.service_file_path.name, "enable")
if create_example_cfg:
@@ -126,6 +120,7 @@ class TelegramBotExtension(BaseExtension):
"following wiki page for further information:",
"https://github.com/nlef/moonraker-telegram-bot/wiki",
],
margin_bottom=1,
)
Logger.print_ok("Telegram Bot installation complete!")

View File

@@ -35,8 +35,6 @@ def change_system_hostname() -> None:
"browser.",
],
custom_title="CHANGE SYSTEM HOSTNAME",
padding_top=0,
padding_bottom=0,
)
if not get_confirm("Do you want to change the hostname?", default_choice=False):
return
@@ -50,8 +48,6 @@ def change_system_hostname() -> None:
"● Any special characters",
"● No leading or trailing '-'",
],
padding_top=0,
padding_bottom=0,
)
hostname = get_string_input(
"Enter the new hostname",

View File

@@ -22,7 +22,12 @@ from core.constants import (
)
from core.logger import DialogType, Logger
from core.types import ComponentStatus, StatusCode
from utils.git_utils import get_local_commit, get_remote_commit, get_repo_name
from utils.git_utils import (
get_local_commit,
get_local_tags,
get_remote_commit,
get_repo_name,
)
from utils.instance_utils import get_instances
from utils.sys_utils import (
check_package_install,
@@ -31,6 +36,14 @@ from utils.sys_utils import (
)
def get_kiauh_version() -> str:
"""
Helper method to get the current KIAUH version by reading the latest tag
:return: string of the latest tag
"""
return get_local_tags(Path(__file__).parent.parent)[-1]
def convert_camelcase_to_kebabcase(name: str) -> str:
return re.sub(r"(?<!^)(?=[A-Z])", "-", name).lower()

View File

@@ -84,7 +84,38 @@ def get_repo_name(repo: Path) -> str | None:
return None
def get_tags(repo_path: str) -> List[str]:
def get_local_tags(repo_path: Path, _filter: str | None = None) -> List[str]:
"""
Get all tags of a local Git repository
:param repo_path: Path to the local Git repository
:param _filter: Optional filter to filter the tags by
:return: List of tags
"""
try:
cmd = ["git", "tag", "-l"]
if _filter is not None:
cmd.append(f"'${_filter}'")
result: str = check_output(
cmd,
stderr=DEVNULL,
cwd=repo_path.as_posix(),
).decode(encoding="utf-8")
tags = result.split("\n")
return tags[:-1]
except CalledProcessError:
return []
def get_remote_tags(repo_path: str) -> List[str]:
"""
Gets the tags of a GitHub repostiory
:param repo_path: path of the GitHub repository - e.g. `<owner>/<name>`
:return: List of tags
"""
try:
url = f"https://api.github.com/repos/{repo_path}/tags"
with urllib.request.urlopen(url) as r:
@@ -102,14 +133,14 @@ def get_tags(repo_path: str) -> List[str]:
raise
def get_latest_tag(repo_path: str) -> str:
def get_latest_remote_tag(repo_path: str) -> str:
"""
Gets the latest stable tag of a GitHub repostiory
:param repo_path: path of the GitHub repository - e.g. `<owner>/<name>`
:return: tag or empty string
"""
try:
if len(latest_tag := get_tags(repo_path)) > 0:
if len(latest_tag := get_remote_tags(repo_path)) > 0:
return latest_tag[0]
else:
return ""
@@ -124,7 +155,10 @@ def get_latest_unstable_tag(repo_path: str) -> str:
:return: tag or empty string
"""
try:
if len(unstable_tags := [t for t in get_tags(repo_path) if "-" in t]) > 0:
if (
len(unstable_tags := [t for t in get_remote_tags(repo_path) if "-" in t])
> 0
):
return unstable_tags[0]
else:
return ""
@@ -133,6 +167,34 @@ def get_latest_unstable_tag(repo_path: str) -> str:
raise
def compare_semver_tags(tag1: str, tag2: str) -> bool:
"""
Compare two semver version strings.
Does not support comparing pre-release versions (e.g. 1.0.0-rc.1, 1.0.0-beta.1)
:param tag1: First version string
:param tag2: Second version string
:return: True if tag1 is greater than tag2, False otherwise
"""
if tag1 == tag2:
return False
def parse_version(v):
return list(map(int, v[1:].split(".")))
tag1_parts = parse_version(tag1)
tag2_parts = parse_version(tag2)
max_len = max(len(tag1_parts), len(tag2_parts))
tag1_parts += [0] * (max_len - len(tag1_parts))
tag2_parts += [0] * (max_len - len(tag2_parts))
for part1, part2 in zip(tag1_parts, tag2_parts):
if part1 != part2:
return part1 > part2
return False
def get_local_commit(repo: Path) -> str | None:
if not repo.exists() or not repo.joinpath(".git").exists():
return None

View File

@@ -40,7 +40,7 @@ function main_ui() {
function get_kiauh_version() {
local version
cd "${KIAUH_SRCDIR}"
version="$(git describe HEAD --always --tags | cut -d "-" -f 1,2)"
version="$(git tag -l 'v5*' | tail -1)"
echo "${version}"
}