name: Backend CI/CD Pipeline on: push: branches: [master, main] paths: - 'maternal-app-backend/**' - '.github/workflows/backend-ci.yml' pull_request: branches: [master, main] paths: - 'maternal-app-backend/**' - '.github/workflows/backend-ci.yml' jobs: lint-and-test: name: Lint and Test Backend runs-on: ubuntu-latest defaults: run: working-directory: maternal-app/maternal-app-backend services: postgres: image: postgres:15 env: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpassword POSTGRES_DB: maternal_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 mongodb: image: mongo:7 ports: - 27017:27017 options: >- --health-cmd "mongosh --eval 'db.adminCommand(\"ping\")'" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: maternal-app/maternal-app-backend/package-lock.json - name: Install dependencies run: npm ci - name: Run linter run: npm run lint - name: Run unit tests run: npm run test:cov env: DATABASE_HOST: localhost DATABASE_PORT: 5432 DATABASE_USER: testuser DATABASE_PASSWORD: testpassword DATABASE_NAME: maternal_test REDIS_HOST: localhost REDIS_PORT: 6379 MONGODB_URI: mongodb://localhost:27017/maternal_test JWT_SECRET: test-jwt-secret-key-for-ci JWT_REFRESH_SECRET: test-refresh-secret-key-for-ci OPENAI_API_KEY: test-api-key - name: Upload coverage reports uses: codecov/codecov-action@v4 with: directory: maternal-app/maternal-app-backend/coverage flags: backend fail_ci_if_error: false - name: Check coverage thresholds run: | COVERAGE=$(npm run test:cov -- --silent | grep 'All files' | awk '{print $4}' | sed 's/%//') echo "Current coverage: ${COVERAGE}%" if (( $(echo "$COVERAGE < 70" | bc -l) )); then echo "::warning::Coverage ${COVERAGE}% is below 70% threshold" fi e2e-tests: name: E2E Tests Backend runs-on: ubuntu-latest needs: lint-and-test defaults: run: working-directory: maternal-app/maternal-app-backend services: postgres: image: postgres:15 env: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpassword POSTGRES_DB: maternal_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 mongodb: image: mongo:7 ports: - 27017:27017 options: >- --health-cmd "mongosh --eval 'db.adminCommand(\"ping\")'" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: maternal-app/maternal-app-backend/package-lock.json - name: Install dependencies run: npm ci - name: Run database migrations run: npm run migration:run env: DATABASE_HOST: localhost DATABASE_PORT: 5432 DATABASE_USER: testuser DATABASE_PASSWORD: testpassword DATABASE_NAME: maternal_test - name: Run E2E tests run: npm run test:e2e env: DATABASE_HOST: localhost DATABASE_PORT: 5432 DATABASE_USER: testuser DATABASE_PASSWORD: testpassword DATABASE_NAME: maternal_test REDIS_HOST: localhost REDIS_PORT: 6379 MONGODB_URI: mongodb://localhost:27017/maternal_test JWT_SECRET: test-jwt-secret-key-for-ci JWT_REFRESH_SECRET: test-refresh-secret-key-for-ci OPENAI_API_KEY: test-api-key CI: true - name: Upload E2E test results uses: actions/upload-artifact@v4 if: always() with: name: e2e-test-results path: maternal-app/maternal-app-backend/test-results/ retention-days: 30 build: name: Build Backend Application runs-on: ubuntu-latest needs: lint-and-test defaults: run: working-directory: maternal-app/maternal-app-backend steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: maternal-app/maternal-app-backend/package-lock.json - name: Install dependencies run: npm ci - name: Build application run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: backend-build path: maternal-app/maternal-app-backend/dist/ retention-days: 7 performance-test: name: Performance Testing runs-on: ubuntu-latest needs: build if: github.event_name == 'pull_request' defaults: run: working-directory: maternal-app/maternal-app-backend services: postgres: image: postgres:15 env: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpassword POSTGRES_DB: maternal_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 steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: maternal-app/maternal-app-backend/package-lock.json - name: Install dependencies run: npm ci - name: Download build artifacts uses: actions/download-artifact@v4 with: name: backend-build path: maternal-app/maternal-app-backend/dist/ - name: Start application run: | npm run start:prod & sleep 10 env: DATABASE_HOST: localhost DATABASE_PORT: 5432 DATABASE_USER: testuser DATABASE_PASSWORD: testpassword DATABASE_NAME: maternal_test REDIS_HOST: localhost REDIS_PORT: 6379 JWT_SECRET: test-jwt-secret-key-for-ci JWT_REFRESH_SECRET: test-refresh-secret-key-for-ci PORT: 3000 - name: Install Artillery run: npm install -g artillery@latest - name: Run performance tests run: | if [ -f "artillery.yml" ]; then artillery run artillery.yml --output performance-report.json else echo "::warning::No artillery.yml found, skipping performance tests" fi - name: Generate performance report if: always() run: | if [ -f "performance-report.json" ]; then artillery report performance-report.json --output performance-report.html fi - name: Upload performance report uses: actions/upload-artifact@v4 if: always() with: name: performance-report path: | maternal-app/maternal-app-backend/performance-report.json maternal-app/maternal-app-backend/performance-report.html retention-days: 30