diff --git a/scripts/deploy-selfhost-image.sh b/scripts/deploy-selfhost-image.sh index 2bf7a7b3a3..e46c7fb9b7 100755 --- a/scripts/deploy-selfhost-image.sh +++ b/scripts/deploy-selfhost-image.sh @@ -61,31 +61,6 @@ validate_inputs() { fi } -wait_for_healthy() { - local deadline container_id status - - deadline=$((SECONDS + HEALTH_TIMEOUT_SECONDS)) - while [ "$SECONDS" -le "$deadline" ]; do - container_id="$(docker compose "${compose_args[@]}" ps -q "$SERVICE" 2>/dev/null || true)" - if [ -n "$container_id" ]; then - status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)" - if [ "$status" = "healthy" ]; then - echo "selfhost image deploy: $SERVICE is healthy" - return 0 - fi - fi - if [ "$SECONDS" -ge "$deadline" ]; then - break - fi - sleep 2 - done - - echo "error: $SERVICE did not become healthy within ${HEALTH_TIMEOUT_SECONDS}s" >&2 - docker compose "${compose_args[@]}" ps "$SERVICE" >&2 || true - docker compose "${compose_args[@]}" logs --tail=80 "$SERVICE" >&2 || true - exit 1 -} - require_cmd docker docker compose version >/dev/null @@ -126,7 +101,7 @@ docker compose "${compose_args[@]}" pull --policy always "$SERVICE" echo "selfhost image deploy: restarting $SERVICE" maybe_infisical_run docker compose "${compose_args[@]}" up -d --no-build --no-deps "$SERVICE" -wait_for_healthy +wait_for_healthy "$SERVICE" "$HEALTH_TIMEOUT_SECONDS" "selfhost image deploy" "${compose_args[@]}" env_put LOOPOVER_IMAGE "$IMAGE" echo "selfhost image deploy: complete ($IMAGE)" diff --git a/scripts/deploy-selfhost-prebuilt.sh b/scripts/deploy-selfhost-prebuilt.sh index 0080c1c20f..87a40f75a8 100755 --- a/scripts/deploy-selfhost-prebuilt.sh +++ b/scripts/deploy-selfhost-prebuilt.sh @@ -16,6 +16,9 @@ set -euo pipefail ENV_FILE="${SELFHOST_ENV_FILE:-.env}" NODE_IMAGE="${SELFHOST_NODE_IMAGE:-public.ecr.aws/docker/library/node:24-slim}" SERVICE="${SELFHOST_SERVICE:-loopover}" +# #8395: same override + default deploy-selfhost-image.sh already uses, so both deploy paths honour one +# health-check budget. +HEALTH_TIMEOUT_SECONDS="${SELFHOST_HEALTH_TIMEOUT_SECONDS:-180}" SKIP_SENTRY_UPLOAD="${SELFHOST_SKIP_SENTRY_UPLOAD:-0}" SENTRY_CLI_PACKAGE="${SENTRY_CLI_PACKAGE:-@sentry/cli@3.6.0}" @@ -118,6 +121,13 @@ YAML echo "selfhost deploy: restarting $SERVICE" maybe_infisical_run docker compose "${compose_args[@]}" up -d --no-deps "$SERVICE" + + # #8395: `up -d` only confirms the container was CREATED and STARTED -- without this, a crash-looping + # or never-healthy image still reported "selfhost deploy: complete". Called here (not at the top level) + # because compose_args is function-local, and it includes the generated override file. Exits non-zero + # with the same ps/logs diagnostics deploy-selfhost-image.sh produces; the EXIT trap above still cleans + # up the temp override file on that path. + wait_for_healthy "$SERVICE" "$HEALTH_TIMEOUT_SECONDS" "selfhost deploy" "${compose_args[@]}" } require_cmd docker diff --git a/scripts/lib/selfhost-deploy-common.sh b/scripts/lib/selfhost-deploy-common.sh index a9d30b2542..ed2e457117 100644 --- a/scripts/lib/selfhost-deploy-common.sh +++ b/scripts/lib/selfhost-deploy-common.sh @@ -134,3 +134,38 @@ compose_file_args() { printf '%s\n' -f "$file" done } + +# Block until $service reports healthy, or fail the deploy (#8395). Moved here from +# deploy-selfhost-image.sh so BOTH deploy scripts share one implementation instead of one hand-copying +# the other -- deploy-selfhost-prebuilt.sh previously printed "complete" straight after +# `docker compose up -d`, which only confirms the container STARTED, so a crash-looping image reported +# success. Parameterized rather than reading caller globals: prebuilt's compose_args is a function-local +# array. $log_prefix keeps each script's existing message wording byte-identical. +# Usage: wait_for_healthy +wait_for_healthy() { + local service="$1" timeout_seconds="$2" log_prefix="$3" + shift 3 + local -a compose_args=("$@") + local deadline container_id status + + deadline=$((SECONDS + timeout_seconds)) + while [ "$SECONDS" -le "$deadline" ]; do + container_id="$(docker compose "${compose_args[@]}" ps -q "$service" 2>/dev/null || true)" + if [ -n "$container_id" ]; then + status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)" + if [ "$status" = "healthy" ]; then + echo "$log_prefix: $service is healthy" + return 0 + fi + fi + if [ "$SECONDS" -ge "$deadline" ]; then + break + fi + sleep 2 + done + + echo "error: $service did not become healthy within ${timeout_seconds}s" >&2 + docker compose "${compose_args[@]}" ps "$service" >&2 || true + docker compose "${compose_args[@]}" logs --tail=80 "$service" >&2 || true + exit 1 +}