Files
2026-04-29 10:41:22 -07:00

61 lines
1.9 KiB
Bash

#!/usr/bin/env bash
# wait-healthy.sh — poll docker compose services until all are healthy/running.
# Invoked by the compose-wait-healthy composite action. Configure via env vars:
# WAIT_HEALTHY_SERVICES whitespace-separated list of service names (required)
# WAIT_HEALTHY_DEADLINE seconds before the script fails (default: 600)
# WAIT_HEALTHY_POLL seconds between polls (default: 5)
# COMPOSE_PROJECT compose project name passed via -p (optional)
set -euo pipefail
compose() {
if [[ -n "${COMPOSE_PROJECT:-}" ]]; then
docker compose -p "${COMPOSE_PROJECT}" "$@"
else
docker compose "$@"
fi
}
read -r -a services <<< "${WAIT_HEALTHY_SERVICES}"
if [[ ${#services[@]} -eq 0 ]]; then
echo "ERROR: WAIT_HEALTHY_SERVICES is empty" >&2
exit 64
fi
deadline="${WAIT_HEALTHY_DEADLINE:-600}"
poll="${WAIT_HEALTHY_POLL:-5}"
start="$(date +%s)"
while :; do
pending=()
for svc in "${services[@]}"; do
cid="$(compose ps -q "${svc}" || true)"
if [[ -z "${cid}" ]]; then
pending+=("${svc}(no container)")
continue
fi
status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "${cid}" 2>/dev/null || echo unknown)"
case "${status}" in
healthy|running) ;;
*) pending+=("${svc}=${status}") ;;
esac
done
if [[ ${#pending[@]} -eq 0 ]]; then
echo "all services healthy: ${services[*]}"
exit 0
fi
elapsed=$(( $(date +%s) - start ))
if (( elapsed > deadline )); then
echo "FAIL: timeout after ${elapsed}s waiting on: ${pending[*]}" >&2
for svc in "${services[@]}"; do
echo "----- ${svc} (last 50 log lines) -----" >&2
compose logs --no-color --tail=50 "${svc}" >&2 || true
done
exit 1
fi
printf '[%4ds] waiting on: %s\n' "${elapsed}" "${pending[*]}"
sleep "${poll}"
done