Skip to content

Commit 7c9fab0

Browse files
committed
2
1 parent a946e44 commit 7c9fab0

File tree

1 file changed

+245
-18
lines changed

1 file changed

+245
-18
lines changed

.github/workflows/ci-cd.yaml

Lines changed: 245 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ env:
1616
REGISTRY: us-central1-docker.pkg.dev/devops-toolchain-456502/devops-toolchain
1717

1818
jobs:
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

Comments
 (0)