#!/usr/bin/env bash set -euo pipefail ARCHIVE="${1:-}" IMAGE="${2:-}" ENV_FILE="${3:-}" HOST_PORT="${4:-9135}" CONTAINER_NAME="cozsweet-frontend-domestic-test" INTERNAL_PORT="3000" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SERVICE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" SHARED_ROOT="$SERVICE_ROOT/shared" RELEASE_ROOT="$SERVICE_ROOT/releases/docker" if [ -z "$ARCHIVE" ] || [ -z "$IMAGE" ] || [ -z "$ENV_FILE" ]; then echo "Usage: $0 [host-port]" >&2 exit 64 fi if [ "$HOST_PORT" != "9135" ]; then echo "Domestic frontend test must use isolated host port 9135" >&2 exit 64 fi case "$ENV_FILE" in "$SHARED_ROOT"/*) ;; *) echo "Environment file must be stored under $SHARED_ROOT" >&2 exit 64 ;; esac if [ ! -s "$ARCHIVE" ] || [ ! -s "$ENV_FILE" ]; then echo "Image archive or environment file is missing" >&2 exit 66 fi mkdir -p "$RELEASE_ROOT" chmod 700 "$SHARED_ROOT" gzip -dc "$ARCHIVE" | docker load >/dev/null docker image inspect "$IMAGE" >/dev/null old_image_id="" old_image_ref="" if docker container inspect "$CONTAINER_NAME" >/dev/null 2>&1; then old_image_id="$(docker inspect -f '{{.Image}}' "$CONTAINER_NAME")" old_image_ref="$(docker inspect -f '{{.Config.Image}}' "$CONTAINER_NAME")" elif ss -ltnH "sport = :$HOST_PORT" 2>/dev/null | grep -q .; then echo "Host port $HOST_PORT is already used by another service" >&2 exit 69 fi run_container() { local image_ref="$1" docker run -d \ --name "$CONTAINER_NAME" \ --restart unless-stopped \ -p "127.0.0.1:$HOST_PORT:$INTERNAL_PORT" \ --env-file "$ENV_FILE" \ -e "PORT=$INTERNAL_PORT" \ "$image_ref" >/dev/null } wait_healthy() { local deadline=$((SECONDS + 180)) while [ "$SECONDS" -le "$deadline" ]; do state="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$CONTAINER_NAME" 2>/dev/null || true)" status="$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1:$HOST_PORT/" 2>/dev/null || true)" if [[ "$status" =~ ^(200|204|301|302|303|307|308)$ ]] && { [ "$state" = "healthy" ] || [ "$state" = "running" ]; }; then return 0 fi sleep 2 done return 1 } docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true deployment_ok="1" if ! run_container "$IMAGE"; then deployment_ok="0" elif ! wait_healthy; then deployment_ok="0" fi if [ "$deployment_ok" != "1" ]; then docker logs --tail 120 "$CONTAINER_NAME" >&2 || true docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true if [ -n "$old_image_id" ] && docker image inspect "$old_image_id" >/dev/null 2>&1; then echo "New container failed health checks; restoring previous image" >&2 run_container "$old_image_id" wait_healthy || true fi exit 1 fi created_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" image_id="$(docker image inspect "$IMAGE" -f '{{.Id}}')" record="{\"createdAt\":\"$created_at\",\"environment\":\"domestic-test\",\"image\":\"$IMAGE\",\"imageId\":\"$image_id\",\"container\":\"$CONTAINER_NAME\",\"hostPort\":$HOST_PORT}" if [ -f "$RELEASE_ROOT/current.json" ]; then cp "$RELEASE_ROOT/current.json" "$RELEASE_ROOT/previous.json" fi printf '%s\n' "$record" > "$RELEASE_ROOT/current.json" printf '%s\n' "$record" >> "$RELEASE_ROOT/releases.jsonl" echo "Domestic frontend test is healthy" echo "container=$CONTAINER_NAME" echo "host_port=$HOST_PORT" echo "image=$IMAGE" if [ -n "$old_image_ref" ]; then echo "previous_image=$old_image_ref" fi