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>
76 lines
3.2 KiB
Bash
Executable File
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
|
|
}
|