Added compose-wait-healthy.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-29 10:41:22 -07:00
parent 38a77422b9
commit 0c26e7a41c
2 changed files with 94 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
name: compose-wait-healthy
description: "Wait until every named docker-compose service reports 'healthy' (or 'running' when no HEALTHCHECK is defined). On timeout, dumps tail logs for diagnosis."
inputs:
services:
description: "Whitespace-separated list of compose service names to wait on."
required: true
deadline:
description: "Per-invocation deadline in seconds before the action fails."
required: false
default: "600"
poll:
description: "Seconds between polls."
required: false
default: "5"
project:
description: "Compose project name (passed as -p). Optional."
required: false
default: ""
workingDirectory:
description: "Directory containing the compose.yml/compose.yaml. Defaults to repo root."
required: false
default: "."
runs:
using: "composite"
steps:
- name: "Wait for healthy"
shell: bash
working-directory: ${{ inputs.workingDirectory }}
env:
WAIT_HEALTHY_SERVICES: ${{ inputs.services }}
WAIT_HEALTHY_DEADLINE: ${{ inputs.deadline }}
WAIT_HEALTHY_POLL: ${{ inputs.poll }}
COMPOSE_PROJECT: ${{ inputs.project }}
run: bash "$GITHUB_ACTION_PATH/wait-healthy.sh"
@@ -0,0 +1,60 @@
#!/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