docs: Add CI/CD pipeline documentation (Agent 98)

- Comprehensive CI/CD pipeline documentation (CI_CD_PIPELINE.md)
- GitHub Actions workflows (test, build, deploy)
- GitLab CI example (.gitlab-ci.yml)
- Security scanning integration (Trivy, Cargo Audit, SAST)
- Performance testing integration (Criterion benchmarks)
- GitOps workflows (ArgoCD, Kustomize, Terraform)
- Multi-environment deployment (dev, staging, production)
- Automated rollback on failure
- Health check validation
- Kubernetes manifests and Helm charts

Wave 125 Phase 3B - Deployment Excellence
Agent 98 Mission: CI/CD Pipeline Documentation (P2 - MEDIUM)
Duration: 1-2 hours
This commit is contained in:
jgrusewski
2025-10-07 20:53:38 +02:00
parent 10f04b5da7
commit 601fdf7d9b
5 changed files with 2567 additions and 0 deletions

283
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,283 @@
name: Build Pipeline
on:
push:
branches:
- main
- develop
tags:
- 'v*.*.*'
pull_request:
branches:
- main
- develop
env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}
jobs:
docker-build:
name: Docker Build (${{ matrix.service }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
service:
- api_gateway
- trading_service
- backtesting_service
- ml_training_service
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}
tags: |
type=ref,event=branch
type=ref,event=pr
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@v5
with:
context: .
file: services/${{ matrix.service }}/Dockerfile
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
platforms: linux/amd64,linux/arm64
build-args: |
SERVICE_NAME=${{ matrix.service }}
GIT_COMMIT=${{ github.sha }}
GIT_BRANCH=${{ github.ref_name }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
- name: Scan Docker Image (Trivy)
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }}
format: 'sarif'
output: 'trivy-results-${{ matrix.service }}.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '0' # Don't fail build, just report
- name: Upload Trivy Results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results-${{ matrix.service }}.sarif'
category: 'docker-${{ matrix.service }}'
- name: Check for Critical Vulnerabilities
run: |
CRITICAL_COUNT=$(grep -c "CRITICAL" trivy-results-${{ matrix.service }}.sarif || true)
HIGH_COUNT=$(grep -c "HIGH" trivy-results-${{ matrix.service }}.sarif || true)
echo "::notice::Critical vulnerabilities: $CRITICAL_COUNT"
echo "::notice::High vulnerabilities: $HIGH_COUNT"
if [ "$CRITICAL_COUNT" -gt 0 ]; then
echo "::error::Critical vulnerabilities found in ${{ matrix.service }}"
exit 1
fi
build-tli:
name: Build TLI Client
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: tli
asset_name: tli-linux-amd64
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: tli
asset_name: tli-macos-amd64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: tli.exe
asset_name: tli-windows-amd64.exe
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Cargo Registry
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Build TLI
run: cargo build --release -p tli --target ${{ matrix.target }}
- name: Strip Binary (Linux/macOS)
if: matrix.os != 'windows-latest'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Upload Binary
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
retention-days: 30
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [docker-build, build-tli]
if: startsWith(github.ref, 'refs/tags/v')
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: artifacts/**/*
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
push-to-ecr:
name: Push to AWS ECR (Production)
runs-on: ubuntu-latest
needs: docker-build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
timeout-minutes: 15
strategy:
matrix:
service:
- api_gateway
- trading_service
- backtesting_service
- ml_training_service
permissions:
id-token: write
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Pull Image from GHCR
run: |
docker pull ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }}
- name: Tag Image for ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker tag \
${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} \
$ECR_REGISTRY/${{ matrix.service }}:${{ github.sha }}
docker tag \
${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }} \
$ECR_REGISTRY/${{ matrix.service }}:latest
- name: Push Image to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker push $ECR_REGISTRY/${{ matrix.service }}:${{ github.sha }}
docker push $ECR_REGISTRY/${{ matrix.service }}:latest
summary:
name: Build Summary
runs-on: ubuntu-latest
needs: [docker-build, build-tli]
if: always()
steps:
- name: Generate Summary
run: |
echo "## Build Pipeline Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Docker Build | ${{ needs.docker-build.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| TLI Build | ${{ needs.build-tli.result }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "### 🚀 Production Images Built" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Images available at:" >> $GITHUB_STEP_SUMMARY
echo "- \`ghcr.io/${{ github.repository_owner }}/api_gateway:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`ghcr.io/${{ github.repository_owner }}/trading_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`ghcr.io/${{ github.repository_owner }}/backtesting_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`ghcr.io/${{ github.repository_owner }}/ml_training_service:${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
fi
- name: Check Overall Status
if: contains(needs.*.result, 'failure')
run: |
echo "::error::Build pipeline failed - check job results above"
exit 1

