1- name : Build and Push Docker Images
1+ name : Build, Push, and Deploy to EC2
22
33on :
44 push :
88 branches :
99 - master
1010 workflow_dispatch :
11+ inputs :
12+ force_rebuild :
13+ description : " Force rebuild of all Docker images"
14+ required : false
15+ default : false
16+ type : boolean
1117
1218permissions :
1319 contents : read
1420
21+ env :
22+ DOCKER_REGISTRY : docker.io
23+ DOCKER_USERNAME : ${{ secrets.DOCKERHUB_USERNAME }}
24+ DOCKER_PASSWORD : ${{ secrets.DOCKERHUB_TOKEN }}
25+
1526jobs :
16- build-and-push :
17- name : Build and Push ${{ matrix.service }}
27+ detect-changes :
28+ name : Detect Changes
1829 runs-on : ubuntu-latest
30+ outputs :
31+ backends : ${{ steps.set-services.outputs.backends }}
32+ frontends : ${{ steps.set-services.outputs.frontends }}
33+ has_backend_changes : ${{ steps.set-services.outputs.has_backend_changes }}
34+ has_frontend_changes : ${{ steps.set-services.outputs.has_frontend_changes }}
35+ steps :
36+ - name : Checkout Repository
37+ uses : actions/checkout@v4
38+
39+ - name : Check for changes
40+ id : filter
41+ uses : dorny/paths-filter@v3
42+ with :
43+ filters : |
44+ api-gateway:
45+ - 'apps/api-gateway/**'
46+ - 'packages/**'
47+ - 'package.json'
48+ - 'pnpm-lock.yaml'
49+ - 'pnpm-workspace.yaml'
50+ - 'tsconfig.base.json'
51+ - 'nx.json'
52+ auth-service:
53+ - 'apps/auth-service/**'
54+ - 'packages/**'
55+ - 'package.json'
56+ - 'pnpm-lock.yaml'
57+ - 'pnpm-workspace.yaml'
58+ - 'tsconfig.base.json'
59+ - 'nx.json'
60+ product-service:
61+ - 'apps/product-service/**'
62+ - 'packages/**'
63+ - 'package.json'
64+ - 'pnpm-lock.yaml'
65+ - 'pnpm-workspace.yaml'
66+ - 'tsconfig.base.json'
67+ - 'nx.json'
68+ order-service:
69+ - 'apps/order-service/**'
70+ - 'packages/**'
71+ - 'package.json'
72+ - 'pnpm-lock.yaml'
73+ - 'pnpm-workspace.yaml'
74+ - 'tsconfig.base.json'
75+ - 'nx.json'
76+ admin-service:
77+ - 'apps/admin-service/**'
78+ - 'packages/**'
79+ - 'package.json'
80+ - 'pnpm-lock.yaml'
81+ - 'pnpm-workspace.yaml'
82+ - 'tsconfig.base.json'
83+ - 'nx.json'
84+ chatting-service:
85+ - 'apps/chatting-service/**'
86+ - 'packages/**'
87+ - 'package.json'
88+ - 'pnpm-lock.yaml'
89+ - 'pnpm-workspace.yaml'
90+ - 'tsconfig.base.json'
91+ - 'nx.json'
92+ recommendation-service:
93+ - 'apps/recommendation-service/**'
94+ - 'packages/**'
95+ - 'package.json'
96+ - 'pnpm-lock.yaml'
97+ - 'pnpm-workspace.yaml'
98+ - 'tsconfig.base.json'
99+ - 'nx.json'
100+ logger-service:
101+ - 'apps/logger-service/**'
102+ - 'packages/**'
103+ - 'package.json'
104+ - 'pnpm-lock.yaml'
105+ - 'pnpm-workspace.yaml'
106+ - 'tsconfig.base.json'
107+ - 'nx.json'
108+ kafka-consumer:
109+ - 'apps/kafka/**'
110+ - 'packages/**'
111+ - 'package.json'
112+ - 'pnpm-lock.yaml'
113+ - 'pnpm-workspace.yaml'
114+ - 'tsconfig.base.json'
115+ - 'nx.json'
116+ user-ui:
117+ - 'apps/user-ui/**'
118+ - 'packages/**'
119+ - 'package.json'
120+ - 'pnpm-lock.yaml'
121+ - 'pnpm-workspace.yaml'
122+ - 'tsconfig.base.json'
123+ - 'nx.json'
124+ seller-ui:
125+ - 'apps/seller-ui/**'
126+ - 'packages/**'
127+ - 'package.json'
128+ - 'pnpm-lock.yaml'
129+ - 'pnpm-workspace.yaml'
130+ - 'tsconfig.base.json'
131+ - 'nx.json'
132+ admin-ui:
133+ - 'apps/admin-ui/**'
134+ - 'packages/**'
135+ - 'package.json'
136+ - 'pnpm-lock.yaml'
137+ - 'pnpm-workspace.yaml'
138+ - 'tsconfig.base.json'
139+ - 'nx.json'
140+
141+ - name : Set services output
142+ id : set-services
143+ run : |
144+ FORCE="${{ github.event.inputs.force_rebuild }}"
145+ BACKENDS=()
146+ FRONTENDS=()
147+
148+ if [ "$FORCE" = "true" ]; then
149+ BACKENDS=("api-gateway" "auth-service" "product-service" "order-service" "admin-service" "chatting-service" "recommendation-service" "logger-service" "kafka-consumer")
150+ FRONTENDS=("user-ui" "seller-ui" "admin-ui")
151+ else
152+ if [ "${{ steps.filter.outputs.api-gateway }}" = "true" ]; then BACKENDS+=("api-gateway"); fi
153+ if [ "${{ steps.filter.outputs.auth-service }}" = "true" ]; then BACKENDS+=("auth-service"); fi
154+ if [ "${{ steps.filter.outputs.product-service }}" = "true" ]; then BACKENDS+=("product-service"); fi
155+ if [ "${{ steps.filter.outputs.order-service }}" = "true" ]; then BACKENDS+=("order-service"); fi
156+ if [ "${{ steps.filter.outputs.admin-service }}" = "true" ]; then BACKENDS+=("admin-service"); fi
157+ if [ "${{ steps.filter.outputs.chatting-service }}" = "true" ]; then BACKENDS+=("chatting-service"); fi
158+ if [ "${{ steps.filter.outputs.recommendation-service }}" = "true" ]; then BACKENDS+=("recommendation-service"); fi
159+ if [ "${{ steps.filter.outputs.logger-service }}" = "true" ]; then BACKENDS+=("logger-service"); fi
160+ if [ "${{ steps.filter.outputs.kafka-consumer }}" = "true" ]; then BACKENDS+=("kafka-consumer"); fi
161+
162+ if [ "${{ steps.filter.outputs.user-ui }}" = "true" ]; then FRONTENDS+=("user-ui"); fi
163+ if [ "${{ steps.filter.outputs.seller-ui }}" = "true" ]; then FRONTENDS+=("seller-ui"); fi
164+ if [ "${{ steps.filter.outputs.admin-ui }}" = "true" ]; then FRONTENDS+=("admin-ui"); fi
165+ fi
166+
167+ if [ ${#BACKENDS[@]} -eq 0 ]; then
168+ echo "backends=[]" >> $GITHUB_OUTPUT
169+ echo "has_backend_changes=false" >> $GITHUB_OUTPUT
170+ else
171+ JSON_BACKENDS=$(printf '%s\n' "${BACKENDS[@]}" | jq -R . | jq -s -c .)
172+ echo "backends=$JSON_BACKENDS" >> $GITHUB_OUTPUT
173+ echo "has_backend_changes=true" >> $GITHUB_OUTPUT
174+ fi
175+
176+ if [ ${#FRONTENDS[@]} -eq 0 ]; then
177+ echo "frontends=[]" >> $GITHUB_OUTPUT
178+ echo "has_frontend_changes=false" >> $GITHUB_OUTPUT
179+ else
180+ JSON_FRONTENDS=$(printf '%s\n' "${FRONTENDS[@]}" | jq -R . | jq -s -c .)
181+ echo "frontends=$JSON_FRONTENDS" >> $GITHUB_OUTPUT
182+ echo "has_frontend_changes=true" >> $GITHUB_OUTPUT
183+ fi
184+
185+ build-backend :
186+ name : Build Backend ${{ matrix.service }}
187+ runs-on : ubuntu-latest
188+ needs : detect-changes
189+ if : needs.detect-changes.outputs.has_backend_changes == 'true'
19190 strategy :
20191 fail-fast : false
21192 matrix :
22- service :
23- - api-gateway
24- - auth-service
25- - product-service
26- - order-service
27- - admin-service
28- - chatting-service
29- - recommendation-service
30- - logger-service
31- - kafka-consumer
32- - user-ui
33- - seller-ui
34- - admin-ui
193+ service : ${{ fromJson(needs.detect-changes.outputs.backends) }}
35194
36195 steps :
37196 - name : Checkout Repository
47206 if : github.event_name != 'pull_request'
48207 uses : docker/login-action@v3
49208 with :
50- username : ${{ secrets.DOCKER_USERNAME }}
51- password : ${{ secrets.DOCKER_PASSWORD }}
209+ username : ${{ secrets.DOCKERHUB_USERNAME }}
210+ password : ${{ secrets.DOCKERHUB_TOKEN }}
52211
53212 - name : Set Dockerfile path
54213 id : vars
@@ -67,7 +226,77 @@ jobs:
67226 file : ${{ steps.vars.outputs.dockerfile }}
68227 push : ${{ github.event_name != 'pull_request' }}
69228 tags : |
70- ${{ secrets.DOCKER_USERNAME || 'eshop' }}/${{ matrix.service }}:latest
71- ${{ secrets.DOCKER_USERNAME || 'eshop' }}/${{ matrix.service }}:${{ github.sha }}
229+ ${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:latest
230+ ${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:${{ github.sha }}
231+ cache-from : type=gha,scope=${{ matrix.service }}
232+ cache-to : type=gha,mode=max,scope=${{ matrix.service }}
233+
234+ build-frontend :
235+ name : Build Frontend ${{ matrix.service }}
236+ runs-on : ubuntu-latest
237+ needs : detect-changes
238+ if : needs.detect-changes.outputs.has_frontend_changes == 'true'
239+ strategy :
240+ fail-fast : false
241+ matrix :
242+ service : ${{ fromJson(needs.detect-changes.outputs.frontends) }}
243+
244+ steps :
245+ - name : Checkout Repository
246+ uses : actions/checkout@v4
247+
248+ - name : Set up QEMU
249+ uses : docker/setup-qemu-action@v3
250+
251+ - name : Set up Docker Buildx
252+ uses : docker/setup-buildx-action@v3
253+
254+ - name : Log in to Docker Hub
255+ if : github.event_name != 'pull_request'
256+ uses : docker/login-action@v3
257+ with :
258+ username : ${{ secrets.DOCKERHUB_USERNAME }}
259+ password : ${{ secrets.DOCKERHUB_TOKEN }}
260+
261+ - name : Build and Push Image
262+ uses : docker/build-push-action@v5
263+ with :
264+ context : .
265+ file : apps/${{ matrix.service }}/Dockerfile
266+ push : ${{ github.event_name != 'pull_request' }}
267+ tags : |
268+ ${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:latest
269+ ${{ secrets.DOCKERHUB_USERNAME || 'eshop' }}/${{ matrix.service }}:${{ github.sha }}
72270 cache-from : type=gha,scope=${{ matrix.service }}
73271 cache-to : type=gha,mode=max,scope=${{ matrix.service }}
272+
273+ deploy :
274+ name : Deploy to EC2
275+ runs-on : ubuntu-latest
276+ needs : [detect-changes, build-backend, build-frontend]
277+ if : |
278+ always() &&
279+ github.event_name != 'pull_request' &&
280+ (needs.detect-changes.outputs.has_backend_changes == 'true' || needs.detect-changes.outputs.has_frontend_changes == 'true') &&
281+ (needs.build-backend.result == 'success' || needs.build-backend.result == 'skipped') &&
282+ (needs.build-frontend.result == 'success' || needs.build-frontend.result == 'skipped')
283+ steps :
284+ - name : Checkout Repository
285+ uses : actions/checkout@v4
286+
287+ - name : Set up SSH Private Key
288+ run : |
289+ cat << 'EOF' > key.pem
290+ ${{ secrets.EC2_PRIVATE_KEY }}
291+ EOF
292+ chmod 600 key.pem
293+
294+ - name : Run Deployment Script
295+ env :
296+ EC2_HOST : ${{ secrets.EC2_HOST }}
297+ SSH_KEY_PATH : ./key.pem
298+ DOCKER_USERNAME : ${{ secrets.DOCKERHUB_USERNAME }}
299+ DOCKER_PASSWORD : ${{ secrets.DOCKERHUB_TOKEN }}
300+ run : |
301+ chmod +x scripts/deploy-production.sh
302+ ./scripts/deploy-production.sh
0 commit comments