Fix notification recipient for friend addition: notify the friend bei… #178
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy to Server | |
| on: | |
| push: | |
| branches: | |
| - main # 프로덕션 배포 | |
| - release # 프로덕션 배포 | |
| - develop # 개발 환경 배포 | |
| workflow_dispatch: # 수동 실행 (환경 선택 가능) | |
| inputs: | |
| environment: | |
| description: '배포 환경 선택' | |
| required: true | |
| default: 'prod' | |
| type: choice | |
| options: | |
| - dev | |
| - prod | |
| jobs: | |
| deploy: | |
| name: Deploy to ${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'Production' || github.event.inputs.environment == 'prod' && 'Production' || 'Development' }} Server | |
| runs-on: ubuntu-latest | |
| env: | |
| DEPLOY_ENV: ${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup SSH | |
| uses: webfactory/ssh-agent@v0.9.0 | |
| with: | |
| ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| - name: Add server to known hosts | |
| run: | | |
| ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts | |
| - name: Create Firebase service account key file | |
| run: | | |
| mkdir -p src/main/resources | |
| cat > src/main/resources/firebase-service-account.json << 'EOF' | |
| ${{ secrets.FCM_SERVICE_ACCOUNT_KEY_JSON }} | |
| EOF | |
| - name: Copy files to server | |
| run: | | |
| DEPLOY_ENV_VALUE="${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }}" | |
| # 환경별 디렉토리 설정 (각 환경이 완전히 독립적으로 동작) | |
| if [ "$DEPLOY_ENV_VALUE" = "prod" ]; then | |
| REMOTE_DIR="~/taba_backend_prod" | |
| echo "=== Copying files to production directory: $REMOTE_DIR ===" | |
| else | |
| REMOTE_DIR="~/taba_backend_dev" | |
| echo "=== Copying files to development directory: $REMOTE_DIR ===" | |
| fi | |
| # 원격 디렉토리 생성 | |
| ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "mkdir -p $REMOTE_DIR" | |
| # 환경별로 필요한 파일만 복사 (다른 환경 파일은 복사하지 않음) | |
| if [ "$DEPLOY_ENV_VALUE" = "prod" ]; then | |
| echo "=== Copying production files only ===" | |
| scp -o StrictHostKeyChecking=no docker-compose.prod.yml Dockerfile ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:$REMOTE_DIR/ | |
| else | |
| echo "=== Copying development files only ===" | |
| scp -o StrictHostKeyChecking=no docker-compose.dev.yml Dockerfile ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:$REMOTE_DIR/ | |
| fi | |
| # 공통 파일 복사 (각 환경의 독립 디렉토리에) | |
| scp -o StrictHostKeyChecking=no build.gradle settings.gradle gradle.properties ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:$REMOTE_DIR/ | |
| scp -r -o StrictHostKeyChecking=no gradle ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:$REMOTE_DIR/ | |
| scp -r -o StrictHostKeyChecking=no src ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:$REMOTE_DIR/ | |
| - name: Deploy Application | |
| run: | | |
| DEPLOY_ENV_VALUE="${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }}" | |
| ssh -o StrictHostKeyChecking=no "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" DEPLOY_ENV="$DEPLOY_ENV_VALUE" bash << 'REMOTE_SCRIPT' | |
| # 환경별 디렉토리 설정 (각 환경이 완전히 독립적으로 동작) | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| DEPLOY_DIR="$HOME/taba_backend_prod" | |
| else | |
| DEPLOY_DIR="$HOME/taba_backend_dev" | |
| fi | |
| cd "$DEPLOY_DIR" | |
| echo "=== Working directory: $DEPLOY_DIR ===" | |
| echo "=== Current directory: $(pwd) ===" | |
| # 공통 이메일 설정 (프로덕션/개발 동일) | |
| export MAIL_HOST="${{ secrets.MAIL_HOST }}" | |
| export MAIL_PORT="${{ secrets.MAIL_PORT }}" | |
| export MAIL_USERNAME="${{ secrets.MAIL_USERNAME }}" | |
| export MAIL_PASSWORD="${{ secrets.MAIL_PASSWORD }}" | |
| # 이메일 기본값 설정 (Gmail) | |
| [ -z "$MAIL_HOST" ] && export MAIL_HOST=smtp.gmail.com | |
| [ -z "$MAIL_PORT" ] && export MAIL_PORT=587 | |
| # 환경별 설정 (각 환경은 독립적으로 동작) | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| echo "=== Production environment configuration ===" | |
| export DB_NAME="${{ secrets.DB_NAME_PROD }}" | |
| export DB_USERNAME="${{ secrets.DB_USERNAME_PROD }}" | |
| export DB_PASSWORD="${{ secrets.DB_PASSWORD_PROD }}" | |
| export REDIS_PASSWORD="${{ secrets.REDIS_PASSWORD_PROD }}" | |
| export JWT_SECRET="${{ secrets.JWT_SECRET_PROD }}" | |
| export SERVER_URL="${{ secrets.SERVER_URL_PROD }}" | |
| export EXTERNAL_PORT="${{ secrets.EXTERNAL_PORT_PROD }}" | |
| export DB_EXTERNAL_PORT="${{ secrets.DB_EXTERNAL_PORT_PROD }}" | |
| export REDIS_EXTERNAL_PORT="${{ secrets.REDIS_EXTERNAL_PORT_PROD }}" | |
| export FRONTEND_URL="${{ secrets.FRONTEND_URL_PROD }}" | |
| # 포트 기본값 설정 (프로덕션: Backend(8080), MySQL(3306), Redis(6379)) | |
| [ -z "$EXTERNAL_PORT" ] && export EXTERNAL_PORT=8080 | |
| [ -z "$DB_EXTERNAL_PORT" ] && export DB_EXTERNAL_PORT=3306 | |
| [ -z "$REDIS_EXTERNAL_PORT" ] && export REDIS_EXTERNAL_PORT=6379 | |
| export SPRING_PROFILES_ACTIVE=prod | |
| COMPOSE_FILE="docker-compose.prod.yml" | |
| BACKEND_CONTAINER="taba-backend-prod" | |
| MYSQL_CONTAINER="taba-mysql-prod" | |
| REDIS_CONTAINER="taba-redis-prod" | |
| echo "✅ Using production compose file: $COMPOSE_FILE" | |
| echo "✅ Production containers: $BACKEND_CONTAINER, $MYSQL_CONTAINER, $REDIS_CONTAINER" | |
| else | |
| echo "=== Development environment configuration ===" | |
| export DB_NAME="${{ secrets.DB_NAME_DEV }}" | |
| export DB_USERNAME="${{ secrets.DB_USERNAME_DEV }}" | |
| export DB_PASSWORD="${{ secrets.DB_PASSWORD_DEV }}" | |
| export REDIS_PASSWORD="${{ secrets.REDIS_PASSWORD_DEV }}" | |
| export JWT_SECRET="${{ secrets.JWT_SECRET_DEV }}" | |
| export SERVER_URL="${{ secrets.SERVER_URL_DEV }}" | |
| export EXTERNAL_PORT="${{ secrets.EXTERNAL_PORT_DEV }}" | |
| export DB_EXTERNAL_PORT="${{ secrets.DB_EXTERNAL_PORT_DEV }}" | |
| export REDIS_EXTERNAL_PORT="${{ secrets.REDIS_EXTERNAL_PORT_DEV }}" | |
| export FRONTEND_URL="${{ secrets.FRONTEND_URL_DEV }}" | |
| # 포트 기본값 설정 (개발: Backend(8081), MySQL(3307), Redis(6380) - 동시 실행 가능) | |
| [ -z "$EXTERNAL_PORT" ] && export EXTERNAL_PORT=8081 | |
| [ -z "$DB_EXTERNAL_PORT" ] && export DB_EXTERNAL_PORT=3307 | |
| [ -z "$REDIS_EXTERNAL_PORT" ] && export REDIS_EXTERNAL_PORT=6380 | |
| export SPRING_PROFILES_ACTIVE=dev | |
| COMPOSE_FILE="docker-compose.dev.yml" | |
| BACKEND_CONTAINER="taba-backend-dev" | |
| MYSQL_CONTAINER="taba-mysql-dev" | |
| REDIS_CONTAINER="taba-redis-dev" | |
| echo "✅ Using development compose file: $COMPOSE_FILE" | |
| echo "✅ Development containers: $BACKEND_CONTAINER, $MYSQL_CONTAINER, $REDIS_CONTAINER" | |
| fi | |
| # 다른 환경의 컨테이너는 건드리지 않음을 확인 | |
| echo "=== Ensuring other environment containers are not affected ===" | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| DEV_BACKEND=$(docker ps --format '{{.Names}}' | grep "^taba-backend-dev$" || echo "") | |
| DEV_MYSQL=$(docker ps --format '{{.Names}}' | grep "^taba-mysql-dev$" || echo "") | |
| DEV_REDIS=$(docker ps --format '{{.Names}}' | grep "^taba-redis-dev$" || echo "") | |
| if [ -n "$DEV_BACKEND" ] || [ -n "$DEV_MYSQL" ] || [ -n "$DEV_REDIS" ]; then | |
| echo "✅ Development environment containers are running (will not be affected):" | |
| [ -n "$DEV_BACKEND" ] && echo " - $DEV_BACKEND" | |
| [ -n "$DEV_MYSQL" ] && echo " - $DEV_MYSQL" | |
| [ -n "$DEV_REDIS" ] && echo " - $DEV_REDIS" | |
| fi | |
| else | |
| PROD_BACKEND=$(docker ps --format '{{.Names}}' | grep "^taba-backend-prod$" || echo "") | |
| PROD_MYSQL=$(docker ps --format '{{.Names}}' | grep "^taba-mysql-prod$" || echo "") | |
| PROD_REDIS=$(docker ps --format '{{.Names}}' | grep "^taba-redis-prod$" || echo "") | |
| if [ -n "$PROD_BACKEND" ] || [ -n "$PROD_MYSQL" ] || [ -n "$PROD_REDIS" ]; then | |
| echo "✅ Production environment containers are running (will not be affected):" | |
| [ -n "$PROD_BACKEND" ] && echo " - $PROD_BACKEND" | |
| [ -n "$PROD_MYSQL" ] && echo " - $PROD_MYSQL" | |
| [ -n "$PROD_REDIS" ] && echo " - $PROD_REDIS" | |
| fi | |
| fi | |
| # 필수 환경 변수 확인 | |
| if [ -z "$DB_NAME" ] || [ -z "$DB_USERNAME" ] || [ -z "$DB_PASSWORD" ] || [ -z "$JWT_SECRET" ] || [ -z "$SERVER_URL" ]; then | |
| echo "❌ 필수 환경 변수가 설정되지 않았습니다." | |
| echo "DB_NAME: ${DB_NAME:+설정됨}${DB_NAME:-미설정}" | |
| echo "DB_USERNAME: ${DB_USERNAME:+설정됨}${DB_USERNAME:-미설정}" | |
| echo "DB_PASSWORD: ${DB_PASSWORD:+설정됨}${DB_PASSWORD:-미설정}" | |
| echo "JWT_SECRET: ${JWT_SECRET:+설정됨}${JWT_SECRET:-미설정}" | |
| echo "SERVER_URL: ${SERVER_URL:+설정됨}${SERVER_URL:-미설정}" | |
| exit 1 | |
| fi | |
| export DB_HOST=mysql DB_PORT=3306 REDIS_HOST=redis REDIS_PORT=6379 SERVER_PORT=8080 FILE_UPLOAD_DIR=/app/uploads | |
| export JWT_EXPIRATION=${JWT_EXPIRATION:-604800000} | |
| [ -z "$REDIS_PASSWORD" ] && export REDIS_PASSWORD= | |
| COMPOSE_CMD="docker-compose -f $COMPOSE_FILE" | |
| # MySQL 사용자 생성 함수 | |
| ensure_mysql_user() { | |
| local mysql_container="$MYSQL_CONTAINER" | |
| local db_name="$DB_NAME" | |
| local db_username="$DB_USERNAME" | |
| local db_password="$DB_PASSWORD" | |
| local root_pass=$(docker exec "$mysql_container" printenv MYSQL_ROOT_PASSWORD 2>/dev/null || echo "$db_password") | |
| local root_test=$(docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "SELECT 1" 2>&1) || true | |
| if echo "$root_test" | grep -qiE "(ERROR|Access denied|denied)"; then | |
| root_pass="$db_password" | |
| root_test=$(docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "SELECT 1" 2>&1) || true | |
| fi | |
| if ! echo "$root_test" | grep -qiE "(ERROR|Access denied|denied)"; then | |
| docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "CREATE DATABASE IF NOT EXISTS \`$db_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" 2>&1 | grep -v "Warning" >/dev/null || true | |
| docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "DROP USER IF EXISTS '$db_username'@'%';" 2>&1 | grep -v "Warning" >/dev/null || true | |
| docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "CREATE USER '$db_username'@'%' IDENTIFIED BY '$db_password';" 2>&1 | grep -v "Warning" >/dev/null || true | |
| docker exec "$mysql_container" mysql -u root -p"$root_pass" -e "GRANT ALL PRIVILEGES ON \`$db_name\`.* TO '$db_username'@'%'; FLUSH PRIVILEGES;" 2>&1 | grep -v "Warning" >/dev/null || true | |
| fi | |
| } | |
| # 공유 네트워크 생성 (prod/dev 모두 사용, 이미 존재하면 무시) | |
| docker network create taba-network 2>/dev/null || true | |
| # 현재 환경의 백엔드 컨테이너만 중지 및 제거 (다른 환경은 건드리지 않음) | |
| echo "=== Stopping current environment backend container ===" | |
| docker stop "$BACKEND_CONTAINER" 2>/dev/null || true | |
| docker rm -f "$BACKEND_CONTAINER" 2>/dev/null || true | |
| # 손상된 이미지 제거 (ContainerConfig 에러 방지) | |
| echo "=== Removing old backend image to prevent ContainerConfig errors ===" | |
| # docker-compose가 사용하는 이미지 이름 찾기 (프로젝트명-서비스명 형식) | |
| COMPOSE_PROJECT_NAME=$(basename "$(pwd)" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]//g') | |
| OLD_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "${COMPOSE_PROJECT_NAME}.*backend|.*backend.*${COMPOSE_PROJECT_NAME}" | head -1 || echo "") | |
| if [ -z "$OLD_IMAGE" ]; then | |
| # 대체 방법: <none> 태그가 있는 이미지 찾기 | |
| OLD_IMAGE=$(docker images --format "{{.ID}}" --filter "dangling=true" | head -1 || echo "") | |
| if [ -n "$OLD_IMAGE" ]; then | |
| echo "Removing dangling image: $OLD_IMAGE" | |
| docker rmi -f "$OLD_IMAGE" 2>/dev/null || true | |
| fi | |
| else | |
| echo "Removing old backend image: $OLD_IMAGE" | |
| docker rmi -f "$OLD_IMAGE" 2>/dev/null || true | |
| fi | |
| # 손상된 컨테이너 이미지 참조 정리 | |
| docker container prune -f 2>/dev/null || true | |
| # 백엔드 이미지 빌드 | |
| echo "=== Building backend image ===" | |
| $COMPOSE_CMD build --no-cache --progress=plain backend || exit 1 | |
| # 현재 환경의 MySQL/Redis 컨테이너만 완전히 제거 (다른 환경은 건드리지 않음) | |
| echo "=== Stopping and removing current environment MySQL/Redis containers ===" | |
| # 여러 번 시도하여 완전히 제거 | |
| for attempt in {1..3}; do | |
| docker stop "$MYSQL_CONTAINER" "$REDIS_CONTAINER" 2>/dev/null || true | |
| docker rm -f "$MYSQL_CONTAINER" "$REDIS_CONTAINER" 2>/dev/null || true | |
| sleep 1 | |
| # 컨테이너가 완전히 제거되었는지 확인 | |
| if ! docker ps -a --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$" && \ | |
| ! docker ps -a --format '{{.Names}}' | grep -q "^${REDIS_CONTAINER}$"; then | |
| echo "✅ Containers removed successfully" | |
| break | |
| fi | |
| if [ $attempt -eq 3 ]; then | |
| echo "⚠️ Some containers may still exist, but continuing..." | |
| fi | |
| done | |
| # MySQL, Redis 시작 (다른 환경의 컨테이너는 그대로 유지) | |
| echo "=== Starting MySQL and Redis for current environment ===" | |
| # --force-recreate 옵션으로 ContainerConfig 에러 방지 | |
| $COMPOSE_CMD up -d --force-recreate mysql redis | |
| # 컨테이너가 실제로 시작될 때까지 대기 (재시작 포함) | |
| echo "=== Waiting for containers to start ===" | |
| for i in {1..60}; do | |
| MYSQL_RUNNING=$(docker ps --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$" && echo "yes" || echo "no") | |
| REDIS_RUNNING=$(docker ps --format '{{.Names}}' | grep -q "^${REDIS_CONTAINER}$" && echo "yes" || echo "no") | |
| if [ "$MYSQL_RUNNING" = "yes" ] && [ "$REDIS_RUNNING" = "yes" ]; then | |
| echo "✅ Containers are running" | |
| break | |
| fi | |
| # 종료된 컨테이너가 있으면 재시작 시도 | |
| if [ "$MYSQL_RUNNING" = "no" ]; then | |
| MYSQL_STATUS=$(docker ps -a --format '{{.Names}} {{.Status}}' | grep "^${MYSQL_CONTAINER} " || echo "") | |
| if [ -n "$MYSQL_STATUS" ]; then | |
| echo "⚠️ MySQL container not running: $MYSQL_STATUS, attempting restart..." | |
| docker start "$MYSQL_CONTAINER" 2>/dev/null || $COMPOSE_CMD up -d --no-deps mysql | |
| fi | |
| fi | |
| if [ "$REDIS_RUNNING" = "no" ]; then | |
| REDIS_STATUS=$(docker ps -a --format '{{.Names}} {{.Status}}' | grep "^${REDIS_CONTAINER} " || echo "") | |
| if [ -n "$REDIS_STATUS" ]; then | |
| echo "⚠️ Redis container not running: $REDIS_STATUS, attempting restart..." | |
| docker logs --tail=20 "$REDIS_CONTAINER" 2>&1 || true | |
| docker start "$REDIS_CONTAINER" 2>/dev/null || $COMPOSE_CMD up -d --no-deps redis | |
| fi | |
| fi | |
| if [ $i -eq 60 ]; then | |
| echo "❌ Containers failed to start after 60 seconds" | |
| echo "=== Container status ===" | |
| docker ps -a | grep -E "${MYSQL_CONTAINER}|${REDIS_CONTAINER}" || true | |
| echo "=== MySQL logs ===" | |
| docker logs --tail=30 "$MYSQL_CONTAINER" 2>&1 || true | |
| echo "=== Redis logs ===" | |
| docker logs --tail=30 "$REDIS_CONTAINER" 2>&1 || true | |
| exit 1 | |
| fi | |
| sleep 2 | |
| done | |
| # MySQL health check 대기 | |
| echo "=== Waiting for MySQL to be healthy ===" | |
| for i in {1..60}; do | |
| HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' "$MYSQL_CONTAINER" 2>/dev/null || echo "unknown") | |
| echo "MySQL health check attempt $i/60: $HEALTH_STATUS" | |
| if [ "$HEALTH_STATUS" = "healthy" ]; then | |
| echo "✅ MySQL is healthy" | |
| break | |
| fi | |
| if [ $i -eq 60 ]; then | |
| echo "❌ MySQL failed to become healthy after 60 attempts" | |
| docker logs --tail=50 "$MYSQL_CONTAINER" 2>&1 || true | |
| exit 1 | |
| fi | |
| sleep 2 | |
| done | |
| ensure_mysql_user | |
| # 사용자 연결 테스트 | |
| for retry in {1..3}; do | |
| USER_TEST=$(docker exec "$MYSQL_CONTAINER" mysql -u "$DB_USERNAME" -p"$DB_PASSWORD" -e "USE \`$DB_NAME\`; SELECT 1;" 2>&1) || true | |
| if ! echo "$USER_TEST" | grep -qiE "(ERROR|Access denied|denied)"; then | |
| echo "✅ MySQL connection successful" | |
| break | |
| fi | |
| if [ $retry -lt 3 ]; then | |
| ensure_mysql_user | |
| sleep 3 | |
| else | |
| echo "❌ MySQL connection failed" | |
| docker logs --tail=30 "$MYSQL_CONTAINER" 2>&1 | |
| exit 1 | |
| fi | |
| done | |
| # 백엔드 시작 (MySQL/Redis는 이미 실행 중이므로 --no-deps 사용) | |
| echo "=== Starting backend ===" | |
| # MySQL/Redis가 실행 중인지 확인 | |
| if ! docker ps --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$" || \ | |
| ! docker ps --format '{{.Names}}' | grep -q "^${REDIS_CONTAINER}$"; then | |
| echo "❌ MySQL or Redis container is not running" | |
| docker ps --format '{{.Names}}' | grep -E "${MYSQL_CONTAINER}|${REDIS_CONTAINER}" || true | |
| exit 1 | |
| fi | |
| echo "✅ MySQL and Redis are running, starting backend..." | |
| # ContainerConfig 에러 방지를 위해 기존 백엔드 컨테이너 완전히 제거 | |
| echo "=== Removing existing backend container to prevent ContainerConfig errors ===" | |
| docker stop "$BACKEND_CONTAINER" 2>/dev/null || true | |
| docker rm -f "$BACKEND_CONTAINER" 2>/dev/null || true | |
| # docker-compose를 사용하여 컨테이너 제거 (메타데이터 정리) | |
| $COMPOSE_CMD rm -f backend 2>/dev/null || true | |
| sleep 2 | |
| # 백엔드 컨테이너 시작 | |
| $COMPOSE_CMD up -d --no-deps --force-recreate --remove-orphans backend || exit 1 | |
| sleep 5 | |
| echo "=== Backend logs ===" | |
| docker logs --tail=30 "$BACKEND_CONTAINER" 2>&1 | |
| REMOTE_SCRIPT | |
| env: | |
| DEPLOY_ENV: ${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }} | |
| - name: Check container status | |
| run: | | |
| DEPLOY_ENV_VALUE="${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }}" | |
| ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} DEPLOY_ENV="$DEPLOY_ENV_VALUE" bash << 'REMOTE_SCRIPT' | |
| # 환경별 디렉토리 설정 | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| DEPLOY_DIR="$HOME/taba_backend_prod" | |
| else | |
| DEPLOY_DIR="$HOME/taba_backend_dev" | |
| fi | |
| cd "$DEPLOY_DIR" | |
| BACKEND_CONTAINER=$([ "$DEPLOY_ENV" = "prod" ] && echo "taba-backend-prod" || echo "taba-backend-dev") | |
| echo "=== Container Status ===" | |
| docker ps -a --filter "name=taba" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" || true | |
| echo "" | |
| echo "=== Backend Logs ===" | |
| docker logs --tail=50 "$BACKEND_CONTAINER" 2>&1 || echo "No logs" | |
| REMOTE_SCRIPT | |
| env: | |
| DEPLOY_ENV: ${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }} | |
| - name: Health Check (Internal) | |
| continue-on-error: true | |
| run: | | |
| ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} bash << 'REMOTE_SCRIPT' | |
| BACKEND_CONTAINER=$([ "$DEPLOY_ENV" = "prod" ] && echo "taba-backend-prod" || echo "taba-backend-dev") | |
| sleep 60 | |
| for i in {1..10}; do | |
| docker ps | grep -q "$BACKEND_CONTAINER" && docker exec "$BACKEND_CONTAINER" curl -f http://localhost:8080/api/v1/actuator/health 2>&1 && exit 0 | |
| sleep 10 | |
| done | |
| docker logs --tail=50 "$BACKEND_CONTAINER" 2>&1 | |
| exit 1 | |
| REMOTE_SCRIPT | |
| env: | |
| DEPLOY_ENV: ${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }} | |
| - name: Health Check (External) - Optional | |
| continue-on-error: true | |
| run: | | |
| DEPLOY_ENV="${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }}" | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| SERVER_URL='${{ secrets.SERVER_URL_PROD }}' | |
| else | |
| SERVER_URL='${{ secrets.SERVER_URL_DEV }}' | |
| fi | |
| echo "Checking external health endpoint..." | |
| echo "DEPLOY_ENV: $DEPLOY_ENV" | |
| echo "SERVER_URL: ${SERVER_URL:0:20}..." # 처음 20자만 표시 | |
| # SERVER_URL이 비어있거나 잘못된 형식인지 확인 | |
| if [ -z "$SERVER_URL" ] || [ "$SERVER_URL" = "null" ] || [[ "$SERVER_URL" == *"***"* ]]; then | |
| echo "SERVER_URL not properly set, skipping external health check" | |
| exit 0 | |
| fi | |
| # SERVER_URL에서 도메인 추출 (https://api.example.com/api/v1 -> https://api.example.com) | |
| SERVER_DOMAIN=$(echo "$SERVER_URL" | sed 's|https\?://||' | sed 's|/.*||') | |
| if [ -n "$SERVER_DOMAIN" ] && [ "$SERVER_DOMAIN" != "null" ]; then | |
| curl -f https://${SERVER_DOMAIN}/api/v1/actuator/health || echo "External health check failed" | |
| else | |
| echo "SERVER_URL not set, skipping external health check" | |
| fi | |
| - name: Deployment Summary | |
| run: | | |
| DEPLOY_ENV="${{ (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main') && 'prod' || github.ref == 'refs/heads/develop' && 'dev' || github.event.inputs.environment == 'prod' && 'prod' || github.event.inputs.environment == 'dev' && 'dev' || 'dev' }}" | |
| if [ "$DEPLOY_ENV" = "prod" ]; then | |
| SERVER_URL='${{ secrets.SERVER_URL_PROD }}' | |
| ENV_NAME="프로덕션" | |
| else | |
| SERVER_URL='${{ secrets.SERVER_URL_DEV }}' | |
| ENV_NAME="개발" | |
| fi | |
| SERVER_DOMAIN=$(echo "$SERVER_URL" | sed 's|https\?://||' | sed 's|/.*||' 2>/dev/null || echo "") | |
| echo "## 🚀 $ENV_NAME 배포 완료!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **환경**: $ENV_NAME" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "$SERVER_DOMAIN" ] && [ "$SERVER_DOMAIN" != "null" ] && [[ ! "$SERVER_DOMAIN" == *"***"* ]]; then | |
| echo "- **서버**: $SERVER_DOMAIN" >> $GITHUB_STEP_SUMMARY | |
| echo "- **API URL**: $SERVER_URL" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Health Check**: $SERVER_URL/actuator/health" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Swagger UI**: $SERVER_URL/swagger-ui/index.html" >> $GITHUB_STEP_SUMMARY | |
| else | |
| DEPLOY_ENV_UPPER=$(echo "$DEPLOY_ENV" | tr '[:lower:]' '[:upper:]') | |
| echo "- **서버**: GitHub Secrets에서 SERVER_URL_${DEPLOY_ENV_UPPER} 설정 필요" >> $GITHUB_STEP_SUMMARY | |
| echo "- **API URL**: 설정되지 않음" >> $GITHUB_STEP_SUMMARY | |
| fi |