Skip to content

Merge tag '2.1.2' into develop #88

Merge tag '2.1.2' into develop

Merge tag '2.1.2' into develop #88

name: Development Deployment
on:
push:
branches:
- develop
workflow_dispatch:
inputs:
deploy_client:
description: 'Deploy client container'
required: false
default: true
type: boolean
deploy_nginx:
description: 'Deploy nginx container'
required: false
default: false
type: boolean
force_rebuild:
description: 'Force rebuild (for secret changes)'
required: false
default: true
type: boolean
concurrency:
group: deploy-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: false
jobs:
build-image:
runs-on: ubuntu-latest
environment:
name: development
outputs:
nginx_changed: ${{ steps.changes.outputs.nginx }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Detect changed files
if: github.event_name == 'push'
uses: dorny/paths-filter@v3
id: changes
with:
filters: |
client:
- 'app/**'
- 'src/**'
- 'public/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'docker/client/**'
- 'next.config.js'
nginx:
- 'docker/nginx/**'
- name: Set deployment flags for manual trigger
if: github.event_name == 'workflow_dispatch'
id: manual_flags
run: |
echo "client=${{ github.event.inputs.deploy_client }}" >> $GITHUB_OUTPUT
echo "nginx=${{ github.event.inputs.deploy_nginx }}" >> $GITHUB_OUTPUT
- name: Login to DockerHub
if: |
(github.event_name == 'push' && (steps.changes.outputs.client == 'true' || steps.changes.outputs.nginx == 'true')) ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.deploy_client == 'true' || github.event.inputs.deploy_nginx == 'true'))
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
if: |
(github.event_name == 'push' && (steps.changes.outputs.client == 'true' || steps.changes.outputs.nginx == 'true')) ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.deploy_client == 'true' || github.event.inputs.deploy_nginx == 'true'))
uses: docker/setup-buildx-action@v3
- name: Create .env.production
if: |
(github.event_name == 'push' && steps.changes.outputs.client == 'true') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_client == 'true')
run: |
echo "NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}" > .env.production
echo "NEXT_PUBLIC_AWS_S3=${{ secrets.NEXT_PUBLIC_AWS_S3 }}" >> .env.production
echo "NEXT_PUBLIC_GTM_ID=${{ secrets.NEXT_PUBLIC_GTM_ID }}" >> .env.production
echo "NEXT_PUBLIC_GA_ID=${{ secrets.NEXT_PUBLIC_GA_ID }}" >> .env.production
echo "DISCORD_FEEDBACK_WEBHOOK_URL=${{ secrets.DISCORD_FEEDBACK_WEBHOOK_URL }}" >> .env.production
- name: Build and Push Client image
if: |
(github.event_name == 'push' && steps.changes.outputs.client == 'true') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_client == 'true')
uses: docker/build-push-action@v5
with:
push: true
context: .
file: ./docker/client/Dockerfile
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/yogieat-client:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and Push Nginx image
if: |
(github.event_name == 'push' && steps.changes.outputs.nginx == 'true') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_nginx == 'true')
uses: docker/build-push-action@v5
with:
push: true
file: ./docker/nginx/Dockerfile
context: ./docker/nginx
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/yogieat-nginx:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-application:
needs: build-image
runs-on: ubuntu-latest
environment:
name: development
steps:
- name: Checkout Repository
uses: actions/checkout@v5
- name: Copy docker-compose.yml to Server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_PRIVATE_KEY }}
source: "docker-compose.yml"
target: "~/yogieat"
- name: Deploy Project to Gabia Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_PRIVATE_KEY }}
script: |
cd ~/yogieat
# Pull latest images
sudo docker compose pull
# Restart only client-dev (무중단 배포)
sudo docker compose up -d --no-deps client-dev
# Clean up unused images only (preserve volumes with SSL certificates)
sudo docker image prune -a -f
sudo docker compose ps
- name: Restart Nginx if changed
if: |
(github.event_name == 'push' && needs.build-image.outputs.nginx_changed == 'true') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_nginx == 'true')
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_PRIVATE_KEY }}
script: |
cd ~/yogieat
sudo docker compose up -d --no-deps nginx
- name: Health Check
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_PRIVATE_KEY }}
script: |
cd ~/yogieat
echo "🔍 Checking client container health..."
# Configuration: start_period=60s, interval=15s
# Max wait: 90s (60s grace + 15s interval * 2)
MAX_WAIT_TIME=90
POLL_INTERVAL=10
ELAPSED=0
while [ $ELAPSED -lt $MAX_WAIT_TIME ]; do
CLIENT_STATUS=$(sudo docker inspect --format='{{.State.Health.Status}}' yogieat-client-dev 2>/dev/null || echo "no-healthcheck")
if [ "$CLIENT_STATUS" = "healthy" ] || [ "$CLIENT_STATUS" = "no-healthcheck" ]; then
echo "✅ Client container is healthy after ${ELAPSED}s"
sudo docker compose ps
exit 0
elif [ "$CLIENT_STATUS" = "unhealthy" ]; then
echo "❌ Client container is unhealthy after ${ELAPSED}s"
sudo docker compose ps
echo ""
echo "📋 Recent logs:"
sudo docker compose logs --tail=100 client-dev
exit 1
else
echo "⏳ Client status: $CLIENT_STATUS (${ELAPSED}s/${MAX_WAIT_TIME}s)"
sleep $POLL_INTERVAL
ELAPSED=$((ELAPSED + POLL_INTERVAL))
fi
done
echo "⏱️ Timeout: Container did not become healthy within ${MAX_WAIT_TIME}s"
sudo docker compose ps
echo ""
echo "📋 Recent logs:"
sudo docker compose logs --tail=100 client-dev
exit 1
- name: Extract commit info
id: commit
run: |
COMMIT_MSG=$(echo "${{ github.event.head_commit.message }}" | head -n 1)
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's|refs/heads/||')
echo "message=$COMMIT_MSG" >> $GITHUB_OUTPUT
echo "sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Discord notification
if: always()
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
title: "🚀 Development 배포"
description: |
**상태**: ${{ job.status }}
**브랜치**: `${{ steps.commit.outputs.branch }}`
**트리거**: ${{ github.event_name == 'workflow_dispatch' && '🖱️ 수동 배포' || '🔄 자동 배포' }}
**커밋**: [`${{ steps.commit.outputs.sha }}`](https://github.com/${{ github.repository }}/commit/${{ github.sha }})
**메시지**: ${{ steps.commit.outputs.message }}
color: ${{ job.status == 'success' && '0x57F287' || '0xED4245' }}
username: GitHub Actions