1616 REGISTRY : us-central1-docker.pkg.dev/devops-toolchain-456502/devops-toolchain
1717
1818jobs :
19- build-and -deploy :
20- name : Build and Deploy
19+ build-test -deploy :
20+ name : Build, Test, and Deploy
2121 runs-on : ubuntu-latest
2222 permissions :
2323 contents : read
@@ -28,28 +28,81 @@ jobs:
2828 - name : Checkout code
2929 uses : actions/checkout@v4
3030
31- # Set up Google Cloud SDK and kubectl
32- - name : Set up Google Cloud SDK
33- uses : google-github-actions/setup-gcloud@v2
31+ # Authenticate to Google Cloud
32+ - id : ' auth'
33+ name : ' Authenticate to Google Cloud'
34+ uses : ' google-github-actions/auth@v2'
3435 with :
35- project_id : ${{ env.PROJECT_ID }}
36- service_account_key : ${{ secrets.GCP_SA_KEY }}
37- export_default_credentials : true
36+ credentials_json : ' ${{ secrets.GCP_SA_KEY }}'
37+ token_format : ' access_token'
38+ project_id : ' ${{ env.PROJECT_ID }}'
39+ service_account : ' github-actions@devops-toolchain-456502.iam.gserviceaccount.com'
3840
39- # Get GKE Credentials
40- - name : Get GKE Credentials
41+ # Setup gcloud CLI
42+ - name : Set up Cloud SDK
43+ uses : google-github-actions/setup-gcloud@v2
44+
45+ # Install kubectl
46+ - name : Install kubectl
4147 run : |
42- gcloud container clusters get-credentials $GKE_CLUSTER --region $GKE_ZONE --project $PROJECT_ID
48+ gcloud components install kubectl --quiet
4349
44- # Build Docker Image
50+ # Configure Docker for Artifact Registry
51+ - name : Configure Docker
52+ run : |
53+ gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
54+
55+ # Add a debugging step to list files
56+ - name : List files
57+ run : ls -la
58+
59+ - name : Check Dockerfile exists
60+ run : |
61+ if [ ! -f Dockerfile ]; then
62+ echo "Error: Dockerfile not found"
63+ exit 1
64+ fi
65+
66+ # Build Stage with fixed tag format
4567 - name : Build Docker Image
4668 env :
4769 IMAGE_TAG : ${{ github.sha }}
4870 run : |
49- docker build -t "${REGISTRY}/app:${IMAGE_TAG}" -t "${REGISTRY}/app:latest" .
71+ # Validate environment variables
72+ echo "Building image for registry: $REGISTRY"
73+ echo "Using tag: $IMAGE_TAG"
74+
75+ # Build with proper tag format
76+ docker build \
77+ --build-arg NODE_ENV=production \
78+ -t "${REGISTRY}/app:${IMAGE_TAG}" \
79+ -t "${REGISTRY}/app:latest" \
80+ .
81+
82+ # Verify the build
83+ docker images "${REGISTRY}/app"
84+
85+ # Test Stage
86+ - name : Run Tests
87+ env :
88+ IMAGE_TAG : ${{ github.sha }}
89+ run : |
90+ echo "Running basic tests..."
91+ docker run --rm "${REGISTRY}/app:${IMAGE_TAG}" npm test || exit 1
92+
93+ # Security Scan Stage
94+ - name : Run Security Scan
95+ uses : aquasecurity/trivy-action@master
96+ with :
97+ image-ref : ' ${{ env.REGISTRY }}/app:${{ github.sha }}'
98+ format : ' table'
99+ exit-code : ' 1'
100+ ignore-unfixed : true
101+ severity : ' CRITICAL,HIGH'
102+ continue-on-error : true
50103
51- # Push Docker Image
52- - name : Push Docker Image
104+ # Push Image Stage with error handling
105+ - name : Push docker Image
53106 run : |
54107 REGISTRY_LOWERCASE=$(echo "$REGISTRY" | tr '[:upper:]' '[:lower:]')
55108 if ! docker push $REGISTRY_LOWERCASE/app:$GITHUB_SHA; then
@@ -58,7 +111,181 @@ jobs:
58111 fi
59112 docker push $REGISTRY/app:latest
60113
61- # Create Namespace
62- - name : Create Namespace
114+ # Get GKE Credentials with error handling
115+ - name : Get GKE Credentials
116+ run : |
117+ if ! gcloud container clusters get-credentials $GKE_CLUSTER --region $GKE_ZONE --project $PROJECT_ID; then
118+ echo "Failed to get cluster credentials"
119+ exit 1
120+ fi
121+
122+ # Deploy to Dev Stage
123+ - name : Deploy to Dev
124+ run : |
125+ kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f -
126+ cat <<EOF | kubectl apply -f -
127+ apiVersion: apps/v1
128+ kind: Deployment
129+ metadata:
130+ name: app
131+ namespace: dev
132+ spec:
133+ replicas: 1
134+ selector:
135+ matchLabels:
136+ app: devops-toolchain
137+ template:
138+ metadata:
139+ labels:
140+ app: devops-toolchain
141+ spec:
142+ containers:
143+ - name: app
144+ image: $REGISTRY/app:$GITHUB_SHA
145+ ports:
146+ - containerPort: 3000
147+ ---
148+ apiVersion: v1
149+ kind: Service
150+ metadata:
151+ name: app-service
152+ namespace: dev
153+ spec:
154+ type: ClusterIP
155+ ports:
156+ - port: 80
157+ targetPort: 3000
158+ selector:
159+ app: devops-toolchain
160+ EOF
161+
162+ # Commented out Test Reporting
163+ # - name: Publish Test Results
164+ # uses: EnricoMi/publish-unit-test-result-action@v2
165+ # if: always()
166+ # with:
167+ # files: "test-results/*.xml" # Updated path
168+ # check_name: "Test Results"
169+ # comment_mode: off
170+ # github_token: ${{ secrets.GITHUB_TOKEN }}
171+
172+ # Add this job-level monitoring
173+ - name : Setup Pipeline Monitoring
174+ run : |
175+ cat <<EOF > /tmp/pipeline_metrics
176+ # HELP pipeline_duration_seconds Duration of pipeline execution
177+ # TYPE pipeline_duration_seconds gauge
178+ # HELP pipeline_status Pipeline execution status (0=failed, 1=success)
179+ # TYPE pipeline_status gauge
180+ EOF
181+
182+ # Add after each major stage (build, test, deploy)
183+ - name : Export Stage Metrics
184+ if : always()
185+ run : |
186+ STAGE_DURATION=$SECONDS
187+ echo "pipeline_stage_duration_seconds{stage=\"${{ github.job }}\",pipeline=\"${{ github.workflow }}\"} $STAGE_DURATION" >> /tmp/pipeline_metrics
188+ echo "pipeline_status{stage=\"${{ github.job }}\",status=\"${{ job.status }}\"} $([ "${{ job.status }}" = "success" ] && echo 1 || echo 0)" >> /tmp/pipeline_metrics
189+
190+ # Add a debugging step to print the metrics file
191+ - name : Debug Pipeline Metrics
192+ run : |
193+ cat /tmp/pipeline_metrics
194+
195+ # Add at the end of the workflow
196+ - name : Push Pipeline Metrics
197+ if : ${{ env.SKIP_METRICS != 'true' }}
198+ run : |
199+ curl -X POST http://34.60.241.53:9091/metrics/job/github_actions \
200+ --data-binary "@/tmp/pipeline_metrics"
201+
202+ deploy-prod :
203+ name : Deploy to Production
204+ needs : build-test-deploy
205+ runs-on : ubuntu-latest
206+ environment :
207+ name : production
208+ url : ${{ steps.get_url.outputs.url }}
209+ permissions :
210+ contents : read
211+ id-token : write
212+
213+ steps :
214+ - name : Checkout code
215+ uses : actions/checkout@v4
216+
217+ # Authenticate to Google Cloud
218+ - id : ' auth'
219+ name : ' Authenticate to Google Cloud'
220+ uses : ' google-github-actions/auth@v2'
221+ with :
222+ credentials_json : ' ${{ secrets.GCP_SA_KEY }}'
223+ token_format : ' access_token'
224+
225+ # Install kubectl
226+ - name : Install kubectl
227+ run : |
228+ gcloud components install kubectl
229+ gcloud components install gke-gcloud-auth-plugin
230+
231+ # Get GKE Credentials with error handling
232+ - name : Get GKE Credentials
233+ run : |
234+ if ! gcloud container clusters get-credentials $GKE_CLUSTER --region $GKE_ZONE --project $PROJECT_ID; then
235+ echo "Failed to get cluster credentials"
236+ exit 1
237+ fi
238+
239+ # Deploy to Prod Stage
240+ - name : Deploy to Production
241+ run : |
242+ kubectl create namespace prod --dry-run=client -o yaml | kubectl apply -f -
243+ cat <<EOF | kubectl apply -f -
244+ apiVersion: apps/v1
245+ kind: Deployment
246+ metadata:
247+ name: app
248+ namespace: prod
249+ spec:
250+ replicas: 2
251+ selector:
252+ matchLabels:
253+ app: devops-toolchain
254+ template:
255+ metadata:
256+ labels:
257+ app: devops-toolchain
258+ spec:
259+ containers:
260+ - name: app
261+ image: $REGISTRY/app:$GITHUB_SHA
262+ ports:
263+ - containerPort: 3000
264+ resources:
265+ requests:
266+ memory: "128Mi"
267+ cpu: "100m"
268+ limits:
269+ memory: "256Mi"
270+ cpu: "200m"
271+ ---
272+ apiVersion: v1
273+ kind: Service
274+ metadata:
275+ name: app-service
276+ namespace: prod
277+ spec:
278+ type: LoadBalancer
279+ ports:
280+ - port: 80
281+ targetPort: 3000
282+ selector:
283+ app: devops-toolchain
284+ EOF
285+
286+ # Get Service URL
287+ - name : Get Service URL
288+ id : get_url
63289 run : |
64- kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f -
290+ PROD_URL=$(kubectl get service app-service -n prod -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
291+ echo "url=https://${PROD_URL}" >> $GITHUB_OUTPUT
0 commit comments