name: CI/CD Pipeline on: push: branches: [ main, dev, feature/* ] pull_request: branches: [ main, dev ] jobs: # ===== TESTING PHASE ===== test: runs-on: ubuntu-latest services: redis: image: redis:7-alpine 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@v3 - name: Setup Python uses: actions/setup-python@v4 with: python-version: "3.13" - name: Install uv uses: astral-sh/setup-uv@v1 with: version: "1.0.0" - name: Cache dependencies uses: actions/cache@v3 with: path: | .venv .uv_cache key: ${{ runner.os }}-uv-3.13-${{ hashFiles('**/uv.lock') }} restore-keys: ${{ runner.os }}-uv-3.13- - name: Install dependencies run: | uv sync --group dev cd panel && npm ci && cd .. - name: Setup test database run: | touch database.db uv run python -c " from orm.base import Base from services.db import get_engine engine = get_engine() Base.metadata.create_all(engine) print('Test database initialized') " - name: Start servers run: | chmod +x scripts/ci-server.py timeout 300 python scripts/ci-server.py & echo $! > ci-server.pid echo "Waiting for servers..." timeout 120 bash -c ' while ! (curl -f http://localhost:8000/ > /dev/null 2>&1 && \ curl -f http://localhost:3000/ > /dev/null 2>&1); do sleep 2 done echo "Servers ready!" ' - name: Run tests run: | for test_type in "not e2e" "integration" "e2e" "browser"; do echo "Running $test_type tests..." uv run pytest tests/ -m "$test_type" -v --tb=short || \ if [ "$test_type" = "browser" ]; then echo "Browser tests failed (expected)"; else exit 1; fi done - name: Generate coverage run: | uv run pytest tests/ --cov=. --cov-report=xml --cov-report=html - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage.xml fail_ci_if_error: false - name: Cleanup if: always() run: | [ -f ci-server.pid ] && kill $(cat ci-server.pid) 2>/dev/null || true pkill -f "python dev.py|npm run dev|vite|ci-server.py" || true rm -f backend.pid frontend.pid ci-server.pid # ===== CODE QUALITY PHASE ===== quality: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Python uses: actions/setup-python@v4 with: python-version: "3.13" - name: Install uv uses: astral-sh/setup-uv@v1 with: version: "1.0.0" - name: Install dependencies run: | uv sync --group lint uv sync --group dev - name: Run quality checks run: | uv run ruff check . uv run mypy . --strict # ===== DEPLOYMENT PHASE ===== deploy: runs-on: ubuntu-latest needs: [test, quality] if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' environment: production steps: - name: Checkout code uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup SSH uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Deploy env: HOST_KEY: ${{ secrets.HOST_KEY }} TARGET: ${{ github.ref == 'refs/heads/main' && 'discoursio-api' || 'discoursio-api-staging' }} ENV: ${{ github.ref == 'refs/heads/main' && 'PRODUCTION' || 'STAGING' }} run: | echo "🚀 Deploying to $ENV..." mkdir -p ~/.ssh echo "$HOST_KEY" > ~/.ssh/known_hosts chmod 600 ~/.ssh/known_hosts git remote add dokku dokku@v2.discours.io:$TARGET git push dokku HEAD:main -f echo "✅ $ENV deployment completed!" # ===== SUMMARY ===== summary: runs-on: ubuntu-latest needs: [test, quality, deploy] if: always() steps: - name: Pipeline Summary run: | echo "## 🎯 CI/CD Pipeline Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### 📊 Test Results: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY echo "### 🔍 Code Quality: ${{ needs.quality.result }}" >> $GITHUB_STEP_SUMMARY echo "### 🚀 Deployment: ${{ needs.deploy.result || 'skipped' }}" >> $GITHUB_STEP_SUMMARY echo "### 📈 Coverage: Generated (XML + HTML)" >> $GITHUB_STEP_SUMMARY