test.yml•11.2 kB
name: Test and Deploy
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
env:
PYTHON_VERSION: '3.11'
REDIS_VERSION: '7-alpine'
jobs:
lint:
name: Code Quality Checks
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.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 flake8 black isort mypy
pip install -r requirements.txt
- name: Run Black (code formatting)
run: black --check src/ tests/
continue-on-error: true
- name: Run isort (import sorting)
run: isort --check-only src/ tests/
continue-on-error: true
- name: Run Flake8 (linting)
run: flake8 src/ tests/ --max-line-length=100 --extend-ignore=E203,W503
continue-on-error: true
- name: Run MyPy (type checking)
run: mypy src/ --ignore-missing-imports
continue-on-error: true
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-asyncio pytest-cov pytest-mock
- name: Run unit tests
run: |
pytest tests/ \
--ignore=tests/integration/ \
--cov=src \
--cov-report=xml \
--cov-report=term \
--cov-report=html \
-v
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unittests
name: codecov-${{ matrix.python-version }}
fail_ci_if_error: false
- name: Archive coverage report
uses: actions/upload-artifact@v3
with:
name: coverage-report-${{ matrix.python-version }}
path: htmlcov/
retention-days: 7
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit-tests
services:
redis:
image: redis:${{ env.REDIS_VERSION }}
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-asyncio pytest-cov
- name: Wait for Redis
run: |
for i in {1..30}; do
if redis-cli -h localhost ping; then
echo "Redis is ready"
break
fi
echo "Waiting for Redis..."
sleep 2
done
- name: Run integration tests
env:
REDIS_HOST: localhost
REDIS_PORT: 6379
KYC_API_KEY: ${{ secrets.KYC_API_KEY || 'test_api_key' }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY || 'test_secret_key' }}
run: |
pytest tests/integration/ \
--cov=src \
--cov-report=xml \
--cov-report=term \
-v
- name: Upload integration coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: integration
name: codecov-integration
fail_ci_if_error: false
security-scan:
name: Security Scanning
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install safety bandit
pip install -r requirements.txt
- name: Run Safety (dependency vulnerabilities)
run: safety check --json
continue-on-error: true
- name: Run Bandit (security issues)
run: bandit -r src/ -f json -o bandit-report.json
continue-on-error: true
- name: Upload Bandit report
uses: actions/upload-artifact@v3
with:
name: bandit-report
path: bandit-report.json
retention-days: 7
build-docker:
name: Build Docker Image
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USERNAME }}/kyc-mcp-server
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ secrets.DOCKER_USERNAME }}/kyc-mcp-server:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
continue-on-error: true
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
continue-on-error: true
smoke-tests:
name: Smoke Tests
runs-on: ubuntu-latest
needs: build-docker
if: github.event_name != 'pull_request'
services:
redis:
image: redis:${{ env.REDIS_VERSION }}
ports:
- 6379:6379
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Start MCP server
env:
REDIS_HOST: localhost
REDIS_PORT: 6379
KYC_API_KEY: ${{ secrets.KYC_API_KEY || 'test_api_key' }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY || 'test_secret_key' }}
run: |
python src/main.py &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
# Wait for server to start
for i in {1..30}; do
if curl -f http://localhost:8000/health; then
echo "Server is ready"
break
fi
echo "Waiting for server..."
sleep 2
done
- name: Run smoke tests
run: |
chmod +x tests/smoke_test.sh
./tests/smoke_test.sh
- name: Stop server
if: always()
run: |
if [ ! -z "$SERVER_PID" ]; then
kill $SERVER_PID || true
fi
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: smoke-tests
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
environment:
name: staging
url: https://staging.kyc-mcp.example.com
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Deploy to EC2
run: |
echo "Deploying to staging environment..."
# Add your deployment commands here
# Example: SSH to EC2 and run deployment script
- name: Run post-deployment validation
run: |
chmod +x tests/validate_deployment.sh
# Run validation against staging
export MCP_SERVER_URL=https://staging.kyc-mcp.example.com
./tests/validate_deployment.sh
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: smoke-tests
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment:
name: production
url: https://kyc-mcp.example.com
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Deploy to EC2
run: |
echo "Deploying to production environment..."
# Add your deployment commands here
- name: Run post-deployment validation
run: |
chmod +x tests/validate_deployment.sh
export MCP_SERVER_URL=https://kyc-mcp.example.com
./tests/validate_deployment.sh
- name: Create release
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 }}
draft: false
prerelease: false
notify:
name: Notify Results
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests, security-scan, build-docker]
if: always()
steps:
- name: Send notification
run: |
echo "Pipeline completed with status: ${{ job.status }}"
# Add notification logic (Slack, email, etc.)