Create sonarqube.yml #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| pull_request: | |
| branches: | |
| - main | |
| - master | |
| env: | |
| PROJECT_ID: devops-toolchain-456502 | |
| GKE_CLUSTER: devops-toolchain-cluster | |
| GKE_ZONE: us-central1 | |
| REGISTRY: us-central1-docker.pkg.dev/devops-toolchain-456502/devops-toolchain | |
| jobs: | |
| build-test-deploy: | |
| name: Build, Test, and Deploy | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| checks: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # Authenticate to Google Cloud | |
| - id: 'auth' | |
| name: 'Authenticate to Google Cloud' | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| credentials_json: '${{ secrets.GCP_SA_KEY }}' | |
| token_format: 'access_token' | |
| project_id: '${{ env.PROJECT_ID }}' | |
| service_account: 'github-actions@devops-toolchain-456502.iam.gserviceaccount.com' | |
| # Setup gcloud CLI | |
| - name: Set up Cloud SDK | |
| uses: google-github-actions/setup-gcloud@v2 | |
| # Install kubectl | |
| - name: Install kubectl | |
| run: | | |
| gcloud components install kubectl --quiet | |
| # Configure Docker for Artifact Registry | |
| - name: Configure Docker | |
| run: | | |
| gcloud auth configure-docker us-central1-docker.pkg.dev --quiet | |
| # Add a debugging step to list files | |
| - name: List files | |
| run: ls -la | |
| - name: Check Dockerfile exists | |
| run: | | |
| if [ ! -f Dockerfile ]; then | |
| echo "Error: Dockerfile not found" | |
| exit 1 | |
| fi | |
| # Build Stage with fixed tag format | |
| - name: Build Docker Image | |
| env: | |
| IMAGE_TAG: ${{ github.sha }} | |
| run: | | |
| # Validate environment variables | |
| echo "Building image for registry: $REGISTRY" | |
| echo "Using tag: $IMAGE_TAG" | |
| # Build with proper tag format | |
| docker build \ | |
| --build-arg NODE_ENV=production \ | |
| -t "${REGISTRY}/app:${IMAGE_TAG}" \ | |
| -t "${REGISTRY}/app:latest" \ | |
| . | |
| # Verify the build | |
| docker images "${REGISTRY}/app" | |
| # Test Stage | |
| - name: Run Tests | |
| env: | |
| IMAGE_TAG: ${{ github.sha }} | |
| run: | | |
| echo "Running basic tests..." | |
| docker run --rm "${REGISTRY}/app:${IMAGE_TAG}" npm test || exit 1 | |
| # Security Scan Stage | |
| - name: Run Security Scan | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: '${{ env.REGISTRY }}/app:${{ github.sha }}' | |
| format: 'table' | |
| exit-code: '1' | |
| ignore-unfixed: true | |
| severity: 'CRITICAL,HIGH' | |
| continue-on-error: true | |
| # Push Image Stage with error handling | |
| - name: Push docker Image | |
| run: | | |
| REGISTRY_LOWERCASE=$(echo "$REGISTRY" | tr '[:upper:]' '[:lower:]') | |
| if ! docker push $REGISTRY_LOWERCASE/app:$GITHUB_SHA; then | |
| echo "Failed to push image" | |
| exit 1 | |
| fi | |
| docker push $REGISTRY/app:latest | |
| # Get GKE Credentials with error handling | |
| - name: Get GKE Credentials | |
| run: | | |
| if ! gcloud container clusters get-credentials $GKE_CLUSTER --region $GKE_ZONE --project $PROJECT_ID; then | |
| echo "Failed to get cluster credentials" | |
| exit 1 | |
| fi | |
| # Deploy to Dev Stage | |
| - name: Deploy to Dev | |
| run: | | |
| kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f - | |
| cat <<EOF | kubectl apply -f - | |
| apiVersion: apps/v1 | |
| kind: Deployment | |
| metadata: | |
| name: app | |
| namespace: dev | |
| spec: | |
| replicas: 1 | |
| selector: | |
| matchLabels: | |
| app: devops-toolchain | |
| template: | |
| metadata: | |
| labels: | |
| app: devops-toolchain | |
| spec: | |
| containers: | |
| - name: app | |
| image: $REGISTRY/app:$GITHUB_SHA | |
| ports: | |
| - containerPort: 3000 | |
| --- | |
| apiVersion: v1 | |
| kind: Service | |
| metadata: | |
| name: app-service | |
| namespace: dev | |
| spec: | |
| type: ClusterIP | |
| ports: | |
| - port: 80 | |
| targetPort: 3000 | |
| selector: | |
| app: devops-toolchain | |
| EOF | |
| # Commented out Test Reporting | |
| # - name: Publish Test Results | |
| # uses: EnricoMi/publish-unit-test-result-action@v2 | |
| # if: always() | |
| # with: | |
| # files: "test-results/*.xml" # Updated path | |
| # check_name: "Test Results" | |
| # comment_mode: off | |
| # github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # Add this job-level monitoring | |
| - name: Setup Pipeline Monitoring | |
| run: | | |
| cat <<EOF > /tmp/pipeline_metrics | |
| # HELP pipeline_duration_seconds Duration of pipeline execution | |
| # TYPE pipeline_duration_seconds gauge | |
| # HELP pipeline_status Pipeline execution status (0=failed, 1=success) | |
| # TYPE pipeline_status gauge | |
| EOF | |
| # Add after each major stage (build, test, deploy) | |
| - name: Export Stage Metrics | |
| if: always() | |
| run: | | |
| STAGE_DURATION=$SECONDS | |
| echo "pipeline_stage_duration_seconds{stage=\"${{ github.job }}\",pipeline=\"${{ github.workflow }}\"} $STAGE_DURATION" >> /tmp/pipeline_metrics | |
| echo "pipeline_status{stage=\"${{ github.job }}\",status=\"${{ job.status }}\"} $([ "${{ job.status }}" = "success" ] && echo 1 || echo 0)" >> /tmp/pipeline_metrics | |
| # Add a debugging step to print the metrics file | |
| - name: Debug Pipeline Metrics | |
| run: | | |
| cat /tmp/pipeline_metrics | |
| # Add at the end of the workflow | |
| - name: Push Pipeline Metrics | |
| if: ${{ env.SKIP_METRICS != 'true' }} | |
| run: | | |
| curl -X POST http://34.60.241.53:9091/metrics/job/github_actions \ | |
| --data-binary "@/tmp/pipeline_metrics" | |
| # SonarQube Scan | |
| - name: SonarQube Scan | |
| uses: SonarSource/sonarcloud-github-action@v1 | |
| with: | |
| sonar-projectKey: ${{ secrets.SONAR_PROJECT_KEY }} | |
| sonar-token: ${{ secrets.SONAR_TOKEN }} | |
| deploy-prod: | |
| name: Deploy to Production | |
| needs: build-test-deploy | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: production | |
| url: ${{ steps.get_url.outputs.url }} | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # Authenticate to Google Cloud | |
| - id: 'auth' | |
| name: 'Authenticate to Google Cloud' | |
| uses: 'google-github-actions/auth@v2' | |
| with: | |
| credentials_json: '${{ secrets.GCP_SA_KEY }}' | |
| token_format: 'access_token' | |
| # Install kubectl | |
| - name: Install kubectl | |
| run: | | |
| gcloud components install kubectl | |
| gcloud components install gke-gcloud-auth-plugin | |
| # Get GKE Credentials with error handling | |
| - name: Get GKE Credentials | |
| run: | | |
| if ! gcloud container clusters get-credentials $GKE_CLUSTER --region $GKE_ZONE --project $PROJECT_ID; then | |
| echo "Failed to get cluster credentials" | |
| exit 1 | |
| fi | |
| # Deploy to Prod Stage | |
| - name: Deploy to Production | |
| run: | | |
| kubectl create namespace prod --dry-run=client -o yaml | kubectl apply -f - | |
| cat <<EOF | kubectl apply -f - | |
| apiVersion: apps/v1 | |
| kind: Deployment | |
| metadata: | |
| name: app | |
| namespace: prod | |
| spec: | |
| replicas: 2 | |
| selector: | |
| matchLabels: | |
| app: devops-toolchain | |
| template: | |
| metadata: | |
| labels: | |
| app: devops-toolchain | |
| spec: | |
| containers: | |
| - name: app | |
| image: $REGISTRY/app:$GITHUB_SHA | |
| ports: | |
| - containerPort: 3000 | |
| resources: | |
| requests: | |
| memory: "128Mi" | |
| cpu: "100m" | |
| limits: | |
| memory: "256Mi" | |
| cpu: "200m" | |
| --- | |
| apiVersion: v1 | |
| kind: Service | |
| metadata: | |
| name: app-service | |
| namespace: prod | |
| spec: | |
| type: LoadBalancer | |
| ports: | |
| - port: 80 | |
| targetPort: 3000 | |
| selector: | |
| app: devops-toolchain | |
| EOF | |
| # Get Service URL | |
| - name: Get Service URL | |
| id: get_url | |
| run: | | |
| PROD_URL=$(kubectl get service app-service -n prod -o jsonpath='{.status.loadBalancer.ingress[0].ip}') | |
| echo "url=https://${PROD_URL}" >> $GITHUB_OUTPUT |