source: .github/workflows/ci-cd.yml@ c0086c0

Last change on this file since c0086c0 was c0086c0, checked in by Andrej <asumanovski@…>, 2 weeks ago

Add GitHub Actions CI/CD pipeline with DockerHub and Kubernetes deployment

  • Build job validates both images on every push and PR
  • Push job builds and pushes backend/frontend images to DockerHub on master
  • Deploy job applies k8s manifests and rolls out new images to k3s cluster
  • Added k8s manifests: namespace, postgres StatefulSet, backend and frontend Deployments
  • Added /api/auth/ping health endpoint for k8s readiness/liveness probes
  • Property mode set to 100644
File size: 4.9 KB
Line 
1name: CI/CD
2
3on:
4 push:
5 branches: [master]
6 pull_request:
7 branches: [master]
8
9env:
10 BACKEND_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/trekr-backend
11 FRONTEND_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/trekr-frontend
12
13jobs:
14 # ── CI: build both images on every push / PR ──────────────────────────────
15 build:
16 name: Build
17 runs-on: ubuntu-latest
18 steps:
19 - uses: actions/checkout@v4
20
21 - name: Set up Docker Buildx
22 uses: docker/setup-buildx-action@v3
23
24 - name: Build backend image (no push)
25 uses: docker/build-push-action@v5
26 with:
27 context: ./backend
28 push: false
29 cache-from: type=gha,scope=backend
30 cache-to: type=gha,scope=backend,mode=max
31
32 - name: Build frontend image (no push)
33 uses: docker/build-push-action@v5
34 with:
35 context: ./frontend
36 push: false
37 cache-from: type=gha,scope=frontend
38 cache-to: type=gha,scope=frontend,mode=max
39
40 # ── CD part 1: push images to DockerHub (master only) ─────────────────────
41 push:
42 name: Push to DockerHub
43 runs-on: ubuntu-latest
44 needs: build
45 if: github.ref == 'refs/heads/master' && github.event_name == 'push'
46 outputs:
47 image_tag: ${{ github.sha }}
48 steps:
49 - uses: actions/checkout@v4
50
51 - name: Set up Docker Buildx
52 uses: docker/setup-buildx-action@v3
53
54 - name: Log in to DockerHub
55 uses: docker/login-action@v3
56 with:
57 username: ${{ secrets.DOCKERHUB_USERNAME }}
58 password: ${{ secrets.DOCKERHUB_TOKEN }}
59
60 - name: Build and push backend
61 uses: docker/build-push-action@v5
62 with:
63 context: ./backend
64 push: true
65 tags: |
66 ${{ env.BACKEND_IMAGE }}:latest
67 ${{ env.BACKEND_IMAGE }}:${{ github.sha }}
68 cache-from: type=gha,scope=backend
69 cache-to: type=gha,scope=backend,mode=max
70
71 - name: Build and push frontend
72 uses: docker/build-push-action@v5
73 with:
74 context: ./frontend
75 push: true
76 tags: |
77 ${{ env.FRONTEND_IMAGE }}:latest
78 ${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
79 cache-from: type=gha,scope=frontend
80 cache-to: type=gha,scope=frontend,mode=max
81
82 # ── CD part 2: deploy to k3s cluster ──────────────────────────────────────
83 deploy:
84 name: Deploy to Kubernetes
85 runs-on: ubuntu-latest
86 needs: push
87 steps:
88 - uses: actions/checkout@v4
89
90 - name: Set up kubectl
91 uses: azure/setup-kubectl@v3
92 with:
93 version: latest
94
95 - name: Configure kubeconfig
96 run: |
97 echo "${{ secrets.KUBECONFIG_B64 }}" | base64 -d > $HOME/kubeconfig.yaml
98 chmod 600 $HOME/kubeconfig.yaml
99 echo "KUBECONFIG=$HOME/kubeconfig.yaml" >> $GITHUB_ENV
100
101 - name: Apply namespace
102 run: kubectl apply -f k8s/namespace.yaml
103
104 - name: Create / update app secrets
105 run: |
106 kubectl create secret generic trekr-secrets \
107 --namespace=trekr \
108 --from-literal=db-password=${{ secrets.DB_PASSWORD }} \
109 --from-literal=jwt-secret=${{ secrets.JWT_SECRET }} \
110 --save-config \
111 --dry-run=client -o yaml | kubectl apply -f -
112
113 - name: Create DB init ConfigMap from DDL
114 run: |
115 kubectl create configmap trekr-db-init \
116 --namespace=trekr \
117 --from-file=01-ddl.sql=db-scripts/ddl.sql \
118 --save-config \
119 --dry-run=client -o yaml | kubectl apply -f -
120
121 - name: Apply Postgres manifests
122 run: kubectl apply -f k8s/postgres.yaml
123
124 - name: Wait for Postgres to be ready
125 run: kubectl rollout status statefulset/postgres -n trekr --timeout=120s
126
127 - name: Apply backend manifests
128 run: kubectl apply -f k8s/backend.yaml
129
130 - name: Apply frontend manifests
131 run: kubectl apply -f k8s/frontend.yaml
132
133 - name: Roll out new images
134 run: |
135 kubectl set image deployment/backend \
136 backend=${{ env.BACKEND_IMAGE }}:${{ github.sha }} \
137 -n trekr
138 kubectl set image deployment/frontend \
139 frontend=${{ env.FRONTEND_IMAGE }}:${{ github.sha }} \
140 -n trekr
141
142 - name: Wait for rollout to complete
143 run: |
144 kubectl rollout status deployment/backend -n trekr --timeout=180s
145 kubectl rollout status deployment/frontend -n trekr --timeout=60s
146
147 - name: Print app URL
148 run: |
149 NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}' 2>/dev/null || \
150 kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
151 echo "App available at: http://${NODE_IP}:30080"
Note: See TracBrowser for help on using the repository browser.