Skip to content

Fix CI/CD pipeline: Correct dependency versions, switch to uv, and op… #2

Fix CI/CD pipeline: Correct dependency versions, switch to uv, and op…

Fix CI/CD pipeline: Correct dependency versions, switch to uv, and op… #2

Workflow file for this run

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