name: CI/CD Pipeline - Build, Test, and Deploy on: push: branches: [main, master, develop] pull_request: branches: [main, master] workflow_dispatch: env: NODE_VERSION: '18' REGISTRY: ${{ secrets.HARBOR_REGISTRY }} IMAGE_NAME: infrastructure/monitoring-dashboard jobs: # Job 1: Lint and Test # test: # name: ๐Ÿงช Test & Lint # runs-on: ubuntu-latest # steps: # - name: Checkout code # uses: actions/checkout@v4 # - name: Setup Node.js # uses: actions/setup-node@v4 # with: # node-version: ${{ env.NODE_VERSION }} # cache: 'npm' # - name: Install dependencies # run: npm ci # - name: Run linting # run: npm run lint # - name: Run tests # run: npm run test:coverage # - name: Upload test results # uses: actions/upload-artifact@v3 # if: always() # with: # name: test-results # path: | # coverage/ # test-results.xml # # Job 2: Security Scan # security: # name: ๐Ÿ”’ Security Scan # runs-on: ubuntu-latest # steps: # - name: Checkout code # uses: actions/checkout@v4 # - name: Setup Node.js # uses: actions/setup-node@v4 # with: # node-version: ${{ env.NODE_VERSION }} # cache: 'npm' # - name: Install dependencies # run: npm ci # - name: Run security audit # run: npm audit --audit-level=high # - name: Check for vulnerabilities # run: | # if npm audit --audit-level=moderate --json | jq '.vulnerabilities | length' | grep -v '^0$'; then # echo "Vulnerabilities found!" # npm audit --audit-level=moderate # exit 1 # fi # Job 3: Build and Push Docker Image build: name: ๐Ÿ—๏ธ Build & Push Image runs-on: ubuntu-latest # needs: [test, security] if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' # outputs: # image-tag: ${{ steps.meta.outputs.tags }} # image-digest: ${{ steps.build.outputs.digest }} steps: # - name: Checkout code # uses: actions/checkout@v4 # - name: Set up Docker Buildx # uses: docker/setup-buildx-action@v3 # - name: Login to Harbor Registry # uses: docker/login-action@v3 # with: # registry: ${{ env.REGISTRY }} # username: ${{ secrets.HARBOR_USERNAME }} # password: ${{ secrets.HARBOR_TOKEN }} # - name: Extract metadata # id: meta # uses: docker/metadata-action@v5 # with: # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # tags: | # type=ref,event=branch # type=ref,event=pr # type=sha,prefix={{branch}}- # type=raw,value=latest,enable={{is_default_branch}} # type=raw,value={{date 'YYYYMMDD-HHmmss'}} # - name: Build and push Docker image # id: build # uses: docker/build-push-action@v5 # with: # context: . # platforms: linux/amd64,linux/arm64 # push: true # tags: ${{ steps.meta.outputs.tags }} # labels: ${{ steps.meta.outputs.labels }} # cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache # cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max - name: Generate SBOM run: | # Install syft curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin # Login to registry - use the REGISTRY variable for the URL echo "${{ secrets.HARBOR_TOKEN }}" | docker login ${{ env.REGISTRY }} -u '${{ secrets.HARBOR_USERNAME }}' --password-stdin # Generate SBOM using latest tag syft ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest -o spdx-json > sbom.spdx.json # Verify SBOM was created if [ ! -f sbom.spdx.json ]; then echo "Failed to generate SBOM" exit 1 fi echo "SBOM generated successfully" - name: Install ORAS CLI run: | ORAS_VERSION="1.1.0" curl -LO https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz tar -xzf oras_${ORAS_VERSION}_linux_amd64.tar.gz oras chmod +x oras mv oras /usr/local/bin/oras - name: Upload SBOM to Harbor via ORAS run: | IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" # Authenticate (ensure $HARBOR_USERNAME and $HARBOR_TOKEN are exported already) echo "${{ secrets.HARBOR_TOKEN }}" | oras login ${{ env.REGISTRY }} -u '${{ secrets.HARBOR_USERNAME }}' --password-stdin # Push the SBOM attached to the image oras push $IMAGE \ --artifact-type application/spdx+json \ --subject $IMAGE \ sbom.spdx.json:application/spdx+json # Job 4: Image Security Scan # scan: # name: ๐Ÿ›ก๏ธ Image Security Scan # runs-on: ubuntu-latest # needs: build # if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' # steps: # - name: Login to Harbor Registry # uses: docker/login-action@v3 # with: # registry: ${{ env.REGISTRY }} # username: ${{ secrets.HARBOR_USERNAME }} # password: ${{ secrets.HARBOR_TOKEN }} # - name: Run Trivy vulnerability scanner # uses: aquasecurity/trivy-action@master # with: # image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} # format: 'sarif' # output: 'trivy-results.sarif' # - name: Upload Trivy scan results # uses: actions/upload-artifact@v3 # with: # name: trivy-scan-results # path: trivy-results.sarif # - name: Check for HIGH/CRITICAL vulnerabilities # uses: aquasecurity/trivy-action@master # with: # image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} # format: 'json' # output: 'trivy-results.json' # exit-code: '1' # severity: 'HIGH,CRITICAL' # # Job 5: Deploy to Development # deploy-dev: # name: ๐Ÿš€ Deploy to Development # runs-on: ubuntu-latest # needs: [build, scan] # if: github.ref == 'refs/heads/develop' # environment: development # steps: # - name: Deploy to development environment # run: | # echo "๐Ÿš€ Deploying to development environment" # echo "Image: ${{ needs.build.outputs.image-tag }}" # echo "Digest: ${{ needs.build.outputs.image-digest }}" # # Add actual deployment commands here # # For example: kubectl, docker-compose, or API calls # # Job 6: Deploy to Production # deploy-prod: # name: ๐Ÿญ Deploy to Production # runs-on: ubuntu-latest # needs: [build, scan] # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' # environment: production # steps: # - name: Checkout code # uses: actions/checkout@v4 # - name: Deploy to production # run: | # echo "๐Ÿญ Deploying to production environment" # echo "Image: ${{ needs.build.outputs.image-tag }}" # echo "Digest: ${{ needs.build.outputs.image-digest }}" # # Example deployment script # # In a real scenario, you might: # # 1. SSH to your server # # 2. Pull the new image # # 3. Update docker-compose.yml # # 4. Restart the service # # 5. Run health checks # - name: Health check after deployment # run: | # echo "๐Ÿ” Running post-deployment health checks" # # Add health check commands here # # curl -f http://your-app-url/health || exit 1 # - name: Notify deployment success # run: | # echo "โœ… Deployment completed successfully!" # echo "๐ŸŒ Application URL: https://your-domain.com" # echo "๐Ÿ“Š Monitoring: https://your-domain.com/health/detailed" # Job 7: Cleanup cleanup: name: ๐Ÿงน Cleanup runs-on: ubuntu-latest needs: [deploy-dev, deploy-prod] if: always() steps: - name: Clean up old images run: | echo "๐Ÿงน Cleaning up old container images" # Add cleanup logic here # For example, remove images older than 30 days