# ── CI/CD Pipeline for Rednote RAG App ────────────────────────────────── # Runs on every push to main and on every pull request. # Backend tests use SQLite in-memory (no external DB needed). # Groq API is mocked in tests — no API key required for CI. # ───────────────────────────────────────────────────────────────────────── name: CI/CD on: push: branches: [main] pull_request: branches: [main] env: # These match the docker-compose defaults POSTGRES_USER: rednote POSTGRES_PASSWORD: rednote POSTGRES_DB: rednote DATABASE_URL: postgresql+asyncpg://rednote:rednote@localhost:5432/rednote SECRET_KEY: ci-secret-key-not-used-in-production GROQ_API_KEY: gsk_ci_mock_key jobs: # ═══════════════════════════════════════════════════════════════════ # JOB 1: Backend Lint, Type Check & Test # ═══════════════════════════════════════════════════════════════════ backend: name: Backend (lint + typecheck + test) runs-on: ubuntu-latest defaults: run: working-directory: backend steps: - uses: actions/checkout@v4 - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" cache: "pip" cache-dependency-path: backend/requirements.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install ruff pyright - name: Lint with Ruff run: ruff check . - name: Type check with Pyright (basic) run: pyright - name: Run tests with pytest run: python -m pytest tests/ -v --tb=short # ═══════════════════════════════════════════════════════════════════ # JOB 2: Frontend Lint, Type Check & Test # ═══════════════════════════════════════════════════════════════════ frontend: name: Frontend (lint + typecheck + test + build) runs-on: ubuntu-latest defaults: run: working-directory: frontend steps: - uses: actions/checkout@v4 - name: Set up Node.js 22 uses: actions/setup-node@v4 with: node-version: "22" cache: "npm" cache-dependency-path: frontend/package-lock.json - name: Install dependencies run: npm ci - name: Lint with ESLint run: npm run lint - name: Type check with TypeScript run: npx tsc --noEmit - name: Run tests with Vitest run: npx vitest run - name: Build production bundle run: npm run build # ═══════════════════════════════════════════════════════════════════ # JOB 3: Docker build (validate images compile) # ═══════════════════════════════════════════════════════════════════ docker: name: Docker build check runs-on: ubuntu-latest needs: [backend, frontend] steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build backend image uses: docker/build-push-action@v6 with: context: . file: backend/Dockerfile.backend push: false tags: rednote-backend:ci cache-from: type=gha cache-to: type=gha,mode=max - name: Build frontend image uses: docker/build-push-action@v6 with: context: . file: frontend/Dockerfile.frontend push: false tags: rednote-frontend:ci cache-from: type=gha cache-to: type=gha,mode=max # ═══════════════════════════════════════════════════════════════════ # JOB 4: Deploy (only on main branch push) # ═══════════════════════════════════════════════════════════════════ deploy: name: Deploy to production runs-on: ubuntu-latest needs: [docker] if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - uses: actions/checkout@v4 - name: Log in 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@v6 with: context: . file: backend/Dockerfile.backend push: true tags: ${{ secrets.DOCKER_USERNAME }}/rednote-backend:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Build and push frontend uses: docker/build-push-action@v6 with: context: . file: frontend/Dockerfile.frontend push: true tags: ${{ secrets.DOCKER_USERNAME }}/rednote-frontend:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Deploy via SSH uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | cd /opt/rednote # Pull latest images docker compose pull # Recreate containers with zero-downtime docker compose up -d --remove-orphans # Clean up old images docker image prune -f