#!/usr/bin/env bash set -Eeuo pipefail DEPLOY_DIR="${DEPLOY_DIR:-/home/gitlab-runner/deploy/marketing-parser}" COMPOSE_FILE="${COMPOSE_FILE:-$DEPLOY_DIR/deployment/docker-compose.server.yml}" PARSER_IMAGE_NAME="${PARSER_IMAGE_NAME:-marketing-parser-app}" PARSER_IMAGE_TAG="${PARSER_IMAGE_TAG:-${CI_COMMIT_SHORT_SHA:-latest}}" PARSER_IMAGE="${PARSER_IMAGE:-${PARSER_IMAGE_NAME}:${PARSER_IMAGE_TAG}}" PARSER_CONTAINER_NAME="${PARSER_CONTAINER_NAME:-konturai-parser-app}" COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-marketing-parser}" WAIT_TIMEOUT_SECONDS="${WAIT_TIMEOUT_SECONDS:-120}" require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing required command: $1" >&2 exit 1 fi } wait_for_container() { local deadline status deadline=$((SECONDS + WAIT_TIMEOUT_SECONDS)) while (( SECONDS < deadline )); do status="$(docker inspect "$PARSER_CONTAINER_NAME" --format '{{.State.Status}}' 2>/dev/null || true)" if [[ "$status" == "running" ]]; then echo "Container is running: $PARSER_CONTAINER_NAME" return 0 fi if [[ "$status" == "exited" || "$status" == "dead" ]]; then echo "Container $PARSER_CONTAINER_NAME is in state: $status" >&2 docker logs --tail 100 "$PARSER_CONTAINER_NAME" || true exit 1 fi sleep 3 done echo "Timed out waiting for $PARSER_CONTAINER_NAME to start" >&2 docker logs --tail 100 "$PARSER_CONTAINER_NAME" || true exit 1 } require_command bash require_command docker require_command rsync if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=(docker compose) elif command -v docker-compose >/dev/null 2>&1; then COMPOSE_CMD=(docker-compose) else echo "Missing docker compose or docker-compose" >&2 exit 1 fi if [[ -z "${CI_PROJECT_DIR:-}" || ! -d "${CI_PROJECT_DIR:-}" ]]; then echo "CI_PROJECT_DIR is not set or does not exist" >&2 exit 1 fi if ! docker info >/dev/null 2>&1; then echo "Docker daemon is not reachable for the runner user" >&2 exit 1 fi install -d "$DEPLOY_DIR" if [[ "$CI_PROJECT_DIR" != "$DEPLOY_DIR" ]]; then rsync -a --delete \ --exclude '.git/' \ --exclude '.m2/' \ --exclude 'target/' \ --exclude '.codex' \ --exclude '.claude/' \ --exclude '.cursor/' \ --exclude '.idea/' \ --exclude '.vscode/' \ --exclude '.DS_Store' \ --exclude '.env' \ --exclude '.env.*' \ "$CI_PROJECT_DIR/" "$DEPLOY_DIR/" fi if [[ ! -f "$DEPLOY_DIR/.env.production" ]]; then if [[ -n "${DEPLOY_ENV_FILE:-}" && -f "${DEPLOY_ENV_FILE}" ]]; then install -m 600 "$DEPLOY_ENV_FILE" "$DEPLOY_DIR/.env.production" else echo "Missing $DEPLOY_DIR/.env.production. Run scripts/bootstrap_gitlab_deploy.sh once or set DEPLOY_ENV_FILE." >&2 exit 1 fi fi if docker ps -a --format '{{.Names}}' | grep -Fxq "$PARSER_CONTAINER_NAME"; then EXISTING_PROJECT="$(docker inspect "$PARSER_CONTAINER_NAME" --format '{{ index .Config.Labels "com.docker.compose.project" }}' 2>/dev/null || true)" if [[ -n "$EXISTING_PROJECT" && "$EXISTING_PROJECT" != "$COMPOSE_PROJECT_NAME" ]]; then echo "Removing existing container $PARSER_CONTAINER_NAME from compose project $EXISTING_PROJECT" docker rm -f "$PARSER_CONTAINER_NAME" fi fi cd "$DEPLOY_DIR" echo "Deploy directory: $DEPLOY_DIR" echo "Building image: $PARSER_IMAGE" docker build -t "$PARSER_IMAGE" -t "${PARSER_IMAGE_NAME}:latest" "$DEPLOY_DIR" export PARSER_IMAGE export PARSER_CONTAINER_NAME export COMPOSE_PROJECT_NAME "${COMPOSE_CMD[@]}" -f "$COMPOSE_FILE" config -q "${COMPOSE_CMD[@]}" -f "$COMPOSE_FILE" up -d --remove-orphans --force-recreate wait_for_container docker ps --filter "name=^${PARSER_CONTAINER_NAME}$" --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'