LibrePortal/scripts/docker/app/functions/function_install_app.sh
librelad d941f59388 feat(app): generic installApp driver + dispatcher fallback (Wave A)
The 31 containers/<app>/<app>.sh files each defined install<App>() with
the SAME 10-step sequence — ~4,000 lines of duplicated boilerplate.
Replaces all that with one generic driver + hook surface.

scripts/app/install/app_install.sh:
  installApp <slug> [config_variables]
    — Dispatches on $<slug> (c/u/s/r/i) the same way the per-app .sh
      files did. Same convention; dockerInstallApp's existing
      `declare $app=i` callsite needs no change.
    — Runs the standard sequence: dockerConfigSetupToContainer →
      dockerComposeSetupFile → optional .env copy → fixPermissions →
      dockerComposeUpdateAndStartApp → standard post-install steps
      (appUpdateSpecifics, setupHeadscale, databaseInstallApp,
      webuiContainerSetup, monitoring registration) → final message.
    — Hooks (all declare-f-gated, silent no-op when absent):
        <slug>_install_pre / _post_setup / _post_compose / _post_start
        <slug>_install_message_data   (echoes extra args for menu)
        <slug>_install_post
        <slug>_uninstall_pre / _post
        <slug>_stop_post
        <slug>_restart_post
      Hooks live in containers/<app>/tools/<app>_tools.sh (auto-sourced
      per the modular-per-app-tools convention).

function_install_app.sh:
  When no install<App>() function exists, fall through to
  `installApp <app_name>` instead of erroring. So an app with no .sh
  at all becomes a zero-byte addition — drop in <app>.config +
  docker-compose.yml + <app>.svg, done.

containers/linkding/linkding.sh:
  Deleted (canary). Linkding's body was 100% standard sequence;
  fallback handles it identically. Smoke-tested with stubbed helpers
  — dispatcher fires, generic runs full flow, monitoring integration
  + final-message hook plumbing all intact.

Wave B (next): delete the .sh for every other 'pure-boilerplate' app
(~15 candidates per the survey). Wave C: extract custom logic from
the 7 fat apps into hooks before deleting their .sh.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-27 01:43:08 +01:00

76 lines
3.2 KiB
Bash
Executable File

#!/bin/bash
dockerInstallApp()
{
local app_name="$1"
local config_variables="$2"
local reset_network="$3"
local app_name_ucfirst="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}"
local installFuncName="install${app_name_ucfirst}"
# App slugs are lowercase (e.g. "libreportal"), but installer functions keep
# their own internal capitalisation (e.g. installLibrePortal) — capitalising
# only the first letter can't reproduce that camelCase. If the naive name
# isn't a defined function, resolve it case-insensitively against the real
# function table so any compound brand/app name (LibrePortal, …) just works.
if ! declare -F "$installFuncName" >/dev/null 2>&1; then
local _resolved
_resolved="$(compgen -A function | grep -ixF "install${app_name}" | head -n1)"
[[ -n "$_resolved" ]] && installFuncName="$_resolved"
fi
if ! declare -F "$installFuncName" >/dev/null 2>&1; then
# No per-app install function (no containers/<app>/<app>.sh defining
# one). Fall through to the generic installApp driver — it does the
# standard 10-step sequence and picks up any <slug>_install_* hooks
# the app declares in tools/<slug>_tools.sh. This is the path taken
# by every app that doesn't need bespoke install code: their
# <app>.sh has been deleted and the dispatcher routes them here.
if declare -F installApp >/dev/null 2>&1; then
installApp "$app_name" "$config_variables"
return $?
fi
isError "No installer function found for app '${app_name}' (looked for ${installFuncName}; installApp fallback unavailable)."
return 1
fi
if [[ "$reset_network" == "true" ]]; then
export LIBREPORTAL_RESET_NETWORK=1
if declare -f ipRemoveFromDatabase >/dev/null 2>&1; then
ipRemoveFromDatabase "$app_name"
fi
if declare -f portsRemoveFromDatabase >/dev/null 2>&1; then
portsRemoveFromDatabase "$app_name"
fi
isNotice "Network reset: cleared previous IP/port allocations for $app_name."
fi
# Silently update the template config so apps.json regen reflects the saved
# values. The visible "Updated CFG_X" lines come from the deployed-config
# pass inside dockerConfigSetupToContainer.
if [[ -n "$config_variables" ]]; then
local template_config="$install_containers_dir$app_name/$app_name.config"
if [[ -f "$template_config" ]]; then
IFS='|' read -ra config_pairs <<< "$config_variables"
for pair in "${config_pairs[@]}"; do
if [[ "$pair" =~ ^(CFG_[A-Z0-9_]+)=(.*)$ ]]; then
local _value="${BASH_REMATCH[2]//%7C/|}"
updateConfigOption "${BASH_REMATCH[1]}" "$_value" "$template_config" >/dev/null
fi
done
fi
fi
# Create a variable with the name of $app_name and set its value to "i"
declare "${app_name}=i"
"${installFuncName}" "$config_variables"
if declare -f webuiGenerateLibrePortalConfig >/dev/null 2>&1; then
webuiGenerateLibrePortalConfig >/dev/null 2>&1 || true
fi
unset LIBREPORTAL_RESET_NETWORK
}