Fix CI/CD pipeline: Correct dependency versions, switch to uv, and op… #2
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: Production ML Pipeline (Auto-Train & Deploy) | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'Scripts/**' | |
| - 'Data/**' | |
| - 'api/**' | |
| workflow_dispatch: | |
| # Prevent parallel pipeline runs on the self-hosted runner | |
| concurrency: | |
| group: ml-pipeline-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| train-and-deploy: | |
| runs-on: self-hosted | |
| timeout-minutes: 60 | |
| defaults: | |
| run: | |
| shell: powershell | |
| env: | |
| MLFLOW_TRACKING_URI: sqlite:///D:/ML 101/ML_101_BootCamp/mlflow.db | |
| MLFLOW_EXPERIMENT_NAME: customer_churn_optimization | |
| MODEL_NAME: customer_churn_model | |
| PYTHONIOENCODING: utf-8 | |
| PYTHONUTF8: 1 | |
| CI: true | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup environment | |
| run: | | |
| if (!(Test-Path ".venv")) { | |
| python -m venv .venv | |
| } | |
| & ".venv\Scripts\Activate.ps1" | |
| python -m pip install -r requirements.txt --quiet | |
| - name: Execute Model Training | |
| id: training | |
| run: | | |
| & ".venv\Scripts\Activate.ps1" | |
| python Scripts/model_training.py | |
| - name: Production Quality Gate | |
| run: | | |
| & ".venv\Scripts\Activate.ps1" | |
| Write-Host "Verifying Production Quality Gate (F1 >= 0.75)..." | |
| # Use a python script to check the latest model's F1 from production_models metadata | |
| python -c @" | |
| import os | |
| import sys | |
| import re | |
| prod_dir = r'mlruns\production_models' | |
| if not os.path.exists(prod_dir): | |
| print('❌ Quality Gate Failed: Production folder not found.') | |
| sys.exit(1) | |
| # Find most recent promoted model metadata | |
| metadata_files = [] | |
| for root, dirs, files in os.walk(prod_dir): | |
| for f in files: | |
| if f == 'production_metadata.txt': | |
| metadata_files.append(os.path.join(root, f)) | |
| if not metadata_files: | |
| print('❌ Quality Gate Failed: No production metadata found.') | |
| sys.exit(1) | |
| # Get the absolute most recent file | |
| latest_meta = max(metadata_files, key=os.path.getmtime) | |
| print(f'Latest Metadata found: {latest_meta}') | |
| with open(latest_meta, 'r') as f: | |
| content = f.read() | |
| print(f'Content:\n{content}') | |
| # Extract F1 score if present in metadata | |
| f1_match = re.search(r'f1_score: ([\d.]+)', content) | |
| if f1_match: | |
| f1_val = float(f1_match.group(1)) | |
| print(f'Detected F1 Score: {f1_val}') | |
| if f1_val < 0.75: | |
| print(f'❌ Quality Gate Failed: F1 Score {f1_val} is below threshold 0.75') | |
| sys.exit(1) | |
| # Check if a model was actually promoted | |
| if 'model_name:' in content and 'version:' in content: | |
| print('✅ Quality Gate Passed: Model successfully promoted to Production.') | |
| else: | |
| print('❌ Quality Gate Failed: Metadata does not indicate a promoted model.') | |
| sys.exit(1) | |
| "@ | |
| Write-Host "✅ Quality Gate Verified." | |
| - name: Hot-Reload API Service | |
| run: | | |
| Write-Host "Deploying new model to API..." | |
| powershell -ExecutionPolicy Bypass -File "./api/restart_service.ps1" | |
| Write-Host "✅ Deployment Complete." | |
| - name: Final Summary | |
| if: always() | |
| run: | | |
| "# Production Deployment Report" >> $env:GITHUB_STEP_SUMMARY | |
| "Status: ${{ job.status == 'success' && '✅ SUCCESS' || '❌ FAILED' }}" >> $env:GITHUB_STEP_SUMMARY | |
| "Pipeline completed on self-hosted runner." >> $env:GITHUB_STEP_SUMMARY |