Skip to content

Task 1 Fully Complete #2

Task 1 Fully Complete

Task 1 Fully Complete #2

Workflow file for this run

name: ProdSegmenter CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch:
env:
PYTHON_VERSION: '3.10'
AZURE_FUNCTIONAPP_NAME: 'func-prodsegmenter-inference'
AZURE_WEBAPP_NAME: 'app-prodsegmenter-frontend'
jobs:
# Code Quality and Linting
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install black flake8 mypy
pip install -r requirements.txt
- name: Run Black formatter check
run: black --check --diff .
- name: Run Flake8 linter
run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
- name: Run MyPy type checker
run: mypy training/ deployment/ --ignore-missing-imports
# Unit and Integration Tests
test:
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov pytest-mock pytest-xdist
pip install -r requirements.txt
- name: Run unit tests
run: |
pytest tests/ -v --cov=training --cov=deployment \
--cov-report=xml --cov-report=html \
--junitxml=test-results.xml \
-m "not slow and not integration"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.python-version }}
path: |
test-results.xml
htmlcov/
# Integration Tests (with Azure services mocked)
integration-test:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-mock
pip install -r requirements.txt
- name: Run integration tests
run: |
pytest tests/integration/ -v \
--junitxml=integration-test-results.xml \
-m "integration"
- name: Upload integration test results
uses: actions/upload-artifact@v3
if: always()
with:
name: integration-test-results
path: integration-test-results.xml
# Security Scanning
security:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install security tools
run: |
python -m pip install --upgrade pip
pip install bandit safety
pip install -r requirements.txt
- name: Run Bandit security linter
run: bandit -r training/ deployment/ -f json -o bandit-report.json
continue-on-error: true
- name: Run Safety vulnerability check
run: safety check --json --output safety-report.json
continue-on-error: true
- name: Upload security reports
uses: actions/upload-artifact@v3
if: always()
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
# Build Docker Images
build-images:
runs-on: ubuntu-latest
needs: [test, integration-test]
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Azure Container Registry
uses: azure/docker-login@v1
with:
login-server: ${{ secrets.AZURE_CONTAINER_REGISTRY }}
username: ${{ secrets.AZURE_ACR_USERNAME }}
password: ${{ secrets.AZURE_ACR_PASSWORD }}
- name: Build and push Azure Function image
uses: docker/build-push-action@v4
with:
context: ./deployment/azure_function
push: true
tags: |
${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-api:latest
${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push Streamlit frontend image
uses: docker/build-push-action@v4
with:
context: ./deployment/streamlit_frontend
push: true
tags: |
${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-frontend:latest
${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-frontend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Deploy to Development Environment
deploy-dev:
runs-on: ubuntu-latest
needs: build-images
if: github.ref == 'refs/heads/develop'
environment: development
steps:
- uses: actions/checkout@v3
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy Azure Function (Dev)
uses: Azure/functions-action@v1
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}-dev
package: './deployment/azure_function'
respect-funcignore: true
- name: Deploy Streamlit App (Dev)
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}-dev
images: ${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-frontend:${{ github.sha }}
- name: Run smoke tests
run: |
# Wait for deployment to be ready
sleep 60
# Test function app health
curl -f https://${{ env.AZURE_FUNCTIONAPP_NAME }}-dev.azurewebsites.net/api/health
# Test web app health
curl -f https://${{ env.AZURE_WEBAPP_NAME }}-dev.azurewebsites.net
# Deploy to Production Environment
deploy-prod:
runs-on: ubuntu-latest
needs: [build-images, deploy-dev]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v3
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy Azure Function (Prod)
uses: Azure/functions-action@v1
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: './deployment/azure_function'
respect-funcignore: true
- name: Deploy Streamlit App (Prod)
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
images: ${{ secrets.AZURE_CONTAINER_REGISTRY }}/prodsegmenter-frontend:${{ github.sha }}
- name: Run production smoke tests
run: |
# Wait for deployment to be ready
sleep 60
# Test function app health
curl -f https://${{ env.AZURE_FUNCTIONAPP_NAME }}.azurewebsites.net/api/health
# Test web app health
curl -f https://${{ env.AZURE_WEBAPP_NAME }}.azurewebsites.net
- name: Create GitHub Release
if: success()
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ github.run_number }}
release_name: Release v${{ github.run_number }}
body: |
Automated release for commit ${{ github.sha }}
Changes in this release:
- See commit history for details
Deployment:
- Azure Function: ${{ env.AZURE_FUNCTIONAPP_NAME }}
- Streamlit App: ${{ env.AZURE_WEBAPP_NAME }}
draft: false
prerelease: false
# Model Training Job (Triggered separately)
train-model:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch'
environment: production
steps:
- uses: actions/checkout@v3
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Azure ML CLI
run: |
az extension add -n ml -y
az extension update -n ml
- name: Submit training job
run: |
az ml job create --file .azure/training-job.yml \
--workspace-name ml-prodsegmenter \
--resource-group rg-prodsegmenter
- name: Monitor training job
run: |
# Get job name from submission
JOB_NAME=$(az ml job list --workspace-name ml-prodsegmenter \
--resource-group rg-prodsegmenter \
--query "[0].name" -o tsv)
# Wait for completion (max 4 hours)
az ml job show --name $JOB_NAME \
--workspace-name ml-prodsegmenter \
--resource-group rg-prodsegmenter \
--query "status"
# Performance Testing
performance-test:
runs-on: ubuntu-latest
needs: deploy-prod
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install load testing tools
run: |
pip install locust requests
- name: Run performance tests
run: |
cd tests/performance
locust -f load_test.py \
--host https://${{ env.AZURE_FUNCTIONAPP_NAME }}.azurewebsites.net \
--users 50 \
--spawn-rate 5 \
--run-time 300s \
--headless \
--html performance-report.html
- name: Upload performance report
uses: actions/upload-artifact@v3
with:
name: performance-report
path: tests/performance/performance-report.html
# Cleanup
cleanup:
runs-on: ubuntu-latest
needs: [deploy-prod, performance-test]
if: always()
steps:
- name: Clean up old container images
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Delete old images from ACR
run: |
# Keep only last 10 images
az acr repository show-manifests \
--name ${{ secrets.AZURE_CONTAINER_REGISTRY }} \
--repository prodsegmenter-api \
--orderby time_desc \
--query "[10:].digest" -o tsv | \
xargs -I {} az acr repository delete \
--name ${{ secrets.AZURE_CONTAINER_REGISTRY }} \
--image prodsegmenter-api@{} --yes