fix(install): don't record an app as installed when its container never started

An image-pull failure (e.g. the MTU-EOF black hole) left apps marked
installed+active with no container: the compose `up -d` failure was invisible
because (a) checkSuccess read $? after `_rc=$?`, always printing "✓ Started",
and (b) the exit code is dropped across dockerComposeUpdateAndStartApp →
dockerComposeUpdate → dockerComposeRestartAfterUpdate, and installApp never
checked it — so post-install integrations ran and set status=1 regardless.

- up_app.sh: restore $? to the compose exit before checkSuccess (both rootless
  and rooted) so a failed `up -d` is reported as an error + logged, not "✓".
- app_install.sh: after `up`, reality-gate on the app's compose project having
  at least one container (ps -a, so a slow-to-start container still counts).
  If none exists, print a clear failure, skip _appPostStartIntegrations (which
  is what records the app + sets status=1), and return non-zero.

Verified: the gate query passes for a running app (libreportal) and refuses
apps with no container (navidrome/ipinfo after their pulls EOF'd).

Signed-off-by: librelad <librelad@digitalangels.vip>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
librelad 2026-07-06 20:43:56 +01:00
parent ffc7801762
commit c92fcc9773
2 changed files with 24 additions and 4 deletions

View File

@ -160,6 +160,21 @@ installApp()
dockerComposeUpdateAndStartApp "$app_name" install dockerComposeUpdateAndStartApp "$app_name" install
_appCallHook "${app_slug}_install_post_start" "$app_name" _appCallHook "${app_slug}_install_post_start" "$app_name"
# Reality gate: after `up`, the app MUST have at least one container
# (its compose project == the app dir name). An image-pull failure
# creates none, yet the deep compose call chain swallows that error —
# without this the app is recorded as installed+active with nothing
# running (the silent-fail seen when a low path-MTU black-holed pulls).
# ps -a (not just running) so a created-but-slow-to-start container
# still counts; only a total absence is treated as failure.
if declare -F dockerCommandRun >/dev/null 2>&1 \
&& ! dockerCommandRun "docker ps -a --filter label=com.docker.compose.project=$app_name --format '{{.Names}}' 2>/dev/null" 2>/dev/null | grep -q '[^[:space:]]'; then
isError "$app_name did not start — no container was created (usually the image failed to pull). NOT recording it as installed."
isNotice "Fix the cause (often image-registry connectivity / MTU), then re-run: libreportal app install $app_name"
eval "$app_slug=n"
return 1
fi
((menu_number++)) ((menu_number++))
echo "" echo ""
echo "---- $menu_number. Running post-install integrations." echo "---- $menu_number. Running post-install integrations."

View File

@ -122,12 +122,17 @@ dockerComposeUp()
fi fi
if [[ $CFG_DOCKER_INSTALL_TYPE == "rootless" ]]; then if [[ $CFG_DOCKER_INSTALL_TYPE == "rootless" ]]; then
isNotice "Starting container for $app_name, this may take a while..." isNotice "Starting container for $app_name, this may take a while..."
local result; result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d"); _rc=$? local result; result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d")
checkSuccess "Started container for $app_name" _rc=$?
# Restore $? to the compose exit code — a bare `checkSuccess`
# after `_rc=$?` would read the assignment's 0 and always print
# success, hiding a failed `up -d` (e.g. an image pull EOF).
( exit "$_rc" ); checkSuccess "Started container for $app_name"
elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then
isNotice "Starting container for $app_name, this may take a while..." isNotice "Starting container for $app_name, this may take a while..."
local result; result=$(cd "$containers_dir$app_name" && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d); _rc=$? local result; result=$(cd "$containers_dir$app_name" && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d)
checkSuccess "Started container for $app_name" _rc=$?
( exit "$_rc" ); checkSuccess "Started container for $app_name"
fi fi
# Used for the CLI dockertype switcher. # Used for the CLI dockertype switcher.
else else