Compare commits

..
Author SHA1 Message Date
dw-0 e20b85e00c feat(tests): load live VM host/key from .env, keep inventory non-sensitive 2026-07-04 14:11:01 +02:00
dw-0 1d1bac1524 feat(tests): implement live VM acceptance test strategy (TODO-006 to TODO-010) 2026-07-04 13:38:52 +02:00
dw-0 7b5522ac94 feat(tests): add unit tests for shared utilities 2026-07-04 12:58:20 +02:00
Cody D DixonandGitHub a42b730688 fix: catch FileNotFoundError in get_current_branch when component dir is missing (#813)
Error when opening KIUAH fix

get_current_branch only catches CalledProcessError, not FileNotFoundError. When crowsnest isn't installed, the missing dir propagates the exception. Fix
2026-06-29 22:43:10 +02:00
dw-0 77d6c87aa0 refactor(readme): update section titles and improve table formatting 2026-06-29 22:20:55 +02:00
dw-0 b893ff14f7 chore(readme): remove jetbrains as sponsor 2026-06-29 21:36:58 +02:00
dw-0 43b0994ac5 fix(README_zh): correct Fluidd logo image path in documentation 2026-06-29 21:31:55 +02:00
dw-0 040edc1d4f chore: remove AGENTS.md 2026-06-28 12:50:59 +02:00
750dba1dbe feat(extensions): add Moongate for Klipper (#809)
* feat(extensions): add Moongate for Klipper

Adds Moongate for Klipper as extension #15 (next free index after KAMP). Moongate pairs a printer with the Moongate Android app for secure remote access and print monitoring over a Cloudflare quick-tunnel fronted by an EdDSA auth proxy.

The extension is KIAUH-native for the parts KIAUH owns (Moonraker instance discovery, the install/remove confirmation dialogs, moonraker.conf backup, and the repo clone wired into the update manager) and delegates the heavy, security-sensitive install steps (cloudflared, the moongate-authproxy and moongate-tunnel systemd services, the Moonraker host rebind) to Moongate's own idempotent, non-interactive install/update/uninstall scripts so that logic stays maintained upstream in one place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(metadata): update index for Moongate extension

* refactor(moongate_extension): enhance error handling and update docstring for clarity

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: dw-0 <th33xitus@gmail.com>
2026-06-28 12:49:30 +02:00
6f4b471008 feat(extension): Add DroidKlipp extension (#804)
* Add DroidKlipp extension

* fix(extensions): restart adb_monitor and redeploy monitor on DroidKlipp update

The update flow only ran git_pull_wrapper, which left the running
adb_monitor service executing stale in-memory code. It also never
rebuilt the deployed ~/droidklipp_monitor.py, since that file is
generated by the installer rather than tracked in the repo.

- stop adb_monitor before the pull, start it after (mirrors mobileraker)
- after pulling, re-deploy droidklipp_monitor.py into $HOME so the
  service picks up the updated code
- best-effort restart the service if the update fails

* fix(droidklipp): clean up comments and update website link in metadata

---------

Co-authored-by: Cody Dixon <codydixon71@gmail.com>
Co-authored-by: dw-0 <th33xitus@gmail.com>
2026-06-28 12:07:19 +02:00
5077765fd6 fix: fluidd logo (#811)
* fix: fluid logo

* fix: use correct logo.svg instead of avatar

---------

Co-authored-by: dw-0 <th33xitus@gmail.com>
2026-06-27 12:53:20 +02:00
Patrick GehrsitzandGitHub 9f97ae6c2a refactor(crowsnest): fix crowsnest install status and update for v5 (#799)
fix: fix crowsnest install status and update for v5

Crowsnest v5 introduced multiple breaking changes resulting in a
'Incomplete' install status even after successful installation.
This adds support for the new v5 setup with backwards compatibility for
old versions.

Signed-off-by: Patrick Gehrsitz <github@mryel.de>
2026-05-30 15:36:32 +02:00
_Redstone_c_andGitHub b90a8f13b1 fix(switch_repo): honor use_python_binary when recreating venv (#793)
* fix(switch_repo): honor use_python_binary when recreating venv

* fix(switch_repo): validate name at function entry
2026-04-24 13:34:45 +02:00
ea1b794afd feat(extensions): Klipper-Adaptive-Meshing-Purging (#785)
* feat(instance_manager): add interactive stopping of Klipper instances with user confirmation

* fix(instance_utils): remove unnecessary 'self' parameter from stop_klipper_instances_interactively function

* refactor(tmc_autotune): replace internal stop function with direct call to stop_klipper_instances_interactively

* feat(klipper_adaptive_meshing_purging): add initial implementation of Klipper Adaptive Meshing and Purging extension

* docs(klipper_adaptive_meshing_purging): update warning message with additional documentation note regarding max_extrude_cross_section

* fix(klipper_adaptive_meshing_purging): update warning message formatting for clarity

* fix(klipper_adaptive_meshing_purging): update warning message formatting for clarity

* fix(_install_cfg): update symlink creation to point to Configuration directory

* fix(license): update copyright information to include myself & upstream

* fix(klipper_adaptive_meshing_purging): update moonraker updater name for consistency with upstream

* style: add type annotations

- Correct minor formatting issues in copyright headers.

* fix: update import path for `simple_config_parser`

* refactor: improve logging and configuration handling for KAMP installation and removal

* style: clarify warning message for purge settings in KAMP extension

---------

Co-authored-by: dw-0 <th33xitus@gmail.com>
2026-04-21 18:00:40 +02:00
dw-0 647a7d9400 feat: add initial AGENTS.md 2026-04-20 21:49:46 +02:00
aa0609d143 fix: auto restart of KIAUH after update (#781)
* fixed auto update

* fixed the restarting logic

* testing

* removed test

* fix: colored output and remove subshell usage in kiauh_update_dialog

---------

Co-authored-by: dw-0 <th33xitus@gmail.com>
2026-04-19 20:10:36 +02:00
dw-0 2175bd55f9 refactor: integrate simple_config_parser as internal module 2026-04-19 15:48:06 +02:00
Patrick GehrsitzandGitHub a935e67431 refactor: use correct crowsnest branch for v5 and future releases (#792)
fix: fix crowsnest for v5 and future releases

Signed-off-by: Patrick Gehrsitz <github@mryel.de>
2026-04-19 14:52:16 +02:00
dw-0andGitHub 2f41e52189 fix(core): refresh system package list in update menu after update (#791) 2026-04-19 12:21:17 +02:00
Théo GaillardandGitHub 757344128a fix(extensions_menu): prevent extension index collisions during loading (#783)
* fix(extensions_menu): prevent extension index collisions during loading

* feat(extensions_menu): add GITHUB_ISSUES_URL for reporting extension loading issues
2026-03-22 13:43:20 +01:00
Théo GaillardandGitHub 7ca08f9b30 feat(instance_manager): add interactive stopping of Klipper instances (#784)
* feat(instance_manager): add interactive stopping of Klipper instances with user confirmation

* fix(instance_utils): remove unnecessary 'self' parameter from stop_klipper_instances_interactively function

* refactor(tmc_autotune): replace internal stop function with direct call to stop_klipper_instances_interactively
2026-03-22 12:01:15 +01:00
David RiosandGitHub 308079a821 fix: incorrect Logger.warn (#778)
Fix method name
2026-02-27 22:45:58 +01:00
Théo GaillardandGitHub 09a5d96b63 feat(tmc_autotune): add initial implementation of TMC Autotune extens… (#771)
* feat(tmc_autotune): add initial implementation of TMC Autotune extension with installation and update functionalities

* fix: remove useless comments

* fix: added support for Kalico style plugins directory

* refactor: extract method for moonraker update manager section removal, automatically reloading moonraker upon call
2026-02-03 20:46:24 +01:00
dw-0andGitHub 1f9d4c823a fix(settings_menu): fix regression by checking for the correct condition (#773) 2026-01-31 11:13:37 +01:00
dw-0 c8df9427b3 fix(backup): improve reusability of backup service and enhance file handling
- Refactor `BackupService` instance management for better reuse across methods.
- Avoid redundant file backups by checking for existing files.
- Enhance directory backup logic to handle nested files and directories more efficiently.
- Standardize timestamp initialization for consistent time-based backup operations.
2026-01-31 10:59:53 +01:00
Théo GaillardandGitHub 5414aba299 fix(backup_service): backup methods use proper paths (#769) (#770)
* fix(backup_service): streamline backup methods for proper paths and add docstring

* fix(backup_service): add proper destination_path in verbose output

* fix(backup_service): replaces html headers with { } to avoid rendering

* nitpick(backup_service): correct variable name for backup destination path in logging
2026-01-29 18:52:48 +01:00
Théo GaillardandGitHub 80948edbb4 fix(gcode_shell_cmd): update comment to clarify config directory usage (#767) 2026-01-19 17:04:28 +01:00
Théo GaillardandGitHub a455edba93 docs(fs_utils): add the documentation for create_symlink (#768) 2026-01-19 17:03:04 +01:00
Théo GaillardandGitHub 810ab3a2fa fix(fs-utils): enhance check_file_exist to support symlink resolution (#766) 2026-01-19 17:02:18 +01:00
dw-0 6c9a78496a fix(client_utils): ensure proper type conversion and hints for improved safety
- Standardize `str()` wrapping for color-applied strings in various returns.
- Refine type hints for better code clarity and robustness.
- Add null checks for port inputs to prevent potential errors.
2026-01-18 15:58:25 +01:00
dw-0 123ccde378 fix(core): standardize handling of None values for repo and version fields
- Improve local and remote version comparison by replacing default placeholders with None.
- Update repo and branch logic to handle None values consistently.
- Refactor type hints for better readability and accuracy.
2026-01-18 15:49:48 +01:00
dw-0andGitHub 45fde808d2 fix(update_menu): refresh component status after updates (#764)
fix(update_menu): refresh component status after updates to ensure consistency
2026-01-18 14:53:04 +01:00
dw-0andGitHub 8ba134f574 fix(ui): handle missing or empty local version values and standardize defaults (#763) 2026-01-18 14:18:27 +01:00
Théo GaillardandGitHub f951936b20 fix: navigation fallthrough in 'Get MCU ID' (Advanced Menu) (#762)
* fix: corrected back navigation in advanced menu in reference to #761

* ci: add delay after warning when no MCUs are found
2026-01-18 12:16:45 +01:00
dw-0 657d919378 chore: extend copyright year to 2026 in all headers 2026-01-11 09:37:17 +01:00
StaubgeborenerandGitHub 24c9b9daa9 chore(Klipper-Backup): Update Copyright year to 2026 (#758)
* Update copyright year to 2026

* Update copyright year in klipper_backup_extension.py
2026-01-11 09:29:33 +01:00
Thijs TriemstraandGitHub 4c511017f1 README: Update KlipperScreen link and contributor name (#753)
Update KlipperScreen link and contributor name

update maintainer for klipperscreen
2025-11-30 17:42:36 +01:00
CliffordandGitHub 372bab8847 feat(gcode_shell_command): allowing for expanding env vars (#747)
allowing for expanding env vars
2025-11-23 12:53:52 +01:00
d5062d41de refactor: remove dependency on libatlas-base-dev (#744)
Remove dependency on libatlas-base-dev

Co-authored-by: charlie-lima-bean <ktoaster@pm.me>
2025-11-23 09:25:05 +01:00
dw-0 e9459bd68e fix(backup): correct backup folder path display in menu 2025-11-09 11:58:03 +01:00
dw-0 ee460663c9 fix(spoolman): ensure proper file handling when adding Spoolman entry 2025-10-28 12:12:36 +01:00
dw-0 6f0e0146ef fix(client): improve version retrieval logic and handle JSON errors 2025-10-27 19:00:08 +01:00
dw-0 229f317025 fix(backup): do not create redundant subdirectory on single file backup 2025-10-27 09:47:09 +01:00
dw-0 48c0ae7227 fix(backup): allow reusing existing backup directory and enhance copy options 2025-10-27 09:30:33 +01:00
dw-0andGitHub 9c7b5fcb10 fix: update scp submodule so duplicate sections are preserved while editing configs (#738)
* fix: improve repository parsing logic to handle empty lines and comments more effectively

* fix: update scp submodule so duplicate sections are preserved while editing configs (#735)

* Squashed 'kiauh/core/submodules/simple_config_parser/' changes from f5eee99..5bc9e0a

5bc9e0a docs: update README
394dd7b refactor!: improve parsing and writing for config (#5)

git-subtree-dir: kiauh/core/submodules/simple_config_parser
git-subtree-split: 5bc9e0a50947f1be2f4877a10ab3a632774f82ea

* fix(logging): change warning to error message for config creation failure

* fix(config): improve readability by using descriptive variable names for options

(cherry picked from commit ae0a6b697e)

* Squashed 'kiauh/core/submodules/simple_config_parser/' changes from 5bc9e0a..eef8861

eef8861 refactor: update type hint for fallback parameter to Any
5d04325 Revert "chore: use Optional instead of | and None instead of _UNSET"

git-subtree-dir: kiauh/core/submodules/simple_config_parser
git-subtree-split: eef8861f126ddf84012ac8bed77b467926016d3e

* Squashed 'kiauh/core/submodules/simple_config_parser/' changes from eef8861..9c89612

9c89612 fix: correct assignment of raw value in option handling

git-subtree-dir: kiauh/core/submodules/simple_config_parser
git-subtree-split: 9c896124cf624e25410714649d306001250482f1

* fix: remove unnecessary whitespace in trusted_clients formatting
2025-10-26 22:03:26 +01:00
dw-0andGitHub 191bdd4874 Revert "fix: update scp submodule so duplicate sections are preserved… (#737)
Revert "fix: update scp submodule so duplicate sections are preserved while editing configs (#735)"

This reverts commit ae0a6b697e.
2025-10-26 18:58:33 +01:00
221 changed files with 5020 additions and 1105 deletions
+1
View File
@@ -7,6 +7,7 @@
*.tmp
__pycache__
.kiauh-env
.venv
*.code-workspace
*.iml
kiauh.cfg
+42 -31
View File
@@ -122,9 +122,9 @@ changes!**
<hr>
<h2 align="center">🌐 Sources & Further Information</h2>
<h2 align="center">⚙️ Core Components ⚙️</h2>
<table align="center">
<table align="center" style="text-align: center;">
<tr>
<th><h3><a href="https://github.com/Klipper3d/klipper">Klipper</a></h3></th>
<th><h3><a href="https://github.com/Arksine/moonraker">Moonraker</a></h3></th>
@@ -140,67 +140,88 @@ changes!**
<th>by <a href="https://github.com/Arksine">Arksine</a></th>
<th>by <a href="https://github.com/mainsail-crew">mainsail-crew</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/fluidd-core/fluidd">Fluidd</a></h3></th>
<th><h3><a href="https://github.com/jordanruthe/KlipperScreen">KlipperScreen</a></h3></th>
<th><h3><a href="https://github.com/OctoPrint/OctoPrint">OctoPrint</a></h3></th>
<th><h3><a href="https://github.com/KlipperScreen/KlipperScreen">KlipperScreen</a></h3></th>
<th></th>
</tr>
<tr>
<th><img src="https://raw.githubusercontent.com/fluidd-core/fluidd/master/docs/assets/images/logo.svg" alt="Fluidd Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/31575189?v=4" alt="jordanruthe avatar" height="64"></th>
<th><img src="https://raw.githubusercontent.com/OctoPrint/OctoPrint/master/docs/images/octoprint-logo.png" alt="OctoPrint Logo" height="64"></th>
<th><img src="https://raw.githubusercontent.com/fluidd-core/fluidd/master/docs/docs/assets/images/logo.svg" alt="Fluidd Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/KlipperScreen?v=4" alt="KlipperScreen Logo" height="64"></th>
<th></th>
</tr>
<tr>
<th>by <a href="https://github.com/fluidd-core">fluidd-core</a></th>
<th>by <a href="https://github.com/jordanruthe">jordanruthe</a></th>
<th>by <a href="https://github.com/OctoPrint">OctoPrint</a></th>
<th>by <a href="https://github.com/alfrix">alfrix</a></th>
<th></th>
</tr>
</table>
<hr>
<h2 align="center">🧩 Community Extensions 🧩</h2>
<table align="center" style="text-align: center;">
<tr>
<th><h3><a href="https://github.com/OctoPrint/OctoPrint">OctoPrint</a></h3></th>
<th><h3><a href="https://github.com/nlef/moonraker-telegram-bot">Moonraker-Telegram-Bot</a></h3></th>
<th><h3><a href="https://github.com/Kragrathea/pgcode">PrettyGCode for Klipper</a></h3></th>
<th><h3><a href="https://github.com/TheSpaghettiDetective/moonraker-obico">Obico for Klipper</a></h3></th>
</tr>
<tr>
<th><img src="https://avatars.githubusercontent.com/u/52351624?v=4" alt="nlef avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/5917231?v=4" alt="Kragrathea avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/46323662?s=200&v=4" alt="Obico logo" height="64"></th>
<th><a href="https://github.com/OctoPrint/OctoPrint"><img src="https://raw.githubusercontent.com/OctoPrint/OctoPrint/master/docs/images/octoprint-logo.png" alt="OctoPrint Logo" height="64"></a></th>
<th><a href="https://github.com/nlef/moonraker-telegram-bot"><img src="https://avatars.githubusercontent.com/u/52351624?v=4" alt="nlef avatar" height="64"></a></th>
<th><a href="https://github.com/Kragrathea/pgcode"><img src="https://avatars.githubusercontent.com/u/5917231?v=4" alt="Kragrathea avatar" height="64"></a></th>
</tr>
<tr>
<th>by <a href="https://github.com/OctoPrint">OctoPrint</a></th>
<th>by <a href="https://github.com/nlef">nlef</a></th>
<th>by <a href="https://github.com/Kragrathea">Kragrathea</a></th>
<th>by <a href="https://github.com/TheSpaghettiDetective">Obico</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/TheSpaghettiDetective/moonraker-obico">Obico for Klipper</a></h3></th>
<th><h3><a href="https://github.com/Clon1998/mobileraker_companion">Mobileraker's Companion</a></h3></th>
<th><h3><a href="https://octoeverywhere.com/?source=kiauh_readme">OctoEverywhere For Klipper</a></h3></th>
<th><h3><a href="https://github.com/crysxd/OctoApp-Plugin">OctoApp For Klipper</a></h3></th>
</tr>
<tr>
<th><a href="https://github.com/TheSpaghettiDetective/moonraker-obico"><img src="https://avatars.githubusercontent.com/u/46323662?s=200&v=4" alt="Obico logo" height="64"></a></th>
<th><a href="https://github.com/Clon1998/mobileraker_companion"><img src="https://raw.githubusercontent.com/Clon1998/mobileraker/master/assets/icon/mr_appicon.png" alt="Mobileraker Logo" height="64"></a></th>
<th><a href="https://octoeverywhere.com/?source=kiauh_readme"><img src="https://octoeverywhere.com/img/logo.svg" alt="OctoEverywhere Logo" height="64"></a></th>
<th><a href="https://octoapp.eu/?source=kiauh_readme"><img src="https://octoapp.eu/octoapp.webp" alt="OctoApp Logo" height="64"></a></th>
</tr>
<tr>
<th>by <a href="https://github.com/TheSpaghettiDetective">Obico</a></th>
<th>by <a href="https://github.com/Clon1998">Patrick Schmidt</a></th>
<th>by <a href="https://github.com/QuinnDamerell">Quinn Damerell</a></th>
<th>by <a href="https://github.com/crysxd">Christian Würthner</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/crysxd/OctoApp-Plugin">OctoApp For Klipper</a></h3></th>
<th><h3><a href="https://github.com/staubgeborener/klipper-backup">Klipper-Backup</a></h3></th>
<th><h3><a href="https://simplyprint.io/">SimplyPrint for Klipper</a></h3></th>
</tr>
<tr>
<th><a href="https://octoapp.eu/?source=kiauh_readme"><img src="https://octoapp.eu/octoapp.webp" alt="OctoApp Logo" height="64"></a></th>
<th><a href="https://github.com/staubgeborener/klipper-backup"><img src="https://avatars.githubusercontent.com/u/28908603?v=4" alt="Staubgeroner Avatar" height="64"></a></th>
<th><a href="https://github.com/SimplyPrint"><img src="https://avatars.githubusercontent.com/u/64896552?s=200&v=4" alt="" height="64"></a></th>
<th><a href="https://github.com/SimplyPrint"><img src="https://avatars.githubusercontent.com/u/64896552?s=200&v=4" alt="SimplyPrint Logo" height="64"></a></th>
</tr>
<tr>
<th>by <a href="https://github.com/crysxd">Christian Würthner</a></th>
<th>by <a href="https://github.com/Staubgeborener">Staubgeborener</a></th>
<th>by <a href="https://github.com/SimplyPrint">SimplyPrint</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/CodeMasterCody3D/DroidKlipp">DroidKlipp</a></h3></th>
<th><h3><a href="https://github.com/PEEKYPAUL/Moongate">Moongate</a></h3></th>
<th></th>
</tr>
<tr>
<th><a href="https://github.com/CodeMasterCody3D/DroidKlipp"><img src="https://raw.githubusercontent.com/CodeMasterCody3D/DroidKlipp/main/logo.png" alt="DroidKlipp Logo" height="64"></a></th>
<th><a href="https://github.com/PEEKYPAUL/Moongate"><img src="https://raw.githubusercontent.com/PEEKYPAUL/Moongate/master/docs/moongate-icon.png" alt="Moongate Logo" height="64"></a></th>
<th></th>
</tr>
<tr>
<th>by <a href="https://github.com/CodeMasterCody3D">CodeMasterCody3D</a></th>
<th>by <a href="https://github.com/PEEKYPAUL">PEEKYPAUL</a></th>
<th></th>
</tr>
</table>
<hr>
@@ -229,13 +250,3 @@ changes!**
a [Ko-fi](https://ko-fi.com/dw__0) !
* Last but not least: Thank you to all contributors and members of the Klipper
Community who like and share this project!
<hr>
<h4 align="center">A special thank you to JetBrains for sponsoring this project
with their incredible software!</h4>
<p align="center">
<a href="https://www.jetbrains.com/community/opensource/#support" target="_blank">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." height="128">
</a>
</p>
+51 -33
View File
@@ -2,7 +2,7 @@
<p align="center">
<a>
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/kiauh.png" alt="KIAUH logo" height="181">
<img src="docs/assets/logo-large.png" alt="KIAUH logo" height="181">
<h1 align="center">Klipper Installation And Update Helper</h1>
</a>
</p>
@@ -34,13 +34,13 @@ KIAUH 是一个帮助您在 Linux 系统上安装 Klipper 的脚本工具,
选择 `Choose OS -> Raspberry Pi OS (other)`:
<p align="center">
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/rpi_imager1.png" alt="KIAUH logo" height="350">
<img src="docs/assets/rpi_imager1.png" alt="KIAUH logo" height="350">
</p>
* 然后选择 `Raspberry Pi OS Lite (32位)` (或如果您想使用64位版本):
<p align="center">
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/rpi_imager2.png" alt="KIAUH logo" height="350">
<img src="docs/assets/rpi_imager2.png" alt="KIAUH logo" height="350">
</p>
* 返回 Raspberry Pi Imager 主界面,选择对应的 SD 卡作为写入目标。
@@ -99,9 +99,9 @@ cd ~ && git clone https://github.com/dw-0/kiauh.git
您会被要求输入 sudo 密码。
因为有几个功能需要 sudo 权限。
## 🌐 相关资源与更多信息
<h2 align="center">⚙️ 核心组件 ⚙️</h2>
<table align="center">
<table align="center" style="text-align: center;">
<tr>
<th><h3><a href="https://github.com/Klipper3d/klipper">Klipper</a></h3></th>
<th><h3><a href="https://github.com/Arksine/moonraker">Moonraker</a></h3></th>
@@ -117,70 +117,93 @@ cd ~ && git clone https://github.com/dw-0/kiauh.git
<th>由 <a href="https://github.com/Arksine">Arksine</a></th>
<th>由 <a href="https://github.com/mainsail-crew">mainsail-crew</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/fluidd-core/fluidd">Fluidd</a></h3></th>
<th><h3><a href="https://github.com/jordanruthe/KlipperScreen">KlipperScreen</a></h3></th>
<th><h3><a href="https://github.com/OctoPrint/OctoPrint">OctoPrint</a></h3></th>
<th><h3><a href="https://github.com/KlipperScreen/KlipperScreen">KlipperScreen</a></h3></th>
<th></th>
</tr>
<tr>
<th><img src="https://raw.githubusercontent.com/fluidd-core/fluidd/master/docs/assets/images/logo.svg" alt="Fluidd Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/31575189?v=4" alt="jordanruthe avatar" height="64"></th>
<th><img src="https://raw.githubusercontent.com/OctoPrint/OctoPrint/master/docs/images/octoprint-logo.png" alt="OctoPrint Logo" height="64"></th>
<th><img src="https://raw.githubusercontent.com/fluidd-core/fluidd/master/docs/docs/assets/images/logo.svg" alt="Fluidd Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/KlipperScreen?v=4" alt="KlipperScreen Logo" height="64"></th>
<th></th>
</tr>
<tr>
<th>由 <a href="https://github.com/fluidd-core">fluidd-core</a></th>
<th>由 <a href="https://github.com/jordanruthe">jordanruthe</a></th>
<th>由 <a href="https://github.com/OctoPrint">OctoPrint</a></th>
<th>由 <a href="https://github.com/alfrix">alfrix</a></th>
<th></th>
</tr>
</table>
<hr>
<h2 align="center">🧩 社区扩展 🧩</h2>
<table align="center" style="text-align: center;">
<tr>
<th><h3><a href="https://github.com/OctoPrint/OctoPrint">OctoPrint</a></h3></th>
<th><h3><a href="https://github.com/nlef/moonraker-telegram-bot">Moonraker-Telegram-Bot</a></h3></th>
<th><h3><a href="https://github.com/Kragrathea/pgcode">PrettyGCode for Klipper</a></h3></th>
<th><h3><a href="https://github.com/TheSpaghettiDetective/moonraker-obico">Obico for Klipper</a></h3></th>
</tr>
<tr>
<th><img src="https://avatars.githubusercontent.com/u/52351624?v=4" alt="nlef avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/5917231?v=4" alt="Kragrathea avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/46323662?s=200&v=4" alt="Obico logo" height="64"></th>
<th><a href="https://github.com/OctoPrint/OctoPrint"><img src="https://raw.githubusercontent.com/OctoPrint/OctoPrint/master/docs/images/octoprint-logo.png" alt="OctoPrint Logo" height="64"></a></th>
<th><a href="https://github.com/nlef/moonraker-telegram-bot"><img src="https://avatars.githubusercontent.com/u/52351624?v=4" alt="nlef avatar" height="64"></a></th>
<th><a href="https://github.com/Kragrathea/pgcode"><img src="https://avatars.githubusercontent.com/u/5917231?v=4" alt="Kragrathea avatar" height="64"></a></th>
</tr>
<tr>
<th>由 <a href="https://github.com/OctoPrint">OctoPrint</a></th>
<th>由 <a href="https://github.com/nlef">nlef</a></th>
<th>由 <a href="https://github.com/Kragrathea">Kragrathea</a></th>
<th>由 <a href="https://github.com/TheSpaghettiDetective">Obico</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/TheSpaghettiDetective/moonraker-obico">Obico for Klipper</a></h3></th>
<th><h3><a href="https://github.com/Clon1998/mobileraker_companion">Mobileraker's Companion</a></h3></th>
<th><h3><a href="https://octoeverywhere.com/?source=kiauh_readme">OctoEverywhere For Klipper</a></h3></th>
<th><h3><a href="https://github.com/crysxd/OctoApp-Plugin">OctoApp For Klipper</a></h3></th>
</tr>
<tr>
<th><a href="https://github.com/TheSpaghettiDetective/moonraker-obico"><img src="https://avatars.githubusercontent.com/u/46323662?s=200&v=4" alt="Obico logo" height="64"></a></th>
<th><a href="https://github.com/Clon1998/mobileraker_companion"><img src="https://raw.githubusercontent.com/Clon1998/mobileraker/master/assets/icon/mr_appicon.png" alt="Mobileraker Logo" height="64"></a></th>
<th><a href="https://octoeverywhere.com/?source=kiauh_readme"><img src="https://octoeverywhere.com/img/logo.svg" alt="OctoEverywhere Logo" height="64"></a></th>
<th><a href="https://octoapp.eu/?source=kiauh_readme"><img src="https://octoapp.eu/octoapp.webp" alt="OctoApp Logo" height="64"></a></th>
</tr>
<tr>
<th>由 <a href="https://github.com/TheSpaghettiDetective">Obico</a></th>
<th>由 <a href="https://github.com/Clon1998">Patrick Schmidt</a></th>
<th>由 <a href="https://github.com/QuinnDamerell">Quinn Damerell</a></th>
<th>由 <a href="https://github.com/crysxd">Christian Würthner</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/crysxd/OctoApp-Plugin">OctoApp For Klipper</a></h3></th>
<th><h3><a href="https://github.com/staubgeborener/klipper-backup">Klipper-Backup</a></h3></th>
<th><h3><a href="https://simplyprint.io/">SimplyPrint for Klipper</a></h3></th>
</tr>
<tr>
<th><a href="https://octoapp.eu/?source=kiauh_readme"><img src="https://octoapp.eu/octoapp.webp" alt="OctoApp Logo" height="64"></a></th>
<th><a href="https://github.com/staubgeborener/klipper-backup"><img src="https://avatars.githubusercontent.com/u/28908603?v=4" alt="Staubgeroner Avatar" height="64"></a></th>
<th><a href="https://github.com/SimplyPrint"><img src="https://avatars.githubusercontent.com/u/64896552?s=200&v=4" alt="" height="64"></a></th>
<th><a href="https://github.com/SimplyPrint"><img src="https://avatars.githubusercontent.com/u/64896552?s=200&v=4" alt="SimplyPrint Logo" height="64"></a></th>
</tr>
<tr>
<th>由 <a href="https://github.com/crysxd">Christian Würthner</a></th>
<th>由 <a href="https://github.com/Staubgeborener">Staubgeborener</a></th>
<th>由 <a href="https://github.com/SimplyPrint">SimplyPrint</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/CodeMasterCody3D/DroidKlipp">DroidKlipp</a></h3></th>
<th><h3><a href="https://github.com/PEEKYPAUL/Moongate">Moongate</a></h3></th>
<th></th>
</tr>
<tr>
<th><a href="https://github.com/CodeMasterCody3D/DroidKlipp"><img src="https://raw.githubusercontent.com/CodeMasterCody3D/DroidKlipp/main/logo.png" alt="DroidKlipp Logo" height="64"></a></th>
<th><a href="https://github.com/PEEKYPAUL/Moongate"><img src="https://raw.githubusercontent.com/PEEKYPAUL/Moongate/master/docs/moongate-icon.png" alt="Moongate Logo" height="64"></a></th>
<th></th>
</tr>
<tr>
<th>由 <a href="https://github.com/CodeMasterCody3D">CodeMasterCody3D</a></th>
<th>由 <a href="https://github.com/PEEKYPAUL">PEEKYPAUL</a></th>
<th></th>
</tr>
</table>
## 🎖️ 贡献者
<hr>
<h2 align="center">🎖️ 贡献者 🎖️</h2>
<div align="center">
<a href="https://github.com/dw-0/kiauh/graphs/contributors">
@@ -192,15 +215,10 @@ cd ~ && git clone https://github.com/dw-0/kiauh.git
<img src="https://repobeats.axiom.co/api/embed/a1afbda9190c04a90cf4bd3061e5573bc836cb05.svg" alt="Repobeats analytics image"/>
</div>
## ✨ 特别感谢
<hr>
<h2 align="center">✨ 特别感谢 ✨</h2>
* 非常感谢 [lixxbox](https://github.com/lixxbox) 设计了如此出色的 KIAUH 标志!
* 同时,非常感谢所有通过 [Ko-fi](https://ko-fi.com/dw__0) 支持我的工作的人!
* 最后但同样重要的是:感谢所有为 Klipper 社区做出贡献的成员,以及喜欢和分享这个项目的朋友们!
<h4 align="center">特别感谢 JetBrains 为本项目提供其出色的软件赞助!</h4>
<p align="center">
<a href="https://www.jetbrains.com/community/opensource/#support" target="_blank">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." height="128">
</a>
</p>
+82
View File
@@ -0,0 +1,82 @@
# Live System Testing
Live tests run KIAUH workflows against a real Debian 12 QEMU/KVM VM. They are
isolated from the local developer machine by design.
## Safety Rules
- Live tests NEVER run on the local machine.
- They require `KIAUH_LIVE_ALLOW=1`.
- The target host must be explicitly set via `KIAUH_LIVE_TARGET_HOST` and match
the VM in the inventory.
- Local hostnames, loopback addresses, and the current hostname are blocked.
## Prepare a VM
1. Create a Debian 12 QEMU/KVM VM.
2. Create a user with passwordless sudo.
3. Install an SSH key for that user.
4. Install KIAUH on the VM (e.g. clone this repository).
5. Create a clean snapshot named `clean`:
```bash
virsh snapshot-create-as debian12-kiauh clean
```
## Inventory
`kiauh/live/inventory.yaml` defines non-sensitive VM settings. The host and SSH
key path are read from environment variables or a `.env` file so they are not
committed.
Create `.env` in the project root:
```bash
KIAUH_LIVE_DEBIAN12_KIAUH_HOST=192.168.122.10
KIAUH_LIVE_DEBIAN12_KIAUH_KEY_FILE=/home/you/.ssh/kiauh_vm
```
Variable naming: `KIAUH_LIVE_<VM_NAME>_HOST` and `KIAUH_LIVE_<VM_NAME>_KEY_FILE`,
with the VM name uppercased and hyphens replaced by underscores.
You can also point to a custom inventory file:
```bash
export KIAUH_LIVE_INVENTORY=/path/to/inventory.yaml
```
## Run Live Tests
```bash
export KIAUH_LIVE_ALLOW=1
export KIAUH_LIVE_TARGET_HOST=192.168.122.10
pytest -m live
```
Each scenario reverts the VM to the clean snapshot first, so scenarios are
independent.
## Add a Scenario
Create a YAML file in `kiauh/live/scenarios/`:
```yaml
name: Install Klipper on Debian 12
vm: debian12-kiauh
os: debian-12
steps:
- command: ["kiauh", "install", "klipper", "--count", "1"]
timeout: 600
expected:
- type: service
name: klipper.service
state: running
```
Supported assertion types: `service`, `file`, `package`, `port`, `command`.
## Troubleshooting
- `UnsafeTargetError`: check `KIAUH_LIVE_ALLOW` and `KIAUH_LIVE_TARGET_HOST`.
- `InventoryError`: check the inventory YAML path and format.
- `LiveRunnerError` during snapshot revert: ensure `virsh` works and the domain
and snapshot names match the inventory.
+74
View File
@@ -0,0 +1,74 @@
## ⚠️ Working on this PRD
Do NOT implement this PRD directly. It has been broken into sequential tasks.
Work through the tasks below in order.
## Task Index
| # | Task | Todo | Blocked by | Status |
|---|------|------|------------|--------|
| 1/5 | VM inventory, SSH fixture, and safety guards | TODO-006 | — | 🔄 open |
| 2/5 | Scenario loader and Klipper install scenario | TODO-007 | TODO-006 | ⏳ blocked |
| 3/5 | Remove Klipper and Moonraker scenarios | TODO-008 | TODO-007 | ⏳ blocked |
| 4/5 | Mainsail and Fluidd install scenarios | TODO-009 | TODO-008 | ⏳ blocked |
| 5/5 | Backup, restore, and update scenarios | TODO-010 | TODO-009 | ⏳ blocked |
Start with: **TODO-006** (PRD #2 - Task 1/5: VM inventory, SSH fixture, and safety guards)
---
# PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM
**Tags:** `prd`, `prd-2`
## Problem Statement
- Pytest unit tests cannot validate real package installs, systemd services, git clones, and OS-specific behavior.
- Running workflow tests on a local developer machine risks destroying the environment.
- Need reproducible, isolated acceptance tests with explicit expected outcomes.
## Solution
- Acceptance tests run only on a pre-built Debian 12 QEMU/KVM VM.
- Test harness connects via SSH; never executes on the local host.
- YAML scenarios define workflows and expected outcomes.
- VM snapshot reverted before every scenario.
- Multi-layer safety prevents local execution.
## User Stories
1. As a maintainer, I want install/remove Klipper workflow tested on a real VM, so I know the installer still works.
2. As a maintainer, I want install/remove Moonraker workflow tested, so API stack compatibility is verified.
3. As a maintainer, I want Mainsail/Fluidd install workflow tested, so web client setup works end-to-end.
4. As a maintainer, I want backup/restore workflow tested, so user data survives the cycle.
5. As a maintainer, I want tests parameterized by VM inventory, so future Ubuntu/Debian versions can be added without code changes.
6. As a maintainer, I want local execution blocked by multiple guards, so the developer machine is never modified.
7. As a CI operator, I want scenario results to show expected vs actual outcome, so failures are actionable.
## Implementation Decisions
- VM inventory config supplies host/IP, SSH user/key, OS family. No auto-provisioning; base images are prepared in advance.
- Test harness: pytest + SSH fixture + Testinfra assertions. Commands are routed through SSH; assertions use Testinfra modules for service/file/package/port state.
- Scenario schema YAML: `name`, `os`, `steps` (commands/options), `expected` (assertions for service running, file exists, package installed, port reachable, process present).
- Snapshot reset: revert VM overlay before every scenario. Scenarios must be independent.
- Safety guards: require `KIAUH_LIVE_TARGET_HOST`; abort if value is `localhost`, `127.*`, or matches current hostname; abort if target resolves to a local interface; verify SSH host key differs from local; optional explicit confirmation prompt.
- Workflow priority: (1) install/remove Klipper; (2) install/remove Moonraker; (3) install Mainsail/Fluidd; (4) backup/restore; (5) update flows.
- Test user on VM has passwordless sudo; VM has internet access; long installs use timeouts.
- Scenario runner exposes expected outcome per step; failure shows command, expected assertion, and actual result.
## Testing Decisions
- Acceptance tests verify observable system state, not internal functions.
- Each scenario defines exact pre-state (clean snapshot) and post-state assertions.
- Flaky network commands are retried with timeout; failures attach relevant VM logs.
- Live suite is marked with a `live` pytest marker and excluded from the default `pytest` run.
## Out of Scope
- Running live tests on the local machine or bare metal.
- Auto-provisioning or building VM images.
- Testing every extension in the first iteration; only core workflows.
## Further Notes
- Future OS matrix (Ubuntu 22.04/24.04) is enabled by adding inventory entries and matching base images; no harness changes needed.
+31
View File
@@ -0,0 +1,31 @@
# PRD #2 - Task 1/5: VM inventory, SSH fixture, and safety guards
**Tags:** `task`, `prd-2`
## Parent PRD
PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM (`docs/prd/PRD-002-live-vm-test-strategy.md`)
## What to build
Create the VM inventory config schema (host/IP, SSH user/key, OS family). Implement the SSH connection fixture and the multi-layer safety guards that abort if the target could be the local machine. Write tests for the guards without executing any workflow.
## Acceptance criteria
- [ ] Inventory config schema documented and validated.
- [ ] SSH fixture connects only when target is explicitly allowed.
- [ ] Guards block `localhost`, `127.*`, current hostname, local interfaces, and unknown SSH host keys.
- [ ] Guard tests run on the local machine and prove the blocks work.
## Blocked by
None — can start immediately.
## Next task
- TODO-007 (PRD #2 - Task 2/5: Scenario loader and Klipper install scenario)
## User stories addressed
- User story 5
- User story 6
+32
View File
@@ -0,0 +1,32 @@
# PRD #2 - Task 2/5: Scenario loader and Klipper install scenario
**Tags:** `task`, `prd-2`
## Parent PRD
PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM (`docs/prd/PRD-002-live-vm-test-strategy.md`)
## What to build
Implement the YAML scenario loader and the `live` pytest marker. Write the first end-to-end scenario: install Klipper on the Debian 12 VM, define expected outcomes (service file, env file, folders), and run it with snapshot revert before the scenario.
## Acceptance criteria
- [ ] YAML scenario loader parses `name`, `os`, `steps`, and `expected` assertions.
- [ ] `pytest -m live` runs only live scenarios; default run skips them.
- [ ] Klipper install scenario runs on the VM and passes.
- [ ] Snapshot revert happens before the scenario.
- [ ] Expected outcomes include file, service, and folder assertions.
## Blocked by
- TODO-006 (PRD #2 - Task 1/5: VM inventory, SSH fixture, and safety guards)
## Next task
- TODO-008 (PRD #2 - Task 3/5: Remove Klipper and Moonraker scenarios)
## User stories addressed
- User story 1
- User story 7
+30
View File
@@ -0,0 +1,30 @@
# PRD #2 - Task 3/5: Remove Klipper and Moonraker scenarios
**Tags:** `task`, `prd-2`
## Parent PRD
PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM (`docs/prd/PRD-002-live-vm-test-strategy.md`)
## What to build
Add remove-Klipper and install/remove-Moonraker scenarios. Each scenario starts from a clean snapshot. Capture relevant VM logs when a scenario fails to make debugging actionable.
## Acceptance criteria
- [ ] Remove Klipper scenario runs and verifies service/files are gone.
- [ ] Install Moonraker scenario runs and verifies service/config/log files.
- [ ] Remove Moonraker scenario runs and verifies cleanup.
- [ ] Failure output includes tail of installer/service logs.
## Blocked by
- TODO-007 (PRD #2 - Task 2/5: Scenario loader and Klipper install scenario)
## Next task
- TODO-009 (PRD #2 - Task 4/5: Mainsail and Fluidd install scenarios)
## User stories addressed
- User story 2
+30
View File
@@ -0,0 +1,30 @@
# PRD #2 - Task 4/5: Mainsail and Fluidd install scenarios
**Tags:** `task`, `prd-2`
## Parent PRD
PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM (`docs/prd/PRD-002-live-vm-test-strategy.md`)
## What to build
Add install scenarios for Mainsail and Fluidd web clients. Assert that the static files are deployed and the reverse-proxy/service config is in place. Harden the snapshot-revert fixture so it runs reliably before every scenario.
## Acceptance criteria
- [ ] Mainsail install scenario passes on the VM.
- [ ] Fluidd install scenario passes on the VM.
- [ ] Expected outcomes check webroot directory and reverse-proxy config.
- [ ] Snapshot revert fixture is robust (wait for SSH, error on revert failure).
## Blocked by
- TODO-008 (PRD #2 - Task 3/5: Remove Klipper and Moonraker scenarios)
## Next task
- TODO-010 (PRD #2 - Task 5/5: Backup, restore, and update scenarios)
## User stories addressed
- User story 3
+32
View File
@@ -0,0 +1,32 @@
# PRD #2 - Task 5/5: Backup, restore, and update scenarios
**Tags:** `task`, `prd-2`
## Parent PRD
PRD #2: Isolated live-system acceptance test strategy on Debian 12 VM (`docs/prd/PRD-002-live-vm-test-strategy.md`)
## What to build
Add backup/restore and update scenarios. Document the live test runbook: how to prepare the VM, set inventory, run scenarios, and read results. Ensure the whole live suite can be executed in one command.
## Acceptance criteria
- [ ] Backup scenario creates an archive with expected content.
- [ ] Restore scenario returns config files to expected state.
- [ ] Update scenario changes a component version/config observable on the VM.
- [ ] Runbook `docs/live-testing.md` covers VM setup, inventory, execution, and troubleshooting.
- [ ] Full `pytest -m live` run completes end-to-end.
## Blocked by
- TODO-009 (PRD #2 - Task 4/5: Mainsail and Fluidd install scenarios)
## Next task
None — this is the last task.
## User stories addressed
- User story 4
- User story 7
+21 -7
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
#=======================================================================#
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -15,18 +15,30 @@ clear -x
# make sure we have the correct permissions while running the script
umask 022
# gets the path where this script is located
KIAUH_SRCDIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd)
# colors
white="\033[37m"
cyan="\033[96m"
red="\033[91m"
yellow="\033[93m"
green="\033[92m"
#===================================================#
#=================== UPDATE KIAUH ==================#
#===================================================#
function update_kiauh() {
status_msg "Updating KIAUH ..."
echo "Updating KIAUH ..."
cd "${KIAUH_SRCDIR}"
git reset --hard && git pull
ok_msg "Update complete! Please restart KIAUH."
exit 0
echo "Update complete! Restarting..."
sleep 1
exec "$0" "$@" # restarts the script
}
#===================================================#
@@ -65,16 +77,18 @@ function kiauh_update_dialog() {
echo -e "\-------------------------------------------------------/"
local yn
read -p "${cyan}###### Do you want to update now? (Y/n):${white} " yn
echo -ne "${cyan}###### Do you want to update now? (Y/n):${white} "
read yn
while true; do
case "${yn}" in
Y|y|Yes|yes|"")
do_action "update_kiauh"
update_kiauh
break;;
N|n|No|no)
break;;
*)
deny_action "kiauh_update_dialog";;
echo -e "${red}Invalid input. Please try again.${white}"
kiauh_update_dialog;;
esac
done
}
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+3 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -19,10 +19,12 @@ CROWSNEST_SERVICE_NAME = "crowsnest.service"
# directories
CROWSNEST_DIR = Path.home().joinpath("crowsnest")
CROWSNEST_ENV_DIR = Path.home().joinpath("crowsnest-env")
# files
CROWSNEST_MULTI_CONFIG = CROWSNEST_DIR.joinpath("tools/.config")
CROWSNEST_INSTALL_SCRIPT = CROWSNEST_DIR.joinpath("tools/install.sh")
CROWSNEST_DEPS_JSON_FILE = CROWSNEST_DIR.joinpath("system-dependencies.json")
CROWSNEST_BIN_FILE = Path("/usr/local/bin/crowsnest")
CROWSNEST_LOGROTATE_FILE = Path("/etc/logrotate.d/crowsnest")
CROWSNEST_SERVICE_FILE = SYSTEMD.joinpath(CROWSNEST_SERVICE_NAME)
+70 -10
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -16,7 +16,9 @@ from typing import List
from components.crowsnest import (
CROWSNEST_BIN_FILE,
CROWSNEST_DEPS_JSON_FILE,
CROWSNEST_DIR,
CROWSNEST_ENV_DIR,
CROWSNEST_INSTALL_SCRIPT,
CROWSNEST_LOGROTATE_FILE,
CROWSNEST_MULTI_CONFIG,
@@ -25,6 +27,8 @@ from components.crowsnest import (
CROWSNEST_SERVICE_NAME,
)
from components.klipper.klipper import Klipper
from components.moonraker.utils.sysdeps_parser import SysDepsParser
from components.moonraker.utils.utils import load_sysdeps_json
from core.logger import DialogType, Logger
from core.services.backup_service import BackupService
from core.settings.kiauh_settings import KiauhSettings
@@ -34,6 +38,7 @@ from utils.common import (
get_install_status,
)
from utils.git_utils import (
get_current_branch,
git_clone_wrapper,
git_pull_wrapper,
)
@@ -47,7 +52,7 @@ from utils.sys_utils import (
def install_crowsnest() -> None:
# Step 1: Clone crowsnest repo
git_clone_wrapper(CROWSNEST_REPO, CROWSNEST_DIR, "master")
git_clone_wrapper(CROWSNEST_REPO, CROWSNEST_DIR)
# Step 2: Install dependencies
check_install_dependencies({"make"})
@@ -135,8 +140,7 @@ def update_crowsnest() -> None:
git_pull_wrapper(CROWSNEST_DIR)
deps = parse_packages_from_file(CROWSNEST_INSTALL_SCRIPT)
check_install_dependencies({*deps})
install_crowsnest_packages()
cmd_sysctl_service(CROWSNEST_SERVICE_NAME, "restart")
@@ -147,12 +151,68 @@ def update_crowsnest() -> None:
def get_crowsnest_status() -> ComponentStatus:
files = [
CROWSNEST_BIN_FILE,
CROWSNEST_LOGROTATE_FILE,
CROWSNEST_SERVICE_FILE,
]
return get_install_status(CROWSNEST_DIR, files=files)
"""
Get the current install status of Crowsnest. Depending on the version the installed
files are different. If a version is not yet specified, it will search for a
non_existant file resulting in 'Incomplete' status.
:return: Installation status
"""
files_dict = {
4: [
CROWSNEST_BIN_FILE,
CROWSNEST_LOGROTATE_FILE,
CROWSNEST_SERVICE_FILE,
],
5: [CROWSNEST_SERVICE_FILE],
}
version = get_crowsnest_version()
non_existant = CROWSNEST_DIR.joinpath("non_existant")
files = files_dict.get(version, [non_existant])
env_dir = None
if version >= 5:
env_dir = CROWSNEST_ENV_DIR
return get_install_status(CROWSNEST_DIR, files=files, env_dir=env_dir)
def get_crowsnest_version() -> int:
"""
Get the current major version. Starting with v5 the default branch will be named
after the major version.
:return: Current major version
"""
version = get_current_branch(CROWSNEST_DIR)
if version is None:
return 0
if version == "master":
return 4
return int(version.removeprefix("v"))
def install_crowsnest_packages() -> None:
Logger.print_status("Parsing Crowsnest system dependencies ...")
crowsnest_deps = []
crowsnest_version = get_crowsnest_version()
if crowsnest_version >= 5 and CROWSNEST_DEPS_JSON_FILE.exists():
Logger.print_info(
f"Parsing system dependencies from {CROWSNEST_DEPS_JSON_FILE.name} ..."
)
parser = SysDepsParser()
sysdeps = load_sysdeps_json(CROWSNEST_DEPS_JSON_FILE)
crowsnest_deps.extend(parser.parse_dependencies(sysdeps))
elif crowsnest_version <= 4 and CROWSNEST_INSTALL_SCRIPT.exists():
Logger.print_info(
f"Parsing system dependencies from {CROWSNEST_INSTALL_SCRIPT.name} ..."
)
crowsnest_deps = parse_packages_from_file(CROWSNEST_INSTALL_SCRIPT)
if not crowsnest_deps:
raise ValueError("Error parsing crowsnest dependencies!")
check_install_dependencies({*crowsnest_deps})
def remove_crowsnest() -> None:
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+3 -5
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -34,7 +34,7 @@ from core.constants import CURRENT_USER
from core.instance_manager.base_instance import SUFFIX_BLACKLIST
from core.logger import DialogType, Logger
from core.services.backup_service import BackupService
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
from core.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from core.types.component_status import ComponentStatus
@@ -113,7 +113,7 @@ def check_user_groups() -> None:
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):
log = "Skipped adding user to required groups. You might encounter issues."
Logger.warn(log)
Logger.print_warn(log)
return
try:
@@ -237,7 +237,6 @@ def install_input_shaper_deps() -> None:
"If you agree, the following additional system packages will be installed:",
"● python3-numpy",
"● python3-matplotlib",
"● libatlas-base-dev",
"● libopenblas-dev",
"\n\n",
"Also, the following Python package will be installed:",
@@ -253,7 +252,6 @@ def install_input_shaper_deps() -> None:
apt_deps = (
"python3-numpy",
"python3-matplotlib",
"libatlas-base-dev",
"libopenblas-dev",
)
check_install_dependencies({*apt_deps})
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -236,9 +236,11 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
if len(self.flash_options.mcu_list) < 1:
Logger.print_warn("No MCUs found!")
Logger.print_warn("Make sure they are connected and repeat this step.")
time.sleep(3)
return
# if standalone is True, we only display the MCUs to the user and return
if self.__standalone and len(self.flash_options.mcu_list) > 0:
if self.__standalone:
Logger.print_ok("The following MCUs were found:", prefix=False)
for i, mcu in enumerate(self.flash_options.mcu_list):
print(f" ● MCU #{i}: {Color.CYAN}{mcu}{Color.RST}")
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+2 -2
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -25,7 +25,7 @@ from components.moonraker import (
from core.constants import CURRENT_USER
from core.instance_manager.base_instance import BaseInstance
from core.logger import Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
from core.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from utils.fs_utils import create_folders
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+3 -3
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -25,7 +25,7 @@ from components.moonraker.utils.sysdeps_parser import SysDepsParser
from components.webui_client.base_data import BaseWebClient
from core.logger import Logger
from core.services.backup_service import BackupService
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
from core.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from core.types.component_status import ComponentStatus
@@ -123,7 +123,7 @@ def create_example_moonraker_conf(
scp = SimpleConfigParser()
scp.read_file(target)
trusted_clients: List[str] = [
f" {'.'.join(ip)}\n",
f"{'.'.join(ip)}",
*scp.getvals("authorization", "trusted_clients"),
]
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -8,7 +8,7 @@
# ======================================================================= #
from typing import List
from typing import List, Optional
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
@@ -27,6 +27,7 @@ def run_client_config_removal(
client_config: BaseWebClientConfig,
kl_instances: List[Klipper],
mr_instances: List[Moonraker],
svc: Optional[BackupService] = None,
) -> Message:
completion_msg = Message(
title=f"{client_config.display_name} Removal Process completed",
@@ -36,12 +37,15 @@ def run_client_config_removal(
if run_remove_routines(client_config.config_dir):
completion_msg.text.append(f"{client_config.display_name} removed")
BackupService().backup_printer_config_dir()
if svc is None:
svc = BackupService()
svc.backup_moonraker_conf()
completion_msg = remove_moonraker_config_section(
completion_msg, client_config, mr_instances
)
svc.backup_printer_cfg()
completion_msg = remove_printer_config_section(
completion_msg, client_config, kl_instances
)
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -41,6 +41,7 @@ def run_client_removal(
)
mr_instances: List[Moonraker] = get_instances(Moonraker)
kl_instances: List[Klipper] = get_instances(Klipper)
svc = BackupService()
if backup_config:
version = ""
@@ -49,7 +50,6 @@ def run_client_removal(
with open(src.joinpath(".version"), "r") as v:
version = v.readlines()[0]
svc = BackupService()
target_path = svc.backup_root.joinpath(f"{client.client_dir.name}_{version}")
success = svc.backup_file(
source_path=client.config_file,
@@ -67,7 +67,7 @@ def run_client_removal(
if remove_client_nginx_logs(client, kl_instances):
completion_msg.text.append("● NGINX logs removed")
BackupService().backup_moonraker_conf()
svc.backup_moonraker_conf()
section = f"update_manager {client_name}"
handled_instances: List[Moonraker] = remove_config_section(
section, mr_instances
@@ -83,6 +83,7 @@ def run_client_removal(
client.client_config,
kl_instances,
mr_instances,
svc,
)
if cfg_completion_msg.color == Color.GREEN:
completion_msg.text.extend(cfg_completion_msg.text[1:])
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+35 -17
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -11,6 +11,7 @@ from __future__ import annotations
import json
import re
import shutil
from json import JSONDecodeError
from pathlib import Path
from subprocess import PIPE, CalledProcessError, run
from typing import List, get_args
@@ -32,7 +33,7 @@ from core.constants import (
from core.logger import Logger
from core.services.backup_service import BackupService
from core.settings.kiauh_settings import KiauhSettings, WebUiSettings
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
from core.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from core.types.color import Color
@@ -77,10 +78,10 @@ def get_current_client_config() -> str:
installed = [c for c in clients if c.client_config.config_dir.exists()]
if not installed:
return Color.apply("-", Color.CYAN)
return str(Color.apply("-", Color.CYAN))
elif len(installed) == 1:
cfg = installed[0].client_config
return Color.apply(cfg.display_name, Color.CYAN)
return str(Color.apply(cfg.display_name, Color.CYAN))
# at this point, both client config folders exists, so we need to check
# which are actually included in the printer.cfg of all klipper instances
@@ -99,18 +100,18 @@ def get_current_client_config() -> str:
# if both are included in the same file, we have a potential conflict
if includes_mainsail and includes_fluidd:
return Color.apply("Conflict", Color.YELLOW)
return str(Color.apply("Conflict", Color.YELLOW))
if not mainsail_includes and not fluidd_includes:
# there are no includes at all, even though the client config folders exist
return Color.apply("-", Color.CYAN)
return str(Color.apply("-", Color.CYAN))
elif len(fluidd_includes) > len(mainsail_includes):
# there are more instances that include fluidd than mainsail
return Color.apply(fluidd.client_config.display_name, Color.CYAN)
return str(Color.apply(fluidd.client_config.display_name, Color.CYAN))
else:
# there are the same amount of non-conflicting includes for each config
# or more instances include mainsail than fluidd
return Color.apply(mainsail.client_config.display_name, Color.CYAN)
return str(Color.apply(mainsail.client_config.display_name, Color.CYAN))
def enable_mainsail_remotemode() -> None:
@@ -154,15 +155,32 @@ def get_local_client_version(client: BaseWebClient) -> str | None:
if not client.client_dir.exists():
return None
if not relinfo_file.is_file() and not version_file.is_file():
return "n/a"
# try to get version from release_info.json first
if relinfo_file.is_file():
with open(relinfo_file, "r") as f:
return str(json.load(f)["version"])
else:
with open(version_file, "r") as f:
return f.readlines()[0]
try:
if relinfo_file.stat().st_size == 0:
raise JSONDecodeError("Empty file", "", 0)
with open(relinfo_file, "r", encoding="utf-8") as f:
data = json.load(f)
raw_version = data.get("version")
if raw_version is not None:
parsed = str(raw_version).strip()
if parsed:
return parsed
except (JSONDecodeError, OSError):
Logger.print_error("Invalid 'release_info.json'")
# fallback to .version file
if version_file.is_file():
try:
with open(version_file, "r") as f:
line = f.readline().strip()
return line or None
except OSError:
Logger.print_error("Unable to read '.version'")
return None
def get_remote_client_version(client: BaseWebClient) -> str | None:
@@ -427,9 +445,9 @@ def get_client_port_selection(
while True:
_type = "Reconfigure" if reconfigure else "Configure"
question = f"{_type} {client.display_name} for port"
port_input = get_number_input(question, min_value=80, default=port)
port_input: int | None = get_number_input(question, min_value=80, default=port)
if port_input not in ports_in_use:
if port_input and port_input not in ports_in_use:
client_settings: WebUiSettings = settings[client.name]
client_settings.port = port_input
settings.save()
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -97,7 +97,7 @@ class ClientInstallMenu(BaseMenu):
self.message_service.set_message(message)
def _get_current_port(self) -> int:
curr_port = get_nginx_listen_port(self.client.nginx_config)
curr_port: int | None = get_nginx_listen_port(self.client.nginx_config)
if curr_port is None:
# if the port is not found in the config file we use
# the default port from the kiauh settings as fallback
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+2 -2
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -58,7 +58,7 @@ class BackupMenu(BaseMenu):
def print_menu(self) -> None:
line1 = Color.apply(
"INFO: Backups are located in '~/kiauh-backups'", Color.YELLOW
"INFO: Backups are located in '~/kiauh_backups'", Color.YELLOW
)
menu = textwrap.dedent(
f"""
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+3 -3
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -95,8 +95,8 @@ class MainMenu(BaseMenu):
status_data: ComponentStatus = status_fn(*args)
code: int = status_data.status
status: StatusText = StatusMap[code]
owner: str = trunc_string(status_data.owner, 23)
repo: str = trunc_string(status_data.repo, 23)
owner: str = trunc_string(status_data.owner, 23) if status_data.owner else '-'
repo: str = trunc_string(status_data.repo, 23) if status_data.repo else '-'
instance_count: int = status_data.instances
count_txt: str = ""
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+3 -3
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -100,11 +100,11 @@ class SettingsMenu(BaseMenu):
def trim_repo_url(repo: str) -> str:
return repo.replace(".git", "").replace("https://", "").replace("git@", "")
if not klipper_status.repo == "-":
if klipper_status.repo:
url = trim_repo_url(klipper_status.repo_url)
self.kl_repo_url = Color.apply(url, Color.CYAN)
self.kl_branch = Color.apply(klipper_status.branch, Color.CYAN)
if not moonraker_status.repo == "-":
if moonraker_status.repo:
url = trim_repo_url(moonraker_status.repo_url)
self.mr_repo_url = Color.apply(url, Color.CYAN)
self.mr_branch = Color.apply(moonraker_status.branch, Color.CYAN)
+40 -5
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -251,20 +251,23 @@ class UpdateMenu(BaseMenu):
self._set_status_data("klipperscreen", get_klipperscreen_status)
self._set_status_data("crowsnest", get_crowsnest_status)
self._fetch_system_package_update_status()
def _fetch_system_package_update_status(self) -> None:
update_system_package_lists(silent=True)
self.packages = get_upgradable_packages()
self.package_count = len(self.packages)
def _format_local_status(self, local_version, remote_version) -> str:
color = Color.RED
if not local_version:
if local_version is None:
color = Color.RED
elif local_version == remote_version:
color = Color.GREEN
elif local_version != remote_version:
color = Color.YELLOW
return Color.apply(local_version or "-", color)
return str(Color.apply(local_version or '-', color))
def _set_status_data(self, name: str, status_fn: Callable, *args) -> None:
comp_status: ComponentStatus = status_fn(*args)
@@ -290,7 +293,13 @@ class UpdateMenu(BaseMenu):
return self.status_data[name]["installed"]
def _is_update_available(self, name: str) -> bool:
return self.status_data[name]["local"] != self.status_data[name]["remote"]
local = self.status_data[name]["local"]
remote = self.status_data[name]["remote"]
if local is None or remote is None:
return False
return local != remote
def _run_update_routine(self, name: str, update_fn: Callable, *args) -> None:
display_name = self.status_data[name]["display_name"]
@@ -306,6 +315,27 @@ class UpdateMenu(BaseMenu):
update_fn(*args)
self._refresh_component_status(name)
def _refresh_component_status(self, name: str) -> None:
"""Refresh the status data for a component after an update."""
if name == "klipper":
self._set_status_data("klipper", get_klipper_status)
elif name == "moonraker":
self._set_status_data("moonraker", get_moonraker_status)
elif name == "mainsail":
self._set_status_data("mainsail", get_client_status, self.mainsail_data, True)
elif name == "mainsail_config":
self._set_status_data("mainsail_config", get_client_config_status, self.mainsail_data)
elif name == "fluidd":
self._set_status_data("fluidd", get_client_status, self.fluidd_data, True)
elif name == "fluidd_config":
self._set_status_data("fluidd_config", get_client_config_status, self.fluidd_data)
elif name == "klipperscreen":
self._set_status_data("klipperscreen", get_klipperscreen_status)
elif name == "crowsnest":
self._set_status_data("crowsnest", get_crowsnest_status)
def _run_system_updates(self) -> None:
if not self.packages:
Logger.print_info("No system upgrades available!")
@@ -313,15 +343,20 @@ class UpdateMenu(BaseMenu):
try:
pkgs: str = ", ".join(self.packages)
Logger.print_dialog(
DialogType.CUSTOM,
["The following packages will be upgraded:", "\n\n", pkgs],
custom_title="UPGRADABLE SYSTEM UPDATES",
)
if not get_confirm("Continue?"):
if not get_confirm("Upgrade packages?"):
return
Logger.print_status("Upgrading system packages ...")
upgrade_system_packages(self.packages)
self._fetch_system_package_update_status()
except Exception as e:
Logger.print_error(f"Error upgrading system packages:\n{e}")
raise
+46 -18
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -22,6 +22,7 @@ from utils.instance_utils import get_instances
class BackupService:
def __init__(self):
self._backup_root = Path.home().joinpath("kiauh_backups")
self._timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
@property
def backup_root(self) -> Path:
@@ -29,7 +30,7 @@ class BackupService:
@property
def timestamp(self) -> str:
return datetime.now().strftime("%Y%m%d-%H%M%S")
return self._timestamp
################################################
# GENERIC BACKUP METHODS
@@ -62,16 +63,21 @@ class BackupService:
target_name
or f"{source_path.stem}_{self.timestamp}{source_path.suffix}"
)
if target_path is not None:
backup_path = self._backup_root.joinpath(target_path, filename)
else:
backup_path = self._backup_root.joinpath(filename)
backup_path.mkdir(parents=True, exist_ok=True)
shutil.copy2(source_path, backup_path)
backup_dir = self._backup_root
if target_path is not None:
backup_dir = self._backup_root.joinpath(target_path)
backup_dir.mkdir(parents=True, exist_ok=True)
target_path = backup_dir.joinpath(filename)
if target_path.exists():
Logger.print_info(f"File '{target_path}' already exists. Skipping ...")
return True
shutil.copy2(source_path, target_path)
Logger.print_ok(
f"Successfully backed up '{source_path}' to '{backup_path}'"
f"Successfully backed up '{source_path}' to '{target_path}'"
)
return True
@@ -109,7 +115,27 @@ class BackupService:
else:
backup_path = self._backup_root.joinpath(backup_dir_name)
shutil.copytree(source_path, backup_path)
if backup_path.exists():
Logger.print_info(f"Reusing existing backup directory '{backup_path}'")
for item in source_path.rglob("*"):
relative_path = item.relative_to(source_path)
target_item = backup_path.joinpath(relative_path)
if item.is_file():
if not target_item.exists():
target_item.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target_item)
else:
Logger.print_info(f"File '{target_item}' already exists. Skipping...")
elif item.is_dir():
target_item.mkdir(parents=True, exist_ok=True)
else:
shutil.copytree(
source_path,
backup_path,
dirs_exist_ok=True,
symlinks=True,
ignore_dangling_symlinks=True,
)
Logger.print_ok(
f"Successfully backed up '{source_path}' to '{backup_path}'"
@@ -125,27 +151,29 @@ class BackupService:
################################################
def backup_printer_cfg(self):
"""Backup printer.cfg files of all Klipper instances.
Files are backed up to:
{backup_root}/{instance_data_dir_name}/printer_{timestamp}.cfg
"""
klipper_instances: List[Klipper] = get_instances(Klipper)
for instance in klipper_instances:
target_path: Path = self._backup_root.joinpath(
instance.data_dir.name, f"config_{self.timestamp}"
)
target_path: Path = self._backup_root.joinpath(instance.data_dir.name)
self.backup_file(
source_path=instance.cfg_file,
target_path=target_path,
target_name=instance.cfg_file.name,
)
def backup_moonraker_conf(self):
"""Backup moonraker.conf files of all Moonraker instances.
Files are backed up to:
{backup_root}/{instance_data_dir_name}/moonraker_{timestamp}.conf
"""
moonraker_instances: List[Moonraker] = get_instances(Moonraker)
for instance in moonraker_instances:
target_path: Path = self._backup_root.joinpath(
instance.data_dir.name, f"config_{self.timestamp}"
)
target_path: Path = self._backup_root.joinpath(instance.data_dir.name)
self.backup_file(
source_path=instance.cfg_file,
target_path=target_path,
target_name=instance.cfg_file.name,
)
def backup_printer_config_dir(self) -> None:
+1 -1
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
+16 -14
View File
@@ -1,5 +1,5 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com> #
# Copyright (C) 2020 - 2026 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
@@ -16,7 +16,7 @@ from components.klipper import KLIPPER_REPO_URL
from components.moonraker import MOONRAKER_REPO_URL
from core.logger import DialogType, Logger
from core.services.backup_service import BackupService
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
from core.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from utils.input_utils import get_confirm
@@ -254,32 +254,34 @@ class KiauhSettings:
section: str,
option: str,
getter: Callable[[str, str, T | None], T],
fallback: T = None,
fallback: T | None = None,
silent: bool = False,
) -> T:
) -> T | None:
if not self.__check_option_exists(section, option, fallback, silent):
return fallback
return getter(section, option, fallback)
def __set_repo_state(self, section: str, repos: List[str]) -> List[Repository]:
_repos: List[Repository] = []
for repo in repos:
try:
if repo.strip().startswith("#") or repo.strip().startswith(";"):
continue
if "," in repo:
url, branch = repo.strip().split(",")
for raw in repos:
line = raw.strip()
if not branch:
branch = "master"
if not line or line.startswith("#") or line.startswith(";"):
continue
try:
if "," in line:
url_part, branch_part = line.split(",")
url = url_part.strip()
branch = branch_part.strip() or "master"
else:
url = repo.strip()
url = line
branch = "master"
# url must not be empty otherwise it's considered
# as an unrecoverable, invalid configuration
if not url:
raise InvalidValueError(section, "repositories", repo)
raise InvalidValueError(section, "repositories", line)
_repos.append(Repository(url.strip(), branch.strip()))
@@ -12,7 +12,7 @@ import re
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Set, Union
from typing import Any, Callable, Dict, List, Set, Union
# definition of section line:
# - the line MUST start with an opening square bracket - it is the first section marker
@@ -91,6 +91,9 @@ class LineType(Enum):
BLANK = "blank"
_UNSET = object()
class NoSectionError(Exception):
"""Raised when a section is not defined"""
@@ -342,7 +345,7 @@ class SimpleConfigParser:
for line in file:
self._parse_line(line)
def write_file(self, path: Union[str, Path]) -> None:
def write_file(self, path: str | Path) -> None:
"""Write the config to a file"""
if path is None:
raise ValueError("File path cannot be None")
@@ -418,9 +421,7 @@ class SimpleConfigParser:
"""Check if an option exists in a section"""
return self.has_section(section) and option in self.get_options(section)
def set_option(
self, section: str, option: str, value: Union[str, List[str]]
) -> None:
def set_option(self, section: str, option: str, value: str | List[str]) -> None:
"""
Set the value of an option in a section. If the section does not exist,
it is created. If the option does not exist, it is created.
@@ -467,8 +468,8 @@ class SimpleConfigParser:
elif opt and isinstance(opt, Option) and isinstance(value, str):
curr_val = opt.value
new_val = value
opt.value = value
opt.raw.replace(curr_val, new_val)
opt.value = new_val
opt.raw = opt.raw.replace(curr_val, new_val)
elif opt and isinstance(opt, MultiLineOption) and isinstance(value, list):
# note: we completely replace the existing values
@@ -561,7 +562,7 @@ class SimpleConfigParser:
else self._find_option_by_name(option, section=sects[0])
)
def getval(self, section: str, option: str, fallback: Optional[str] = None) -> str:
def getval(self, section: str, option: str, fallback: str | _UNSET = _UNSET) -> str:
"""
Return the value of the given option in the given section
@@ -576,12 +577,12 @@ class SimpleConfigParser:
return opt.value if opt else ""
except (NoSectionError, NoOptionError):
if fallback is None:
if fallback is _UNSET:
raise
return fallback
def getvals(
self, section: str, option: str, fallback: Optional[List[str]] = None
self, section: str, option: str, fallback: List[str] | _UNSET = _UNSET
) -> List[str]:
"""
Return the values of the given multi-line option in the given section
@@ -597,22 +598,22 @@ class SimpleConfigParser:
return [v.value for v in opt.values] if opt else []
except (NoSectionError, NoOptionError):
if fallback is None:
if fallback is _UNSET:
raise
return fallback
def getint(self, section: str, option: str, fallback: Optional[int] = None) -> int:
def getint(self, section: str, option: str, fallback: int | _UNSET = _UNSET) -> int:
"""Return the value of the given option in the given section as an int"""
return self._get_conv(section, option, int, fallback=fallback)
def getfloat(
self, section: str, option: str, fallback: Optional[float] = None
self, section: str, option: str, fallback: float | _UNSET = _UNSET
) -> float:
"""Return the value of the given option in the given section as a float"""
return self._get_conv(section, option, float, fallback=fallback)
def getboolean(
self, section: str, option: str, fallback: Optional[bool] = None
self, section: str, option: str, fallback: bool | _UNSET = _UNSET
) -> bool:
"""Return the value of the given option in the given section as a boolean"""
return self._get_conv(
@@ -631,14 +632,14 @@ class SimpleConfigParser:
self,
section: str,
option: str,
conv: Callable[[str], Union[int, float, bool]],
fallback: Optional[Any] = None,
) -> Union[int, float, bool]:
conv: Callable[[str], int | float | bool],
fallback: Any = _UNSET,
) -> int | float | bool:
"""Return the value of the given option in the given section as a converted value"""
try:
return conv(self.getval(section, option, fallback))
except (ValueError, TypeError, AttributeError) as e:
if fallback is not None:
if fallback is not _UNSET:
return fallback
raise ValueError(
f"Cannot convert {self.getval(section, option)} to {conv.__name__}"
@@ -10,8 +10,8 @@ from pathlib import Path
import pytest
from src.simple_config_parser.simple_config_parser import SimpleConfigParser
from tests.utils import load_testdata_from_file
from core.simple_config_parser.simple_config_parser import SimpleConfigParser
from core.simple_config_parser.tests.utils import load_testdata_from_file
BASE_DIR = Path(__file__).parent.joinpath("test_data")
MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
@@ -10,8 +10,8 @@ from pathlib import Path
import pytest
from src.simple_config_parser.simple_config_parser import SimpleConfigParser
from tests.utils import load_testdata_from_file
from core.simple_config_parser.simple_config_parser import SimpleConfigParser
from core.simple_config_parser.tests.utils import load_testdata_from_file
BASE_DIR = Path(__file__).parent.joinpath("test_data")
MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
@@ -10,8 +10,8 @@ from pathlib import Path
import pytest
from src.simple_config_parser.simple_config_parser import SimpleConfigParser
from tests.utils import load_testdata_from_file
from core.simple_config_parser.simple_config_parser import SimpleConfigParser
from core.simple_config_parser.tests.utils import load_testdata_from_file
BASE_DIR = Path(__file__).parent.joinpath("test_data")
MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")

Some files were not shown because too many files have changed in this diff Show More