name: CI Pipeline on: push: branches: [main, develop] pull_request: branches: [main] jobs: backend-lint-test: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: geo_test ports: ['5432:5432'] options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 redis: image: redis:7 ports: ['6379:6379'] options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' cache: 'pip' - name: Install dependencies run: | cd backend pip install -r requirements.txt pip install ruff pytest-cov - name: Lint (ruff) run: cd backend && ruff check app/ - name: Type check (optional) run: cd backend && ruff check app/ --select=E,W continue-on-error: true - name: Run tests env: DATABASE_URL: postgresql+asyncpg://test:test@localhost:5432/geo_test REDIS_URL: redis://localhost:6379/0 JWT_SECRET: test-secret-key-minimum-32-characters-long ENVIRONMENT: test run: | cd backend pytest tests/ -v --tb=short - name: Security scan (bandit) run: | pip install bandit cd backend bandit -r app/ -f json -o bandit-report.json || true bandit -r app/ -f txt || true continue-on-error: true - name: Upload bandit report if: always() uses: actions/upload-artifact@v4 with: name: bandit-report path: backend/bandit-report.json retention-days: 7 - name: Performance baseline env: DATABASE_URL: postgresql+asyncpg://test:test@localhost:5432/geo_test REDIS_URL: redis://localhost:6379/0 JWT_SECRET: test-secret-key-minimum-32-characters-long ENVIRONMENT: test run: | cd backend START_TIME=$(date +%s) pytest tests/ -v --tb=short -q 2>&1 | tee test-output.txt END_TIME=$(date +%s) DURATION=$((END_TIME - START_TIME)) echo "Backend test duration: ${DURATION}s" >> $GITHUB_STEP_SUMMARY echo "## Performance Baseline" >> $GITHUB_STEP_SUMMARY echo "- Backend test suite duration: **${DURATION}s**" >> $GITHUB_STEP_SUMMARY continue-on-error: true frontend-lint-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Install dependencies run: cd frontend && npm ci - name: Lint (ESLint) run: cd frontend && npm run lint - name: Type check run: cd frontend && npx tsc --noEmit - name: Unit tests run: cd frontend && npm run test:ci - name: Security audit (npm audit) run: cd frontend && npm audit --audit-level=moderate || true continue-on-error: true docker-build: runs-on: ubuntu-latest needs: [backend-lint-test, frontend-lint-test] steps: - uses: actions/checkout@v4 - name: Build backend image run: docker build -t geo-backend:test ./backend - name: Build frontend image run: docker build -t geo-frontend:test ./frontend e2e-test: runs-on: ubuntu-latest needs: [backend-lint-test, frontend-lint-test] steps: - uses: actions/checkout@v4 - name: Create .env for Docker Compose run: | cat > .env << 'EOF' POSTGRES_PASSWORD=geo_e2e_test_2026 REDIS_PASSWORD=geo_redis_e2e_2026 JWT_SECRET=e2e-test-secret-key-minimum-32-characters-long ENVIRONMENT=test DEEPSEEK_API_KEY=${{ secrets.DEEPSEEK_API_KEY || '' }} EOF - name: Start Docker Compose services run: | docker compose up -d db redis echo "Waiting for database..." until docker compose exec -T db pg_isready -U postgres -d geo_platform; do sleep 2 done echo "Database is ready" - name: Run database migrations run: | docker compose up -d backend echo "Waiting for backend to start..." for i in $(seq 1 30); do if curl -sf http://localhost:8000/health > /dev/null 2>&1; then echo "Backend is healthy" break fi echo "Waiting for backend... ($i/30)" sleep 5 done - name: Start frontend service run: | docker compose up -d frontend echo "Waiting for frontend to start..." for i in $(seq 1 30); do if curl -sf http://localhost:3000 > /dev/null 2>&1; then echo "Frontend is healthy" break fi echo "Waiting for frontend... ($i/30)" sleep 5 done - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Install Playwright browsers run: | cd frontend npm ci npx playwright install --with-deps chromium - name: Seed test data run: | curl -sf -X POST http://localhost:8000/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"admin@123","name":"E2E Test Admin"}' || true - name: Run E2E tests run: | cd frontend E2E_TEST_EMAIL=admin@example.com \ E2E_TEST_PASSWORD=admin@123 \ npx playwright test --project=chromium --reporter=html env: PLAYWRIGHT_BASE_URL: http://localhost:3000 - name: Upload E2E test results if: failure() uses: actions/upload-artifact@v4 with: name: e2e-test-results path: | frontend/playwright-report/ frontend/test-results/ retention-days: 7 - name: Stop Docker Compose if: always() run: docker compose down -v