name: CI/CD Pipeline - RAG Service on: push: branches: - main - develop paths: - 'provisioning/platform/rag/**' - '.github/workflows/ci-cd.yml' pull_request: branches: - main paths: - 'provisioning/platform/rag/**' env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}/provisioning-rag RUST_VERSION: 1.80.1 jobs: # ============================================================================ # LINTING AND FORMATTING # ============================================================================ lint: name: Lint and Format Check runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: provisioning/platform/rag - name: Run Clippy run: | cd provisioning/platform/rag cargo clippy --all-targets --all-features -- -D warnings - name: Check formatting run: | cd provisioning/platform/rag cargo fmt -- --check # ============================================================================ # UNIT TESTS # ============================================================================ test: name: Unit Tests runs-on: ubuntu-latest needs: lint steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_VERSION }} - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: provisioning/platform/rag - name: Run tests run: | cd provisioning/platform/rag cargo test --lib --doc - name: Run integration tests run: | cd provisioning/platform/rag cargo test --test integration_tests - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: test-results path: provisioning/platform/rag/target/debug/deps/ # ============================================================================ # BENCHMARKING # ============================================================================ benchmark: name: Performance Benchmarks runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_VERSION }} - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: provisioning/platform/rag - name: Run benchmarks run: | cd provisioning/platform/rag cargo bench --no-run cargo bench -- --output-format bencher | tee output.txt - name: Store benchmark results uses: benchmark-action/github-action-benchmark@v1 with: name: Provisioning RAG Benchmarks tool: 'cargo' output-file-path: provisioning/platform/rag/output.txt github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true - name: Upload benchmark results if: always() uses: actions/upload-artifact@v3 with: name: benchmark-results path: provisioning/platform/rag/target/criterion/ # ============================================================================ # SECURITY SCANNING # ============================================================================ security: name: Security Scanning runs-on: ubuntu-latest needs: lint steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_VERSION }} - name: Install cargo-audit run: cargo install cargo-audit - name: Audit dependencies run: | cd provisioning/platform/rag cargo audit - name: Run cargo-deny uses: EmbarkStudios/cargo-deny-action@v1 with: log-level: warn command: check advisories # ============================================================================ # BUILD DOCKER IMAGE # ============================================================================ build: name: Build Docker Image runs-on: ubuntu-latest needs: [test, benchmark, security] if: github.event_name == 'push' && github.ref == 'refs/heads/main' permissions: contents: read packages: write outputs: image-tag: ${{ steps.meta.outputs.tags }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Log in to Container Registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v4 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix={{branch}}- type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image uses: docker/build-push-action@v4 with: context: . file: provisioning/platform/rag/docker/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max # ============================================================================ # DEPLOY TO STAGING # ============================================================================ deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest needs: build if: github.event_name == 'push' && github.ref == 'refs/heads/main' environment: name: staging url: https://rag-staging.example.com steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up kubectl uses: azure/setup-kubectl@v3 with: version: v1.27.0 - name: Configure kubectl run: | mkdir -p $HOME/.kube echo "${{ secrets.KUBECONFIG_STAGING }}" | base64 -d > $HOME/.kube/config chmod 600 $HOME/.kube/config - name: Deploy to Kubernetes run: | cd provisioning/platform/rag/k8s kubectl apply -f 00-namespace.yaml kubectl apply -f 01-configmap.yaml kubectl apply -f 02-secrets.yaml kubectl apply -f 03-storage.yaml kubectl apply -f 04-deployment.yaml kubectl apply -f 05-service.yaml kubectl apply -f 06-hpa-ingress.yaml kubectl apply -f 07-rbac.yaml kubectl rollout status deployment/provisioning-rag -n provisioning-rag --timeout=5m - name: Verify deployment health run: | for i in {1..30}; do if curl -f https://rag-staging.example.com/health; then echo "✓ Deployment healthy" exit 0 fi echo "Waiting for service to be healthy... ($i/30)" sleep 10 done echo "✗ Service failed to become healthy" exit 1 - name: Run smoke tests run: | cd provisioning/platform/rag curl -f https://rag-staging.example.com/health curl -f https://rag-staging.example.com/tasks echo "✓ Smoke tests passed" # ============================================================================ # DEPLOY TO PRODUCTION # ============================================================================ deploy-production: name: Deploy to Production runs-on: ubuntu-latest needs: deploy-staging if: github.event_name == 'push' && github.ref == 'refs/heads/main' environment: name: production url: https://rag.example.com steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up kubectl uses: azure/setup-kubectl@v3 with: version: v1.27.0 - name: Configure kubectl run: | mkdir -p $HOME/.kube echo "${{ secrets.KUBECONFIG_PRODUCTION }}" | base64 -d > $HOME/.kube/config chmod 600 $HOME/.kube/config - name: Create backup of current state run: | cd provisioning/platform/rag/k8s kubectl get deployment provisioning-rag -n provisioning-rag -o yaml > /tmp/deployment-backup.yaml echo "Backup saved for rollback if needed" - name: Deploy to Kubernetes run: | cd provisioning/platform/rag/k8s kubectl apply -f 00-namespace.yaml kubectl apply -f 01-configmap.yaml kubectl apply -f 02-secrets.yaml kubectl apply -f 03-storage.yaml kubectl apply -f 04-deployment.yaml kubectl apply -f 05-service.yaml kubectl apply -f 06-hpa-ingress.yaml kubectl apply -f 07-rbac.yaml kubectl rollout status deployment/provisioning-rag -n provisioning-rag --timeout=10m - name: Verify production health run: | for i in {1..30}; do if curl -f https://rag.example.com/health; then echo "✓ Production deployment healthy" exit 0 fi echo "Waiting for service to be healthy... ($i/30)" sleep 10 done echo "✗ Service failed to become healthy" exit 1 - name: Run comprehensive tests run: | cd provisioning/platform/rag curl -f https://rag.example.com/health curl -f https://rag.example.com/tasks curl -f https://rag.example.com/metrics echo "✓ Comprehensive tests passed" - name: Notify deployment if: success() run: | echo "✓ Production deployment successful" echo "Service URL: https://rag.example.com" echo "Metrics: https://rag.example.com/metrics" # ============================================================================ # ROLLBACK (Manual trigger) # ============================================================================ rollback: name: Rollback Production runs-on: ubuntu-latest if: failure() && github.event_name == 'workflow_dispatch' environment: name: production steps: - name: Set up kubectl uses: azure/setup-kubectl@v3 - name: Configure kubectl run: | mkdir -p $HOME/.kube echo "${{ secrets.KUBECONFIG_PRODUCTION }}" | base64 -d > $HOME/.kube/config chmod 600 $HOME/.kube/config - name: Rollback to previous version run: | kubectl rollout undo deployment/provisioning-rag -n provisioning-rag kubectl rollout status deployment/provisioning-rag -n provisioning-rag --timeout=5m - name: Verify rollback run: | curl -f https://rag.example.com/health echo "✓ Rollback successful" # ============================================================================ # NOTIFICATION # ============================================================================ notify: name: Notification runs-on: ubuntu-latest if: always() needs: [lint, test, benchmark, security, build, deploy-staging, deploy-production] steps: - name: Determine status id: status run: | if [ "${{ job.status }}" == "success" ]; then echo "status=✓ Success" >> $GITHUB_OUTPUT echo "color=0x28a745" >> $GITHUB_OUTPUT else echo "status=✗ Failed" >> $GITHUB_OUTPUT echo "color=0xdc3545" >> $GITHUB_OUTPUT fi - name: Slack Notification uses: slackapi/slack-github-action@v1 with: webhook-url: ${{ secrets.SLACK_WEBHOOK }} payload: | { "text": "RAG Service CI/CD Pipeline ${{ steps.status.outputs.status }}", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "RAG Service CI/CD Pipeline\n*Status:* ${{ steps.status.outputs.status }}\n*Commit:* ${{ github.sha }}\n*Branch:* ${{ github.ref_name }}" } } ] }