name: CI

# Every push, on every branch — not just main — so build breakage shows up
# on the first commit of a branch, not only once a PR targets main. No
# separate pull_request trigger: for a PR from a branch on this same repo,
# push already fires on that branch, and GitHub attaches the run to the PR
# by commit SHA regardless of which event triggered it — adding
# pull_request too would just run every job twice per push. (Re-add it,
# scoped to forks, only if this repo starts accepting fork PRs — push
# events from a fork never reach here.)
on:
  push:

env:
  DATABASE_URL: "postgresql://ci:ci@postgres:5432/batchcooking_ci?schema=public"
  # Test-only secret, never used outside CI — real deployments must set their own.
  JWT_SECRET: "ci-only-secret-not-used-anywhere-else-32chars+"
  # Same reasoning as JWT_SECRET above — lets tech-step-worker.routes.test.ts
  # exercise the success path (matching secret), not just the "unset"
  # rejection every environment that doesn't set this gets by default.
  INTERNAL_WORKER_SECRET: "ci-only-worker-secret-not-used-anywhere-else-32chars+"
  # Same reasoning — lets admin-auth.test.ts exercise the admin login
  # success path + the requireAdmin-guarded routes, not just the "no admin
  # secret configured -> 401" path.
  ADMIN_JWT_SECRET: "ci-only-admin-secret-not-used-anywhere-else-32chars+"
  # Shared between the `test` job's own uvicorn step (below) and apps/api's
  # IntentServiceClient — see the `test` job for why this can't be a
  # `services:` container like postgres above (GitHub Actions can only pull
  # a published image, not build services/tech-step-intent-service/Dockerfile).
  INTENT_SERVICE_BASE_URL: "http://localhost:8000"
  INTENT_SERVICE_SECRET: "ci-only-intent-secret-not-used-anywhere-else-32chars+"

jobs:
  # Five independent jobs, no needs: between them — each starts in parallel
  # and reports as its own check, instead of the previous single chained
  # "lint-and-test then e2e" pipeline.
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: https://github.com/actions/checkout@v4

      - uses: pnpm/action-setup@v4

      - uses: https://github.com/actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm lint

  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: ci
          POSTGRES_PASSWORD: ci
          POSTGRES_DB: batchcooking_ci
        ports:
          - 5433:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10
    steps:
      - uses: https://github.com/actions/checkout@v4

      - uses: pnpm/action-setup@v4

      - uses: https://github.com/actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - uses: https://github.com/astral-sh/setup-uv@v5
        with:
          python-version: "3.12"
          enable-cache: true

      # `services:` (like the `postgres` container above) can only pull an
      # already-published image — it can't build
      # services/tech-step-intent-service/Dockerfile from this checkout.
      # Running `uvicorn` as a plain background step instead: it keeps
      # running for the rest of this job (GitHub Actions steps in one job
      # share the same runner process tree), and `pnpm --filter api test`
      # below needs a real instance to talk to per this repo's "never mock
      # an internal service" test convention — same reasoning as the real
      # `postgres` container just above, not a mock HTTP server.
      - name: Install services/tech-step-intent-service
        working-directory: services/tech-step-intent-service
        run: uv sync --frozen
      - name: Start services/tech-step-intent-service in the background
        working-directory: services/tech-step-intent-service
        run: |
          uv run uvicorn intent_service.main:app --host 0.0.0.0 --port 8000 &
          # `/health` only returns 200 once this service has finished
          # training itself from scratch (no model ever persisted to disk —
          # see its own README) — measured at ~540s (fr) / ~390s (en),
          # ~930s combined, against the current ~74-technique corpus (see
          # docker-compose.yml's healthcheck for the same reasoning and why
          # this grew slightly from the original ~670s).
          timeout 1200 bash -c 'until curl -sf http://localhost:8000/health > /dev/null; do sleep 2; done'

      - run: pnpm install --frozen-lockfile
      - run: pnpm --filter api exec prisma migrate deploy
      - run: pnpm --filter api test

  intent-service-test:
    runs-on: ubuntu-latest
    steps:
      - uses: https://github.com/actions/checkout@v4

      - uses: https://github.com/astral-sh/setup-uv@v5
        with:
          python-version: "3.12"
          enable-cache: true

      - name: Install services/tech-step-intent-service
        working-directory: services/tech-step-intent-service
        run: uv sync --frozen
      - name: Run pytest
        working-directory: services/tech-step-intent-service
        run: uv run pytest -q

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: https://github.com/actions/checkout@v4

      - uses: pnpm/action-setup@v4

      - uses: https://github.com/actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm build

  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: https://github.com/actions/checkout@v4
      
      - name: Install Xvfb and Cypress dependencies
        run: |
          apt-get update
          apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth dbus-x11

      - uses: pnpm/action-setup@v4

      - uses: https://github.com/actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Cache Cypress binary
        uses: https://github.com/actions/cache@v4
        with:
          path: ~/.cache/Cypress
          key: cypress-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

      - run: pnpm install --frozen-lockfile
      # pnpm install doesn't reliably trigger Cypress's postinstall binary
      # download (see apps/web's cypress caveat in the README) — install it
      # explicitly so `cypress run` finds it.
      - run: pnpm --filter web exec cypress install
      - name: Run E2E tests
        env:
          HOST: "0.0.0.0"
        run: pnpm --filter web e2e
      # No dev server needed here — Cypress spins up its own Vite dev
      # server internally for component testing (see cypress.config.ts's
      # `component.devServer`), unlike `e2e` above which needs the real app
      # running first.
      - run: pnpm --filter web cy:run:component
      # The admin app's own Cypress suite (`apps/admin-web`) — its own dev
      # server on :5174, all `/admin/*` calls mocked via `cy.intercept`
      # (no live backend needed), same as the `web` e2e run above.
      - name: Run admin-web E2E tests
        env:
          HOST: "0.0.0.0"
        run: pnpm --filter admin-web e2e
