Files
flights_web/scripts/ci/deploy-container.sh
T
gnezim e80eeb69e0
ci-deploy / build-deploy-test (push) Successful in 1m11s
deploy-container: wait for SSR readiness before returning from swap
Run 549's wait-for-health logged two HTTP 502s before its third
attempt succeeded — nginx → docker forwarding hit the new container
during the ~4s window between \`docker run -d\` returning and
Node.js inside finishing its boot. The retry loop covered it but the
log was noisy and a slower boot could blow past the 30×2s budget.

Added a post-run readiness probe inside swap: poll
http://127.0.0.1:${PORT}/ on the host (docker container is published
to 127.0.0.1, runner uses host network mode) until it answers 2xx,
up to 30 attempts × 1s. Skipped under --dry-run so the tests/ci/
shell tests still pass without touching the network.

Net effect: wait-for-url against the public URL now succeeds first
attempt, and the run aborts cleanly if the SSR doesn't come up at
all instead of looking healthy because nginx happens to keep a
warmed connection.
2026-04-28 14:44:12 +03:00

98 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# deploy-container.sh — swap or rollback the flights-web container on the host.
#
# Usage: deploy-container.sh [--dry-run] <swap|rollback>
#
# `swap` — assumes the new image is tagged flights-web:${GITHUB_SHA}.
# Tags :current → :previous, :sha → :current, restarts container.
# `rollback` — runs flights-web:previous in place of :current, repoints :current.
#
# Env:
# GITHUB_SHA (required for swap)
# FLIGHTS_WEB_PORT (default 3002 — host port that nginx proxies to)
# IMAGE_NAME (default flights-web — set this to point at a registry later)
set -euo pipefail
DRY_RUN=0
if [ "${1:-}" = "--dry-run" ]; then
DRY_RUN=1
shift
fi
CMD="${1:-}"
PORT="${FLIGHTS_WEB_PORT:-3002}"
IMAGE="${IMAGE_NAME:-flights-web}"
run() {
if [ "$DRY_RUN" -eq 1 ]; then
printf 'docker %s\n' "$*"
else
docker "$@"
fi
}
run_or_skip() {
# Same as run, but doesn't fail in real mode if the docker call fails.
if [ "$DRY_RUN" -eq 1 ]; then
printf 'docker %s\n' "$*"
else
docker "$@" || true
fi
}
case "$CMD" in
swap)
: "${GITHUB_SHA:?GITHUB_SHA required for swap}"
SHORT_SHA="${GITHUB_SHA:0:7}"
# 1. Tag the currently-live image as :previous (skip if first deploy).
if [ "$DRY_RUN" -eq 1 ] || docker image inspect "${IMAGE}:current" >/dev/null 2>&1; then
run tag "${IMAGE}:current" "${IMAGE}:previous"
fi
# 2. Tag the new SHA as :current.
run tag "${IMAGE}:${SHORT_SHA}" "${IMAGE}:current"
# 3. Stop + remove existing container if present.
run_or_skip stop flights-web
run_or_skip rm flights-web
# 4. Run new container.
run run -d --name flights-web --restart unless-stopped \
-p "127.0.0.1:${PORT}:8080" \
"${IMAGE}:current"
# 5. Wait for the SSR inside the container to start listening before
# returning. Without this, the next nginx-side probe gets a few
# 502s while Node.js is still booting — works (wait-for-url.sh
# retries) but adds noise to the deploy log.
if [ "$DRY_RUN" -eq 0 ]; then
attempt=1
while [ "$attempt" -le 30 ]; do
if curl -fsS -o /dev/null "http://127.0.0.1:${PORT}/"; then
echo "swap: SSR ready on :${PORT} after $attempt attempt(s)"
break
fi
if [ "$attempt" -eq 30 ]; then
echo "swap: SSR did not respond on :${PORT} after 30 attempts" >&2
exit 1
fi
sleep 1
attempt=$((attempt + 1))
done
fi
;;
rollback)
if [ "$DRY_RUN" -eq 0 ] && ! docker image inspect "${IMAGE}:previous" >/dev/null 2>&1; then
echo "fatal: ${IMAGE}:previous not found — cannot rollback" >&2
exit 1
fi
run_or_skip stop flights-web
run_or_skip rm flights-web
run run -d --name flights-web --restart unless-stopped \
-p "127.0.0.1:${PORT}:8080" \
"${IMAGE}:previous"
# Repoint :current to :previous so subsequent swaps have a sane baseline.
run tag "${IMAGE}:previous" "${IMAGE}:current"
;;
*)
echo "usage: $0 [--dry-run] <swap|rollback>" >&2
exit 2
;;
esac