name: CI # Runs on every push and every pull request. `build-practice-images.yml` # (the k3s/ArgoCD production deploy path) triggers independently on push to # master; until it is made to depend on this job, a red CI does NOT block a # deploy — see docs/operations.md. on: push: pull_request: concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: ai-service: name: ai-service — ruff + pytest runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" # No lockfile exists for either Python project (docs/27-technical-debt.md # D-07), so this mirrors apps/ai-service/Dockerfile's inline install. When # a lockfile lands, replace this with an install from it. - name: Install dependencies run: | python -m pip install --upgrade pip pip install \ "fastapi>=0.115,<1" \ "httpx>=0.27,<1" \ "psycopg[binary]>=3.2,<4" \ "pydantic-settings>=2.6,<3" \ "qdrant-client>=1.7,<2" \ "uvicorn[standard]>=0.30,<1" \ "prometheus-client>=0.20,<1" \ "opentelemetry-api>=1.27,<2" \ "opentelemetry-sdk>=1.27,<2" \ "opentelemetry-exporter-otlp-proto-http>=1.27,<2" \ "anthropic>=0.112,<1" \ "boto3" \ "pytest>=7.4,<9" \ "ruff" - name: ruff working-directory: apps/ai-service run: ruff check . # `tests/conftest.py` forces EMBEDDING_PROVIDER=disabled, because # `main.py` builds the whole runtime at import time and would otherwise # try to reach Qdrant during collection. No test needs a live datastore; # `tests/test_live_datastores.py` gates itself behind RUN_INTEGRATION=1. - name: pytest working-directory: apps/ai-service run: pytest tests -q ingestion: name: ingestion — pytest runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" # `ruff check` is not run here: `ingestion/pyproject.toml` declares no # [tool.ruff] section, so ruff would apply its full default rule set and # report ~426 pre-existing findings. Adding the same lint config # apps/ai-service uses is tracked as follow-up work, not silenced here. - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e "./ingestion[dev]" - name: pytest working-directory: ingestion run: pytest tests -q web: name: web — lint + build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" # Same toolchain the production image uses (apps/web/Dockerfile). - name: Enable pnpm run: corepack enable - name: Install run: pnpm install --frozen-lockfile # api-gateway proxies /auth/* and nothing else (a single # AuthProxyController). Preferring API_GATEWAY_URL in a RAG route # therefore breaks chat, suggest, history, sections, section-text and # feedback the moment apiGateway is enabled -- which is exactly what # shipped in PR #27 and stayed invisible until the config was first # rendered on 2026-08-19. Nothing else in CI would have caught it: the # code compiles and lints fine, and it only misbehaves once a specific # Helm value is set. Assert the boundary directly. - name: Assert RAG routes never resolve through api-gateway run: | offenders=$(grep -rln 'API_GATEWAY_URL' apps/web/app/api/chat apps/web/app/api/suggest apps/web/app/api/history apps/web/app/api/sections apps/web/app/api/section-text apps/web/app/api/feedback || true) if [ -n "$offenders" ]; then echo "::error::RAG routes must use AI_SERVICE_URL, not API_GATEWAY_URL: $offenders" exit 1 fi # ...and auth must keep using it, or login silently talks to the # wrong service instead. grep -q 'API_GATEWAY_URL' apps/web/app/api/auth/login/route.ts grep -q 'API_GATEWAY_URL' apps/web/app/api/auth/me/route.ts - name: Lint run: pnpm --filter @duoc-thu/web lint - name: Build run: pnpm --filter @duoc-thu/web build auth-and-gateway: name: auth-service + api-gateway — lint + build + test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - name: Enable pnpm run: corepack enable - name: Install run: pnpm install --frozen-lockfile - name: Lint run: pnpm --filter @duoc-thu/auth-service --filter @duoc-thu/api-gateway lint - name: Build run: pnpm --filter @duoc-thu/auth-service --filter @duoc-thu/api-gateway build - name: Test run: pnpm --filter @duoc-thu/auth-service --filter @duoc-thu/api-gateway test