Compare commits

...

7 Commits

Author SHA1 Message Date
dw-0
3450cde9c8 Merge 51993e367d into a374ac8fac 2024-05-11 23:08:26 +02:00
dw-0
51993e367d chore: cleanup settings_menu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 23:07:54 +02:00
dw-0
a03e943ebf chore: check import sorting when linting
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:52:25 +02:00
dw-0
fc8fedc9f6 refactor(Klipper): change subprocess imports
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:49:26 +02:00
dw-0
7f79f68209 refactor(Klipper): use warn dialog
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:29:53 +02:00
dw-0
a44508ead5 refactor: update dependency management
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-05-11 22:23:51 +02:00
Justin Otherguy
a374ac8fac fix: add unzip to dependencies for Mainsail and Fluidd 2024-05-09 20:56:16 +02:00
10 changed files with 162 additions and 126 deletions

View File

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

View File

@@ -11,22 +11,21 @@ import grp
import os import os
import re import re
import shutil import shutil
import subprocess from subprocess import CalledProcessError, run
import textwrap from typing import Dict, List, Literal, Optional, Union
from typing import List, Union, Literal, Dict, Optional
from components.klipper import ( from components.klipper import (
MODULE_PATH, KLIPPER_BACKUP_DIR,
KLIPPER_DIR, KLIPPER_DIR,
KLIPPER_ENV_DIR, KLIPPER_ENV_DIR,
KLIPPER_BACKUP_DIR, MODULE_PATH,
) )
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import ( from components.klipper.klipper_dialogs import (
print_missing_usergroup_dialog,
print_instance_overview, print_instance_overview,
print_select_instance_count_dialog, print_missing_usergroup_dialog,
print_select_custom_name_dialog, print_select_custom_name_dialog,
print_select_instance_count_dialog,
) )
from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker import Moonraker
from components.moonraker.moonraker_utils import moonraker_to_multi_conversion from components.moonraker.moonraker_utils import moonraker_to_multi_conversion
@@ -42,9 +41,9 @@ from core.instance_manager.name_scheme import NameScheme
from utils import PRINTER_CFG_BACKUP_DIR from utils import PRINTER_CFG_BACKUP_DIR
from utils.common import get_install_status_common from utils.common import get_install_status_common
from utils.constants import CURRENT_USER from utils.constants import CURRENT_USER
from utils.git_utils import get_repo_name, get_remote_commit, get_local_commit from utils.git_utils import get_local_commit, get_remote_commit, get_repo_name
from utils.input_utils import get_confirm, get_string_input, get_number_input from utils.input_utils import get_confirm, get_number_input, get_string_input
from utils.logger import Logger from utils.logger import DialogType, Logger
from utils.sys_utils import cmd_sysctl_service from utils.sys_utils import cmd_sysctl_service
@@ -98,8 +97,9 @@ def update_name_scheme(
klipper_instances: List[Klipper], klipper_instances: List[Klipper],
moonraker_instances: List[Moonraker], moonraker_instances: List[Moonraker],
) -> NameScheme: ) -> NameScheme:
# if there are more moonraker instances installed than klipper, we # if there are more moonraker instances installed
# load their names into the name_dict, as we will detect and enforce that naming scheme # than klipper, we load their names into the name_dict,
# as we will detect and enforce that naming scheme
if len(moonraker_instances) > len(klipper_instances): if len(moonraker_instances) > len(klipper_instances):
update_name_dict(name_dict, moonraker_instances) update_name_dict(name_dict, moonraker_instances)
return detect_name_scheme(moonraker_instances) return detect_name_scheme(moonraker_instances)
@@ -141,7 +141,11 @@ def get_install_count() -> Union[int, None]:
""" """
kl_instances = InstanceManager(Klipper).instances kl_instances = InstanceManager(Klipper).instances
print_select_instance_count_dialog() print_select_instance_count_dialog()
question = f"Number of{' additional' if len(kl_instances) > 0 else ''} Klipper instances to set up" question = (
f"Number of"
f"{' additional' if len(kl_instances) > 0 else ''} "
f"Klipper instances to set up"
)
return get_number_input(question, 1, default=1, allow_go_back=True) return get_number_input(question, 1, default=1, allow_go_back=True)
@@ -193,7 +197,8 @@ def klipper_to_multi_conversion(new_name: str) -> None:
else: else:
Logger.print_info(f"Existing '{new_instance.data_dir}' found ...") Logger.print_info(f"Existing '{new_instance.data_dir}' found ...")
# patch the virtual_sdcard sections path value to match the new printer_data foldername # patch the virtual_sdcard sections path
# value to match the new printer_data foldername
cm = ConfigManager(new_instance.cfg_file) cm = ConfigManager(new_instance.cfg_file)
if cm.config.has_section("virtual_sdcard"): if cm.config.has_section("virtual_sdcard"):
cm.set_value("virtual_sdcard", "path", str(new_instance.gcodes_dir)) cm.set_value("virtual_sdcard", "path", str(new_instance.gcodes_dir))
@@ -227,9 +232,9 @@ def check_user_groups():
for group in missing_groups: for group in missing_groups:
Logger.print_status(f"Adding user '{CURRENT_USER}' to group {group} ...") Logger.print_status(f"Adding user '{CURRENT_USER}' to group {group} ...")
command = ["sudo", "usermod", "-a", "-G", group, CURRENT_USER] command = ["sudo", "usermod", "-a", "-G", group, CURRENT_USER]
subprocess.run(command, check=True) run(command, check=True)
Logger.print_ok(f"Group {group} assigned to user '{CURRENT_USER}'.") Logger.print_ok(f"Group {group} assigned to user '{CURRENT_USER}'.")
except subprocess.CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"Unable to add user to usergroups: {e}") Logger.print_error(f"Unable to add user to usergroups: {e}")
raise raise
@@ -241,13 +246,13 @@ def handle_disruptive_system_packages() -> None:
services = [] services = []
command = ["systemctl", "is-enabled", "brltty"] command = ["systemctl", "is-enabled", "brltty"]
brltty_status = subprocess.run(command, capture_output=True, text=True) brltty_status = run(command, capture_output=True, text=True)
command = ["systemctl", "is-enabled", "brltty-udev"] command = ["systemctl", "is-enabled", "brltty-udev"]
brltty_udev_status = subprocess.run(command, capture_output=True, text=True) brltty_udev_status = run(command, capture_output=True, text=True)
command = ["systemctl", "is-enabled", "ModemManager"] command = ["systemctl", "is-enabled", "ModemManager"]
modem_manager_status = subprocess.run(command, capture_output=True, text=True) modem_manager_status = run(command, capture_output=True, text=True)
if "enabled" in brltty_status.stdout: if "enabled" in brltty_status.stdout:
services.append("brltty") services.append("brltty")
@@ -259,15 +264,16 @@ def handle_disruptive_system_packages() -> None:
for service in services if services else []: for service in services if services else []:
try: try:
cmd_sysctl_service(service, "mask") cmd_sysctl_service(service, "mask")
except subprocess.CalledProcessError: except CalledProcessError:
warn_msg = textwrap.dedent( Logger.print_dialog(
f""" DialogType.WARNING,
KIAUH was unable to mask the {service} system service. [
Please fix the problem manually. Otherwise, this may have f"KIAUH was unable to mask the {service} system service. "
undesirable effects on the operation of Klipper. "Please fix the problem manually. Otherwise, this may have "
""" "undesirable effects on the operation of Klipper."
)[1:] ],
Logger.print_warn(warn_msg) end="",
)
def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme: def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme:
@@ -280,8 +286,7 @@ def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme:
def get_highest_index(instance_list: List[Klipper]) -> int: def get_highest_index(instance_list: List[Klipper]) -> int:
indices = [int(instance.suffix.split("-")[-1]) for instance in instance_list] return max([int(instance.suffix.split("-")[-1]) for instance in instance_list])
return max(indices)
def create_example_printer_cfg( def create_example_printer_cfg(

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
import shutil import shutil
import textwrap import textwrap
from pathlib import Path from pathlib import Path
from typing import Type, Optional, Tuple from typing import Optional, Tuple, Type
from components.klipper import KLIPPER_DIR from components.klipper import KLIPPER_DIR
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
@@ -19,10 +19,10 @@ from core.instance_manager.instance_manager import InstanceManager
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_GREEN, COLOR_YELLOW from utils.constants import COLOR_CYAN, COLOR_GREEN, RESET_FORMAT
from utils.git_utils import git_clone_wrapper from utils.git_utils import git_clone_wrapper
from utils.input_utils import get_string_input, get_confirm from utils.input_utils import get_confirm, get_string_input
from utils.logger import Logger from utils.logger import DialogType, Logger
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@@ -88,7 +88,7 @@ class SettingsMenu(BaseMenu):
| 3) Toggle unstable Mainsail releases | | 3) Toggle unstable Mainsail releases |
| 4) Toggle unstable Fluidd releases | | 4) Toggle unstable Fluidd releases |
| | | |
| 5) Toggle automatic backups before updates | | 5) Toggle automatic backups before updates |
""" """
)[1:] )[1:]
print(menu, end="") print(menu, end="")
@@ -100,12 +100,17 @@ class SettingsMenu(BaseMenu):
self._format_repo_str("moonraker") self._format_repo_str("moonraker")
self.auto_backups_enabled = self.kiauh_settings.get( self.auto_backups_enabled = self.kiauh_settings.get(
"kiauh", "backup_before_update" "kiauh",
"backup_before_update",
) )
self.mainsail_unstable = self.kiauh_settings.get( self.mainsail_unstable = self.kiauh_settings.get(
"mainsail", "unstable_releases" "mainsail",
"unstable_releases",
)
self.fluidd_unstable = self.kiauh_settings.get(
"fluidd",
"unstable_releases",
) )
self.fluidd_unstable = self.kiauh_settings.get("fluidd", "unstable_releases")
def _format_repo_str(self, repo_name: str) -> None: def _format_repo_str(self, repo_name: str) -> None:
repo = self.kiauh_settings.get(repo_name, "repo_url") repo = self.kiauh_settings.get(repo_name, "repo_url")
@@ -115,20 +120,16 @@ class SettingsMenu(BaseMenu):
setattr(self, f"{repo_name}_repo", f"{COLOR_CYAN}{repo}{RESET_FORMAT} {branch}") setattr(self, f"{repo_name}_repo", f"{COLOR_CYAN}{repo}{RESET_FORMAT} {branch}")
def _gather_input(self) -> Tuple[str, str]: def _gather_input(self) -> Tuple[str, str]:
l2 = "Make sure your input is correct!" Logger.print_dialog(
error = textwrap.dedent( DialogType.ATTENTION,
f"""\n [
{COLOR_YELLOW}/=======================================================\\ "There is no input validation in place! Make sure your"
| ATTENTION: | " input is valid and has no typos! For any change to"
| There is no input validation in place! Make sure your | " take effect, the repository must be cloned again. "
| input is valid and has no typos! For any change to | "Make sure you don't have any ongoing prints running, "
| take effect, the repository must be cloned again. | "as the services will be restarted!"
| Make sure you don't have any ongoing prints running, | ],
| as the services will be restarted! | )
\=======================================================/{RESET_FORMAT}
"""
)[1:]
print(error, end="\n")
repo = get_string_input( repo = get_string_input(
"Enter new repository URL", "Enter new repository URL",
allow_special_chars=True, allow_special_chars=True,
@@ -140,44 +141,35 @@ class SettingsMenu(BaseMenu):
return repo, branch return repo, branch
def _display_summary(self, name: str, repo: str, branch: str):
l1 = f"New {name} repository URL:"
l2 = f"{repo}"
l3 = f"New {name} repository branch:"
l4 = f"{branch}"
summary = textwrap.dedent(
f"""\n
/=======================================================\\
| {l1:<52} |
| {l2:<52} |
| {l3:<52} |
| {l4:<52} |
\=======================================================/
"""
)[1:]
print(summary, end="")
def _set_repo(self, repo_name: str): def _set_repo(self, repo_name: str):
repo, branch = self._gather_input() repo_url, branch = self._gather_input()
self._display_summary(repo_name.capitalize(), repo, branch) display_name = repo_name.capitalize()
Logger.print_dialog(
DialogType.CUSTOM,
[
f"New {display_name} repository URL:",
f"{repo_url}",
f"New {display_name} repository branch:",
f"{branch}",
],
end="",
)
if get_confirm("Apply changes?", allow_go_back=True): if get_confirm("Apply changes?", allow_go_back=True):
self.kiauh_settings.set(repo_name, "repo_url", repo) self.kiauh_settings.set(repo_name, "repo_url", repo_url)
self.kiauh_settings.set(repo_name, "branch", branch) self.kiauh_settings.set(repo_name, "branch", branch)
self.kiauh_settings.save() self.kiauh_settings.save()
self._load_settings() self._load_settings()
Logger.print_ok("Changes saved!") Logger.print_ok("Changes saved!")
else: else:
Logger.print_info( Logger.print_info(
f"Skipping change of {repo_name.capitalize()} source repository ..." f"Skipping change of {display_name} source repository ..."
) )
return return
Logger.print_status( Logger.print_status(f"Switching to {display_name}'s new source repository ...")
f"Switching to {repo_name.capitalize()}'s new source repository ..."
)
self._switch_repo(repo_name) self._switch_repo(repo_name)
Logger.print_ok(f"Switched to {repo} at branch {branch}!") Logger.print_ok(f"Switched to {repo_url} at branch {branch}!")
def _switch_repo(self, name: str) -> None: def _switch_repo(self, name: str) -> None:
target_dir: Path target_dir: Path
@@ -211,17 +203,27 @@ class SettingsMenu(BaseMenu):
def toggle_mainsail_release(self, **kwargs): def toggle_mainsail_release(self, **kwargs):
self.mainsail_unstable = not self.mainsail_unstable self.mainsail_unstable = not self.mainsail_unstable
self.kiauh_settings.set("mainsail", "unstable_releases", self.mainsail_unstable) self.kiauh_settings.set(
"mainsail",
"unstable_releases",
self.mainsail_unstable,
)
self.kiauh_settings.save() self.kiauh_settings.save()
def toggle_fluidd_release(self, **kwargs): def toggle_fluidd_release(self, **kwargs):
self.fluidd_unstable = not self.fluidd_unstable self.fluidd_unstable = not self.fluidd_unstable
self.kiauh_settings.set("fluidd", "unstable_releases", self.fluidd_unstable) self.kiauh_settings.set(
"fluidd",
"unstable_releases",
self.fluidd_unstable,
)
self.kiauh_settings.save() self.kiauh_settings.save()
def toggle_backup_before_update(self, **kwargs): def toggle_backup_before_update(self, **kwargs):
self.auto_backups_enabled = not self.auto_backups_enabled self.auto_backups_enabled = not self.auto_backups_enabled
self.kiauh_settings.set( self.kiauh_settings.set(
"kiauh", "backup_before_update", self.auto_backups_enabled "kiauh",
"backup_before_update",
self.auto_backups_enabled,
) )
self.kiauh_settings.save() self.kiauh_settings.save()

View File

@@ -17,3 +17,5 @@ indent-style = "space"
line-ending = "lf" line-ending = "lf"
quote-style = "double" quote-style = "double"
[tool.ruff.lint]
extend-select = ["I"]

View File

@@ -37,7 +37,7 @@ function install_fluidd() {
fi fi
### checking dependencies ### checking dependencies
local dep=(wget nginx) local dep=(wget nginx unzip)
dependency_check "${dep[@]}" dependency_check "${dep[@]}"
### detect conflicting Haproxy and Apache2 installations ### detect conflicting Haproxy and Apache2 installations
detect_conflicting_packages detect_conflicting_packages

View File

@@ -37,7 +37,7 @@ function install_mainsail() {
fi fi
### checking dependencies ### checking dependencies
local dep=(wget nginx) local dep=(wget nginx unzip)
dependency_check "${dep[@]}" dependency_check "${dep[@]}"
### detect conflicting Haproxy and Apache2 installations ### detect conflicting Haproxy and Apache2 installations
detect_conflicting_packages detect_conflicting_packages