diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..9d1bb77 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,222 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + # --------------------------------------------------------------------------- + # Job 1: Backend — lint + type-check + pytest + # --------------------------------------------------------------------------- + backend: + name: Backend Tests + runs-on: ubuntu-latest + + services: + postgres: + image: pgvector/pgvector:pg16 + env: + POSTGRES_DB: konstruct + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + ports: + - 5432:5432 + + redis: + image: redis:7-alpine + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + ports: + - 6379:6379 + + env: + DATABASE_URL: postgresql+asyncpg://konstruct_app:konstruct_pass@localhost:5432/konstruct + DATABASE_ADMIN_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/postgres + REDIS_URL: redis://localhost:6379/0 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + run: pip install uv + + - name: Install dependencies + run: uv sync + + - name: Lint — ruff check + run: uv run ruff check packages/ tests/ + + - name: Lint — ruff format check + run: uv run ruff format --check packages/ tests/ + + - name: Run pytest + run: uv run pytest tests/ -x --tb=short --junitxml=test-results.xml + + - name: Upload pytest results + if: always() + uses: actions/upload-artifact@v4 + with: + name: pytest-results + path: test-results.xml + retention-days: 30 + + # --------------------------------------------------------------------------- + # Job 2: Portal — build + E2E + Lighthouse CI + # Depends on backend passing (fail-fast) + # --------------------------------------------------------------------------- + portal: + name: Portal E2E + runs-on: ubuntu-latest + needs: backend + + services: + postgres: + image: pgvector/pgvector:pg16 + env: + POSTGRES_DB: konstruct + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + ports: + - 5432:5432 + + redis: + image: redis:7-alpine + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + ports: + - 6379:6379 + + env: + DATABASE_URL: postgresql+asyncpg://konstruct_app:konstruct_pass@localhost:5432/konstruct + DATABASE_ADMIN_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/postgres + REDIS_URL: redis://localhost:6379/0 + LLM_POOL_URL: http://localhost:8004 + NEXT_PUBLIC_API_URL: http://localhost:8001 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: packages/portal/package-lock.json + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install portal dependencies + working-directory: packages/portal + run: npm ci + + - name: Build portal (Next.js standalone) + working-directory: packages/portal + env: + NEXT_PUBLIC_API_URL: http://localhost:8001 + run: npm run build + + - name: Copy standalone assets + working-directory: packages/portal + run: | + cp -r .next/static .next/standalone/.next/static + cp -r public .next/standalone/public + + - name: Install Playwright browsers + working-directory: packages/portal + run: npx playwright install --with-deps chromium firefox webkit + + - name: Install Python dependencies and run migrations + env: + DATABASE_URL: postgresql+asyncpg://konstruct_app:konstruct_pass@localhost:5432/konstruct + DATABASE_ADMIN_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/postgres + REDIS_URL: redis://localhost:6379/0 + run: | + pip install uv + uv sync + uv run alembic upgrade head + uv run python -c "from shared.db import seed_admin; import asyncio; asyncio.run(seed_admin())" || true + + - name: Start gateway (background) + env: + DATABASE_URL: postgresql+asyncpg://konstruct_app:konstruct_pass@localhost:5432/konstruct + DATABASE_ADMIN_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/postgres + REDIS_URL: redis://localhost:6379/0 + LLM_POOL_URL: http://localhost:8004 + run: | + uv run uvicorn gateway.main:app --host 0.0.0.0 --port 8001 & + + - name: Wait for gateway to be ready + run: timeout 30 bash -c 'until curl -sf http://localhost:8001/health; do sleep 1; done' + + - name: Run E2E flow + accessibility tests + working-directory: packages/portal + env: + CI: "true" + PLAYWRIGHT_BASE_URL: http://localhost:3000 + API_URL: http://localhost:8001 + AUTH_SECRET: ${{ secrets.AUTH_SECRET }} + E2E_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL }} + E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }} + E2E_CADMIN_EMAIL: ${{ secrets.E2E_CADMIN_EMAIL }} + E2E_CADMIN_PASSWORD: ${{ secrets.E2E_CADMIN_PASSWORD }} + E2E_OPERATOR_EMAIL: ${{ secrets.E2E_OPERATOR_EMAIL }} + E2E_OPERATOR_PASSWORD: ${{ secrets.E2E_OPERATOR_PASSWORD }} + run: npx playwright test e2e/flows/ e2e/accessibility/ + + - name: Run Lighthouse CI + working-directory: packages/portal + env: + LHCI_BUILD_CONTEXT__CURRENT_HASH: ${{ github.sha }} + run: npx lhci autorun --config=e2e/lighthouse/lighthouserc.json + + - name: Upload Playwright HTML report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: packages/portal/playwright-report/ + retention-days: 30 + + - name: Upload Playwright JUnit results + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-junit + path: packages/portal/playwright-results.xml + retention-days: 30 + + - name: Upload Lighthouse report + if: always() + uses: actions/upload-artifact@v4 + with: + name: lighthouse-report + path: packages/portal/.lighthouseci/ + retention-days: 30