name: CI on: push: branches: [main] pull_request: branches: [main] jobs: backend: runs-on: ubuntu-latest services: postgres: image: pgvector/pgvector:pg16 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: resolutionflow_test ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 env: DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/resolutionflow_test DATABASE_URL_SYNC: postgresql://postgres:postgres@localhost:5432/resolutionflow_test SECRET_KEY: ci-test-secret-key-not-for-production DEBUG: "true" APP_NAME: ResolutionFlow TEST_DB_NAME: resolutionflow_test DB_APP_ROLE_PASSWORD: app_secret_ci steps: - uses: actions/checkout@v5 - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip cache-dependency-path: | backend/requirements.txt backend/requirements-dev.txt - name: Install dependencies run: pip install -r backend/requirements.txt -r backend/requirements-dev.txt - name: Run Alembic migrations run: cd backend && alembic upgrade head - name: Check tenant filter enforcement run: cd backend && python scripts/check_tenant_filters.py # Warn mode only (exits 0). Switch to --fail after Phase 1 backlog clears. # See: docs/superpowers/specs/2026-04-09-tenant-data-isolation-design.md Section 3f - name: Run tests with coverage run: cd backend && python -m pytest --override-ini="addopts=" --cov=app --cov-report=term-missing --cov-report=json:coverage.json --cov-fail-under=50 - name: Display coverage summary if: always() run: | cd backend python -c " import json with open('coverage.json') as f: data = json.load(f) total = data['totals']['percent_covered_display'] print(f'Total coverage: {total}%') print() print('Module coverage:') for fname, fdata in sorted(data['files'].items()): pct = fdata['summary']['percent_covered_display'] if float(pct) < 80: print(f' ⚠ {fname}: {pct}%') else: print(f' ✓ {fname}: {pct}%') " frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Set up Node.js 20 uses: actions/setup-node@v5 with: node-version: "20" cache: npm cache-dependency-path: frontend/package-lock.json - name: Install dependencies run: cd frontend && npm ci - name: Lint run: cd frontend && npm run lint - name: Test with coverage run: cd frontend && npm run test:coverage - name: Build run: cd frontend && NODE_OPTIONS="--max-old-space-size=4096" npm run build - name: Upload build artifact uses: actions/upload-artifact@v5 with: name: frontend-dist path: frontend/dist retention-days: 1 e2e: needs: [frontend] runs-on: ubuntu-latest services: postgres: image: pgvector/pgvector:pg16 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: resolutionflow_test ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 env: PLAYWRIGHT_DATABASE_URL: postgresql+asyncpg://postgres:postgres@127.0.0.1:5432/resolutionflow_test PLAYWRIGHT_DATABASE_URL_SYNC: postgresql://postgres:postgres@127.0.0.1:5432/resolutionflow_test PLAYWRIGHT_API_ORIGIN: http://127.0.0.1:8000 PLAYWRIGHT_BASE_URL: http://127.0.0.1:4173 PLAYWRIGHT_SECRET_KEY: ci-playwright-secret-key PLAYWRIGHT_TEST_EMAIL: teamadmin@resolutionflow.example.com PLAYWRIGHT_TEST_PASSWORD: TestPass123! steps: - uses: actions/checkout@v5 - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip cache-dependency-path: | backend/requirements.txt backend/requirements-dev.txt - name: Set up Node.js 20 uses: actions/setup-node@v5 with: node-version: "20" cache: npm cache-dependency-path: frontend/package-lock.json - name: Install backend dependencies run: pip install -r backend/requirements.txt -r backend/requirements-dev.txt - name: Install frontend dependencies run: cd frontend && npm ci - name: Download frontend build uses: actions/download-artifact@v5 with: name: frontend-dist path: frontend/dist - name: Install Playwright browser run: cd frontend && npx playwright install --with-deps chromium - name: Run Playwright smoke tests run: cd frontend && npm run test:e2e - name: Upload Playwright report if: always() uses: actions/upload-artifact@v5 with: name: playwright-report path: | frontend/playwright-report frontend/test-results if-no-files-found: ignore