name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main, develop] env: PYTHON_VERSION: "3.11" NODE_VERSION: "18" jobs: # Backend Tests backend-test: name: Backend Tests runs-on: ubuntu-latest services: postgres: image: postgres:16-alpine env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: audioforge_test 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 steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - name: Install dependencies run: | cd backend python -m pip install --upgrade pip pip install -e ".[dev]" - name: Run linter run: | cd backend ruff check app/ tests/ - name: Run type checker run: | cd backend mypy app/ --ignore-missing-imports - name: Run tests env: DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/audioforge_test REDIS_URL: redis://localhost:6379/0 MUSICGEN_DEVICE: cpu BARK_DEVICE: cpu run: | cd backend pytest tests/ -v --cov=app --cov-report=xml --cov-report=term - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./backend/coverage.xml flags: backend name: backend-coverage # Frontend Tests frontend-test: name: Frontend Tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: 8 - name: Get pnpm store directory id: pnpm-cache shell: bash run: | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Setup pnpm cache uses: actions/cache@v3 with: path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies run: | cd frontend pnpm install --frozen-lockfile - name: Run linter run: | cd frontend pnpm run lint - name: Run type checker run: | cd frontend pnpm run type-check - name: Run tests run: | cd frontend pnpm run test:coverage - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./frontend/coverage/coverage-final.json flags: frontend name: frontend-coverage - name: Build env: NEXT_PUBLIC_API_URL: http://localhost:8000 run: | cd frontend pnpm run build - name: Upload build artifacts uses: actions/upload-artifact@v3 with: name: frontend-build path: frontend/.next retention-days: 7 # Integration Tests integration-test: name: Integration Tests runs-on: ubuntu-latest needs: [backend-test, frontend-test] services: postgres: image: postgres:16-alpine env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: audioforge_test 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 steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: 8 - name: Install backend dependencies run: | cd backend pip install -e ".[dev]" - name: Install frontend dependencies run: | cd frontend pnpm install --frozen-lockfile - name: Start backend env: DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/audioforge_test REDIS_URL: redis://localhost:6379/0 MUSICGEN_DEVICE: cpu run: | cd backend uvicorn app.main:app --host 0.0.0.0 --port 8000 & sleep 10 - name: Start frontend env: NEXT_PUBLIC_API_URL: http://localhost:8000 run: | cd frontend pnpm run build pnpm run start & sleep 10 - name: Run integration tests run: | python scripts/launch_verification.py --section integration --json integration-results.json - name: Upload integration results uses: actions/upload-artifact@v3 with: name: integration-results path: integration-results.json retention-days: 30 # Security Scan security-scan: name: Security Scan runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' - name: Run Snyk security scan uses: snyk/actions/python@master continue-on-error: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --file=backend/pyproject.toml # Docker Build docker-build: name: Docker Build runs-on: ubuntu-latest needs: [backend-test, frontend-test] if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push backend uses: docker/build-push-action@v5 with: context: ./backend push: true tags: | ${{ secrets.DOCKER_USERNAME }}/audioforge-backend:latest ${{ secrets.DOCKER_USERNAME }}/audioforge-backend:${{ github.sha }} cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/audioforge-backend:buildcache cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/audioforge-backend:buildcache,mode=max - name: Build and push frontend uses: docker/build-push-action@v5 with: context: ./frontend push: true tags: | ${{ secrets.DOCKER_USERNAME }}/audioforge-frontend:latest ${{ secrets.DOCKER_USERNAME }}/audioforge-frontend:${{ github.sha }} cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/audioforge-frontend:buildcache cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/audioforge-frontend:buildcache,mode=max # Performance Tests performance-test: name: Performance Tests runs-on: ubuntu-latest needs: [integration-test] if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Lighthouse CI uses: treosh/lighthouse-ci-action@v10 with: urls: | http://localhost:3000 uploadArtifacts: true temporaryPublicStorage: true # Deployment (Production) deploy-production: name: Deploy to Production runs-on: ubuntu-latest needs: [docker-build, security-scan, performance-test] if: github.event_name == 'push' && github.ref == 'refs/heads/main' environment: name: production url: https://audioforge.com steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to production run: | echo "Deploying to production..." # Add your deployment script here # Example: kubectl apply -f k8s/ # Or: ansible-playbook deploy.yml - name: Verify deployment run: | curl -f https://api.audioforge.com/health || exit 1 curl -f https://audioforge.com || exit 1 - name: Notify team uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} text: 'AudioForge deployed to production!' webhook_url: ${{ secrets.SLACK_WEBHOOK }} if: always() # Notification notify: name: Notify Results runs-on: ubuntu-latest needs: [backend-test, frontend-test, integration-test, security-scan] if: always() steps: - name: Check job statuses run: | echo "Backend Test: ${{ needs.backend-test.result }}" echo "Frontend Test: ${{ needs.frontend-test.result }}" echo "Integration Test: ${{ needs.integration-test.result }}" echo "Security Scan: ${{ needs.security-scan.result }}" - name: Send notification uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} fields: repo,message,commit,author,action,eventName,ref,workflow webhook_url: ${{ secrets.SLACK_WEBHOOK }} if: always()