361
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,361 @@
name: Deployment Pipeline
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
type: choice
options:
- dev
- staging
- production
version:
description: 'Docker image tag (e.g., main-abc123 or v1.2.3)'
required: true
type: string
reason:
description: 'Deployment reason'
required: true
type: string
env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}
jobs:
validate-deployment:
name: Validate Deployment
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
approved: ${{ steps.validate.outputs.approved }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Validate Version Tag
id: validate
run: |
VERSION="${{ github.event.inputs.version }}"
ENVIRONMENT="${{ github.event.inputs.environment }}"
echo "Deploying version: $VERSION to $ENVIRONMENT"
# Verify image exists in registry
docker manifest inspect ${{ env.IMAGE_PREFIX }}/trading_service:$VERSION > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "::error::Image tag $VERSION not found in registry"
exit 1
fi
echo "✅ Image tag $VERSION verified"
echo "approved=true" >> $GITHUB_OUTPUT
request-approval:
name: Request Deployment Approval
runs-on: ubuntu-latest
needs: validate-deployment
if: github.event.inputs.environment == 'staging' || github.event.inputs.environment == 'production'
timeout-minutes: 1440 # 24 hours
steps:
- name: Request Staging Approval
if: github.event.inputs.environment == 'staging'
uses: trstringer/manual-approval@v1
with:
secret: ${{ secrets.GITHUB_TOKEN }}
approvers: team-leads
minimum-approvals: 1
issue-title: "Deploy to Staging: ${{ github.event.inputs.version }}"
issue-body: |
**Environment**: Staging
**Version**: ${{ github.event.inputs.version }}
**Reason**: ${{ github.event.inputs.reason }}
**Triggered by**: ${{ github.actor }}
**Timestamp**: ${{ github.event.head_commit.timestamp }}
Please review and approve this deployment.
- name: Request Production Approval
if: github.event.inputs.environment == 'production'
uses: trstringer/manual-approval@v1
with:
secret: ${{ secrets.GITHUB_TOKEN }}
approvers: team-leads,devops-team
minimum-approvals: 2
issue-title: "🚀 Deploy to Production: ${{ github.event.inputs.version }}"
issue-body: |
**Environment**: Production
**Version**: ${{ github.event.inputs.version }}
**Reason**: ${{ github.event.inputs.reason }}
**Triggered by**: ${{ github.actor }}
**Timestamp**: ${{ github.event.head_commit.timestamp }}
⚠️ **PRODUCTION DEPLOYMENT** - Requires 2 approvals
Please review the following before approving:
- [ ] Staging deployment successful in last 24h
- [ ] All tests passing
- [ ] Security scan clean
- [ ] Rollback plan documented
- [ ] On-call engineer notified
deploy:
name: Deploy to ${{ github.event.inputs.environment }}
runs-on: ubuntu-latest
needs: [validate-deployment, request-approval]
if: always() && needs.validate-deployment.outputs.approved == 'true'
timeout-minutes: 30
environment:
name: ${{ github.event.inputs.environment }}
url: https://${{ github.event.inputs.environment }}.foxhunt.io
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Configure kubectl
run: |
aws eks update-kubeconfig \
--region us-east-1 \
--name foxhunt-${{ github.event.inputs.environment }}
- name: Update Kubernetes Manifests
run: |
cd k8s/overlays/${{ github.event.inputs.environment }}
# Update image tags for all services
kustomize edit set image \
api-gateway=${{ env.IMAGE_PREFIX }}/api_gateway:${{ github.event.inputs.version }} \
trading-service=${{ env.IMAGE_PREFIX }}/trading_service:${{ github.event.inputs.version }} \
backtesting-service=${{ env.IMAGE_PREFIX }}/backtesting_service:${{ github.event.inputs.version }} \
ml-training-service=${{ env.IMAGE_PREFIX }}/ml_training_service:${{ github.event.inputs.version }}
- name: Run Database Migrations
if: github.event.inputs.environment == 'production'
run: |
# Port-forward to PostgreSQL
kubectl port-forward -n foxhunt-${{ github.event.inputs.environment }} \
svc/postgres 5432:5432 &
sleep 5
# Run migrations
cargo sqlx migrate run \
--database-url postgresql://foxhunt:${{ secrets.DB_PASSWORD }}@localhost:5432/foxhunt
- name: Deploy to Kubernetes
run: |
kubectl apply -k k8s/overlays/${{ github.event.inputs.environment }}
- name: Wait for Rollout
run: |
kubectl rollout status deployment/api-gateway \
-n foxhunt-${{ github.event.inputs.environment }} \
--timeout=10m
kubectl rollout status deployment/trading-service \
-n foxhunt-${{ github.event.inputs.environment }} \
--timeout=10m
kubectl rollout status deployment/backtesting-service \
-n foxhunt-${{ github.event.inputs.environment }} \
--timeout=10m
kubectl rollout status deployment/ml-training-service \
-n foxhunt-${{ github.event.inputs.environment }} \
--timeout=10m
- name: Run Health Checks
run: |
NAMESPACE=foxhunt-${{ github.event.inputs.environment }}
MAX_RETRIES=30
RETRY_DELAY=10
for SERVICE in api-gateway trading-service backtesting-service ml-training-service; do
echo "Checking health of $SERVICE..."
for i in $(seq 1 $MAX_RETRIES); do
POD=$(kubectl get pod -n $NAMESPACE -l app=$SERVICE -o jsonpath='{.items[0].metadata.name}')
if kubectl exec -n $NAMESPACE $POD -- grpc_health_probe -addr=:50051; then
echo "✅ $SERVICE health check passed (attempt $i/$MAX_RETRIES)"
break
fi
if [ $i -eq $MAX_RETRIES ]; then
echo "❌ $SERVICE health check failed after $MAX_RETRIES attempts"
exit 1
fi
echo "⏳ Health check failed (attempt $i/$MAX_RETRIES), retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
done
- name: Run Smoke Tests
run: |
ENVIRONMENT=${{ github.event.inputs.environment }}
# API Gateway smoke test
curl -f https://$ENVIRONMENT.foxhunt.io/health || exit 1
# Trading Service smoke test
grpcurl -plaintext $ENVIRONMENT.foxhunt.io:50052 grpc.health.v1.Health/Check
- name: Monitor Metrics (Production)
if: github.event.inputs.environment == 'production'
run: |
# Wait 5 minutes and check Prometheus alerts
echo "Monitoring metrics for 5 minutes..."
sleep 300
# Query Prometheus for firing alerts
ALERTS=$(curl -s http://prometheus.foxhunt.io/api/v1/alerts | jq '.data.alerts | length')
if [ "$ALERTS" -gt 0 ]; then
echo "⚠️ Warning: $ALERTS Prometheus alerts firing"
curl -s http://prometheus.foxhunt.io/api/v1/alerts | jq '.data.alerts'
else
echo "✅ No Prometheus alerts firing"
fi
- name: Notify Slack (Success)
if: success()
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} -H 'Content-Type: application/json' -d '{
"text": "✅ Deployment Successful",
"attachments": [{
"color": "good",
"fields": [
{"title": "Environment", "value": "${{ github.event.inputs.environment }}", "short": true},
{"title": "Version", "value": "${{ github.event.inputs.version }}", "short": true},
{"title": "Reason", "value": "${{ github.event.inputs.reason }}", "short": false},
{"title": "Deployed by", "value": "${{ github.actor }}", "short": true}
]
}]
}'
rollback:
name: Rollback Deployment
runs-on: ubuntu-latest
needs: deploy
if: failure()
timeout-minutes: 10
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Configure kubectl
run: |
aws eks update-kubeconfig \
--region us-east-1 \
--name foxhunt-${{ github.event.inputs.environment }}
- name: Rollback Deployments
run: |
NAMESPACE=foxhunt-${{ github.event.inputs.environment }}
echo "🔄 Initiating rollback..."
kubectl rollout undo deployment/api-gateway -n $NAMESPACE
kubectl rollout undo deployment/trading-service -n $NAMESPACE
kubectl rollout undo deployment/backtesting-service -n $NAMESPACE
kubectl rollout undo deployment/ml-training-service -n $NAMESPACE
echo "⏳ Waiting for rollback to complete..."
kubectl rollout status deployment/api-gateway -n $NAMESPACE --timeout=5m
kubectl rollout status deployment/trading-service -n $NAMESPACE --timeout=5m
kubectl rollout status deployment/backtesting-service -n $NAMESPACE --timeout=5m
kubectl rollout status deployment/ml-training-service -n $NAMESPACE --timeout=5m
echo "✅ Rollback completed"
- name: Verify Rollback
run: |
NAMESPACE=foxhunt-${{ github.event.inputs.environment }}
for SERVICE in api-gateway trading-service backtesting-service ml-training-service; do
POD=$(kubectl get pod -n $NAMESPACE -l app=$SERVICE -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n $NAMESPACE $POD -- grpc_health_probe -addr=:50051
done
- name: Notify Slack (Rollback)
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} -H 'Content-Type: application/json' -d '{
"text": "⚠️ Deployment Failed - Rollback Completed",
"attachments": [{
"color": "danger",
"fields": [
{"title": "Environment", "value": "${{ github.event.inputs.environment }}", "short": true},
{"title": "Version", "value": "${{ github.event.inputs.version }}", "short": true},
{"title": "Reason", "value": "Deployment failed health checks", "short": false},
{"title": "Triggered by", "value": "${{ github.actor }}", "short": true}
]
}]
}'
create-release-tag:
name: Create Release Tag (Production)
runs-on: ubuntu-latest
needs: deploy
if: github.event.inputs.environment == 'production' && success()
timeout-minutes: 5
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.event.inputs.version }}
name: Release ${{ github.event.inputs.version }}
body: |
## Release ${{ github.event.inputs.version }}
**Deployed to Production**: ${{ github.event.head_commit.timestamp }}
**Deployment Reason**: ${{ github.event.inputs.reason }}
**Deployed by**: ${{ github.actor }}
### Services Deployed
- API Gateway: `${{ env.IMAGE_PREFIX }}/api_gateway:${{ github.event.inputs.version }}`
- Trading Service: `${{ env.IMAGE_PREFIX }}/trading_service:${{ github.event.inputs.version }}`
- Backtesting Service: `${{ env.IMAGE_PREFIX }}/backtesting_service:${{ github.event.inputs.version }}`
- ML Training Service: `${{ env.IMAGE_PREFIX }}/ml_training_service:${{ github.event.inputs.version }}`
See [CHANGELOG.md](CHANGELOG.md) for details.
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

