-
Notifications
You must be signed in to change notification settings - Fork 0
234 lines (208 loc) · 8.63 KB
/
development-deploy.yml
File metadata and controls
234 lines (208 loc) · 8.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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
- 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