name: ci

# Один workflow вместо трёх: порядок build → tests → sonar задаётся через `needs`, а между
# отдельными файлами workflow'ов в Gitea связать этапы нечем. Побочный эффект — этапы идут
# последовательно, поэтому прогон дольше, зато тесты не стартуют на заведомо битой сборке,
# а анализ не уходит в SonarCloud с красными тестами.
on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

jobs:
  build-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - name: Restore
        working-directory: backend
        run: dotnet restore TeleWave.slnx
      # Форматирование проверяем до сборки: падает за секунду и сразу показывает, что чинить.
      # Версия csharpier прибита в .config/dotnet-tools.json — глобальная копия разработчика на CI
      # не влияет, иначе её минорка переформатировала бы репозиторий в свой стиль.
      - name: Format check
        working-directory: backend
        run: |
          dotnet tool restore
          dotnet csharpier check .
      # Строгая сборка: TreatWarningsAsErrors=true из Directory.Build.props не отключаем.
      - name: Build (Release)
        working-directory: backend
        run: dotnet build TeleWave.slnx -c Release --no-restore

  build-frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Enable pnpm
        run: corepack enable
      - name: Install
        working-directory: frontend
        run: pnpm install --frozen-lockfile
      - name: Format check
        working-directory: frontend
        run: pnpm format:check
      - name: Lint
        working-directory: frontend
        run: pnpm lint
      - name: Typecheck
        working-directory: frontend
        run: pnpm typecheck
      - name: Build
        working-directory: frontend
        run: pnpm build

  tests:
    runs-on: ubuntu-latest
    needs: [build-backend, build-frontend]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - name: Restore
        working-directory: backend
        run: dotnet restore TeleWave.slnx
      # Покрытие здесь не собираем: его считает джоба sonar своим прогоном (в формате opencover),
      # а бейдж покрытия в README берётся из SonarCloud.
      - name: Test
        working-directory: backend
        run: >
          dotnet test TeleWave.slnx
          -c Release
          --no-restore
          --logger "console;verbosity=normal"

  # Анализ SonarCloud вместе с покрытием: один прогон собирает решение под сканером, гоняет тесты
  # и отправляет результат. Только push в main — декорация pull request'ов у SonarCloud завязана
  # на GitHub/GitLab, из Gitea она не работает, и анализ PR только засорял бы ветки в проекте.
  sonar:
    runs-on: ubuntu-latest
    needs: tests
    if: github.event_name != 'pull_request'
    env:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      SONAR_HOST: https://sonarcloud.io
      SONAR_PROJECT_KEY: mrleo1nid_telewave
      SONAR_ORGANIZATION: mrleo1nid
    steps:
      - uses: actions/checkout@v4
        with:
          # Полная история — иначе Sonar не сопоставит изменения с авторами и «новым кодом».
          fetch-depth: 0

      # Сканер — java-приложение, на runner-образе Java может не оказаться.
      - uses: actions/setup-java@v4
        if: env.SONAR_TOKEN != ''
        with:
          distribution: temurin
          java-version: 21

      - uses: actions/setup-dotnet@v4
        if: env.SONAR_TOKEN != ''
        with:
          dotnet-version: 10.0.x

      - name: Install scanner
        if: env.SONAR_TOKEN != ''
        run: dotnet tool install --global dotnet-sonarscanner

      # Сканер запускается из корня репозитория, а не из backend/: так в анализ попадает и фронт.
      - name: Begin analysis
        if: env.SONAR_TOKEN != ''
        run: |
          export PATH="$PATH:$HOME/.dotnet/tools"
          dotnet sonarscanner begin \
            /k:"$SONAR_PROJECT_KEY" \
            /o:"$SONAR_ORGANIZATION" \
            /d:sonar.host.url="$SONAR_HOST" \
            /d:sonar.token="$SONAR_TOKEN" \
            /d:sonar.cs.opencover.reportsPaths="**/coverage/**/coverage.opencover.xml" \
            /d:sonar.exclusions="frontend/node_modules/**,frontend/dist/**,frontend/src/routeTree.gen.ts,backend/src/TeleWave.Infrastructure/Migrations/**" \
            /d:sonar.coverage.exclusions="backend/src/TeleWave.Infrastructure/Migrations/**,frontend/**"

      # Сканер подмешивает в сборку свои анализаторы, а в проекте TreatWarningsAsErrors=true —
      # любое замечание Sonar роняло бы сборку вместо того, чтобы приехать в отчёт. Строгая
      # сборка живёт в джобе build-backend, здесь она нужна только как носитель анализа.
      - name: Build
        if: env.SONAR_TOKEN != ''
        run: dotnet build backend/TeleWave.slnx -c Release /p:TreatWarningsAsErrors=false

      # Формат opencover, а не cobertura по умолчанию: C#-анализатор Sonar читает именно его.
      # Интеграционные тесты без Docker пропускаются сами (см. PostgresFixture).
      - name: Test + coverage
        if: env.SONAR_TOKEN != ''
        run: >
          dotnet test backend/TeleWave.slnx
          -c Release
          --no-build
          --collect:"XPlat Code Coverage;Format=opencover"
          --results-directory backend/coverage
          --logger "console;verbosity=normal"

      - name: End analysis
        if: env.SONAR_TOKEN != ''
        run: |
          export PATH="$PATH:$HOME/.dotnet/tools"
          dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"

      - name: Skipped
        if: env.SONAR_TOKEN == ''
        run: echo "SONAR_TOKEN не задан — анализ пропущен."
