From c92fcc9773c04e2ea9297f42ed4882ded8ef1b66 Mon Sep 17 00:00:00 2001 From: librelad Date: Mon, 6 Jul 2026 20:43:56 +0100 Subject: [PATCH] fix(install): don't record an app as installed when its container never started MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/app/install/app_install.sh | 15 +++++++++++++++ scripts/docker/app/compose/up_app.sh | 13 +++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/app/install/app_install.sh b/scripts/app/install/app_install.sh index e15c182..e2a5d9f 100644 --- a/scripts/app/install/app_install.sh +++ b/scripts/app/install/app_install.sh @@ -160,6 +160,21 @@ installApp() dockerComposeUpdateAndStartApp "$app_name" install _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++)) echo "" echo "---- $menu_number. Running post-install integrations." diff --git a/scripts/docker/app/compose/up_app.sh b/scripts/docker/app/compose/up_app.sh index e222b09..6436abd 100755 --- a/scripts/docker/app/compose/up_app.sh +++ b/scripts/docker/app/compose/up_app.sh @@ -122,12 +122,17 @@ dockerComposeUp() fi if [[ $CFG_DOCKER_INSTALL_TYPE == "rootless" ]]; then 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=$? - checkSuccess "Started container for $app_name" + local result; result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d") + _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 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=$? - checkSuccess "Started container for $app_name" + local result; result=$(cd "$containers_dir$app_name" && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d) + _rc=$? + ( exit "$_rc" ); checkSuccess "Started container for $app_name" fi # Used for the CLI dockertype switcher. else