From 3be119af135e05d397811c6cdc6de000da12dab3 Mon Sep 17 00:00:00 2001 From: librelad Date: Tue, 26 May 2026 01:29:37 +0100 Subject: [PATCH] refactor(checks): data-driven app requirements (collapse per-service case arms) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 5 service arms in appInstallCheckRequirements (traefik/gluetun/authelia/ headscale/prometheus) were identical _appReqServiceInstalled calls. Collapse them into one generic default: any requirement naming a real container is a service prerequisite — so a new service requirement now needs NO code here, just list it in the app's CFG__REQUIRES. domain + mail stay as their own special types; a requirement that isn't a known app is still treated as a typo and ignored (safety net preserved). Flavor messages kept via a small optional reason map (_appReqServiceMsg); unknown-to-the-map services get a clean generic message. Stays central (it's the requirements engine, not per-app logic) but is now extensible without edits. Verified with stubs: met→rc0, absent service→flavor or generic msg, brand-new container service→generic (zero code), typo→ignored. Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- .../checks/requirements/check_app_install.sh | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/scripts/checks/requirements/check_app_install.sh b/scripts/checks/requirements/check_app_install.sh index fbd0232..c514618 100644 --- a/scripts/checks/requirements/check_app_install.sh +++ b/scripts/checks/requirements/check_app_install.sh @@ -4,8 +4,12 @@ # # Each app declares its prerequisites in its .config as # CFG__REQUIRES="" -# where is a comma-separated list of requirement keys (see the -# `case` arms below for the full set). Examples: +# where is a comma-separated list of requirement keys: the special types +# domain — at least one CFG_DOMAIN_N is set +# mail — global mail is enabled +# or the name of any other app/service that must be installed first. The service +# case is data-driven — any real container name works with NO code change here, +# just list it. Examples: # CFG_AUTHELIA_REQUIRES="domain,traefik" # CFG_BOOKSTACK_REQUIRES="domain,traefik" # @@ -40,38 +44,23 @@ appInstallCheckRequirements() missing+=("Set at least one CFG_DOMAIN_N (General → Network) before installing $app_name.") fi ;; - "traefik") - if ! _appReqServiceInstalled "traefik"; then - missing+=("Install Traefik first — $app_name needs a reverse proxy to publish itself.") - fi - ;; - "gluetun") - if ! _appReqServiceInstalled "gluetun"; then - missing+=("Install Gluetun first — $app_name expects a VPN gateway to route through.") - fi - ;; - "authelia") - if ! _appReqServiceInstalled "authelia"; then - missing+=("Install Authelia first — $app_name's auth integration depends on it.") - fi - ;; - "headscale") - if ! _appReqServiceInstalled "headscale"; then - missing+=("Install Headscale first.") - fi - ;; - "prometheus") - if ! _appReqServiceInstalled "prometheus"; then - missing+=("Install Prometheus first — $app_name has nothing to query without it.") - fi - ;; "mail") if [[ "$CFG_MAIL_ENABLED" != "true" ]]; then missing+=("Configure global mail (CFG_MAIL_ENABLED=true under General → Mail) first.") fi ;; *) - isNotice "Unknown requirement '$req' declared by $app_name — ignoring." + # Any other requirement names a service/app that must be installed. + # Data-driven: if it's a real container it's a service prerequisite — + # adding a new one needs NO code here, just list it in the app's + # CFG__REQUIRES. A name that's not a known app is a typo → ignore. + if [[ -d "${install_containers_dir}${req}" ]]; then + if ! _appReqServiceInstalled "$req"; then + missing+=("$(_appReqServiceMsg "$req" "$app_name")") + fi + else + isNotice "Unknown requirement '$req' declared by $app_name — ignoring." + fi ;; esac done @@ -88,6 +77,26 @@ appInstallCheckRequirements() return 0 } +# Human-friendly "install X first" line for a service prerequisite. Known services +# get a reason; anything else falls back to a generic message — so a NEW service +# requirement works with no code change (add a reason here only if you want flavor). +_appReqServiceMsg() +{ + local svc="$1" app="$2" reason="" + case "$svc" in + traefik) reason="a reverse proxy to publish itself" ;; + gluetun) reason="a VPN gateway to route through" ;; + authelia) reason="its auth integration" ;; + prometheus) reason="something to query" ;; + esac + local disp="$(tr '[:lower:]' '[:upper:]' <<< ${svc:0:1})${svc:1}" + if [[ -n "$reason" ]]; then + echo "Install $disp first — $app needs $reason." + else + echo "Install $disp first — required by $app." + fi +} + # True if any CFG_DOMAIN_ is set to a non-empty value. _appReqHasDomain() {