272
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,272 @@
name: Test Pipeline
on:
push:
branches:
- main
- develop
- 'feature/**'
pull_request:
branches:
- main
- develop
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
SQLX_OFFLINE: true # Use offline mode for CI
jobs:
test:
name: Test Suite
runs-on: ubuntu-latest
timeout-minutes: 30
services:
postgres:
image: timescale/timescaledb:latest-pg16
env:
POSTGRES_USER: foxhunt
POSTGRES_PASSWORD: foxhunt_dev_password
POSTGRES_DB: foxhunt
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
vault:
image: hashicorp/vault:latest
env:
VAULT_DEV_ROOT_TOKEN_ID: foxhunt-dev-root
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
ports:
- 8200:8200
options: >-
--cap-add=IPC_LOCK
--health-cmd "vault status"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt
REDIS_URL: redis://localhost:6379
VAULT_ADDR: http://localhost:8200
VAULT_TOKEN: foxhunt-dev-root
JWT_SECRET: test_jwt_secret_key
RUST_LOG: info
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy, llvm-tools-preview
- name: Cache Cargo Registry
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install SQLx CLI
run: cargo install sqlx-cli --no-default-features --features postgres
- name: Run Database Migrations
run: cargo sqlx migrate run
- name: Install llvm-cov
run: cargo install cargo-llvm-cov
- name: Check Formatting
run: cargo fmt --all -- --check
- name: Run Clippy
run: |
cargo clippy --workspace --all-targets -- \
-D warnings \
-D clippy::unwrap_used \
-D clippy::expect_used \
-D clippy::panic \
-W clippy::all
- name: Run Unit Tests
run: cargo test --workspace --lib --bins
env:
RUST_LOG: debug
- name: Run Integration Tests
run: cargo test --workspace --test '*' -- --test-threads=1
timeout-minutes: 15
- name: Generate Coverage Report
run: |
cargo llvm-cov --workspace --lcov --output-path lcov.info
cargo llvm-cov --workspace --html --output-dir coverage_report
- name: Check Coverage Threshold
run: |
COVERAGE=$(cargo llvm-cov --workspace --summary-only | grep -oP 'TOTAL.*\K[0-9.]+(?=%)')
echo "::notice::Coverage: $COVERAGE%"
if (( $(echo "$COVERAGE < 60.0" | bc -l) )); then
echo "::error::Coverage $COVERAGE% below threshold 60%"
exit 1
fi
echo "::notice::✅ Coverage $COVERAGE% meets threshold"
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage_report/
retention-days: 30
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: target/nextest/
retention-days: 30
security-audit:
name: Security Audit
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install Cargo Audit
run: cargo install cargo-audit
- name: Run Security Audit
run: |
cargo audit --deny warnings \
--ignore RUSTSEC-2020-0071 # RSA Marvin Attack (mitigated)
- name: Install Cargo Geiger
run: cargo install cargo-geiger
- name: Unsafe Code Audit
run: |
cargo geiger --all-features --output-format GitHubMarkdown >> $GITHUB_STEP_SUMMARY
dependency-check:
name: Dependency Check
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install Cargo Outdated
run: cargo install cargo-outdated
- name: Check Outdated Dependencies
run: cargo outdated --exit-code 1
continue-on-error: true
- name: Install Cargo Deny
run: cargo install cargo-deny
- name: Check Licenses
run: cargo deny check licenses
- name: Check Advisories
run: cargo deny check advisories
build-check:
name: Build Check
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo Registry
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check Compilation
run: cargo check --workspace --all-targets
- name: Build Release
run: cargo build --workspace --release
summary:
name: Test Summary
runs-on: ubuntu-latest
needs: [test, security-audit, dependency-check, build-check]
if: always()
steps:
- name: Generate Summary
run: |
echo "## Test Pipeline Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Test Suite | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security Audit | ${{ needs.security-audit.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dependency Check | ${{ needs.dependency-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Build Check | ${{ needs.build-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
- name: Check Overall Status
if: contains(needs.*.result, 'failure')
run: |
echo "::error::Pipeline failed - check job results above"
exit 1