---
name: CI - Main

on:
  push:
    branches:
      - main
      - next
      - version-*
  pull_request:
    branches:
      - main
      - version-*

env:
  POSTGRES_DB: authentik
  POSTGRES_USER: authentik
  POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"

permissions:
  # Needed for checkout
  contents: read
  # Needed for codecov OIDC token
  id-token: write

jobs:
  lint:
    strategy:
      fail-fast: false
      matrix:
        job:
          - bandit
          - black
          - codespell
          - pending-migrations
          - ruff
          - mypy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
      - name: run job
        run: uv run make ci-${{ matrix.job }}
  test-migrations:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
      - name: run migrations
        run: uv run python -m lifecycle.migrate
  test-make-seed:
    runs-on: ubuntu-latest
    steps:
      - id: seed
        run: |
          echo "seed=$(printf "%d\n" "0x$(openssl rand -hex 4)")" >> "$GITHUB_OUTPUT"
    outputs:
      seed: ${{ steps.seed.outputs.seed }}
  test-migrations-from-stable:
    name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
    runs-on: ubuntu-latest
    timeout-minutes: 30
    needs: test-make-seed
    strategy:
      fail-fast: false
      matrix:
        psql:
          - 14-alpine
          - 18-alpine
        run_id: [1, 2, 3, 4, 5]
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
        with:
          fetch-depth: 0
      - name: checkout stable
        run: |
          set -e -o pipefail
          cp -R .github ..
          cp -R scripts ..
          # Previous stable tag
          prev_stable=$(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
          # Current version family based on
          current_version_family=$(cat internal/constants/VERSION | grep -vE -- 'rc[0-9]+$' || true)
          if [[ -n $current_version_family ]]; then
              prev_stable="version/${current_version_family}"
          fi
          echo "::notice::Checking out ${prev_stable} as stable version..."
          git checkout ${prev_stable}
          rm -rf .github/ scripts/
          mv ../.github ../scripts .
      - name: Setup authentik env (stable)
        uses: ./.github/actions/setup
        with:
          postgresql_version: ${{ matrix.psql }}
      - name: run migrations to stable
        run: |
          docker ps
          docker logs setup-postgresql-1
          uv run python -m lifecycle.migrate
      - name: checkout current code
        run: |
          set -x
          git fetch
          git reset --hard HEAD
          git clean -d -fx .
          git checkout $GITHUB_SHA
      - name: Setup authentik env (ensure latest deps are installed)
        uses: ./.github/actions/setup
        with:
          postgresql_version: ${{ matrix.psql }}
      - name: migrate to latest
        run: |
          uv run python -m lifecycle.migrate
      - name: run tests
        env:
          # Test in the main database that we just migrated from the previous stable version
          AUTHENTIK_POSTGRESQL__TEST__NAME: authentik
          CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
          CI_RUN_ID: ${{ matrix.run_id }}
          CI_TOTAL_RUNS: "5"
        run: |
          uv run make ci-test
      - uses: ./.github/actions/test-results
        if: ${{ always() }}
        with:
          flags: unit-migrate
  test-unittest:
    name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
    runs-on: ubuntu-latest
    timeout-minutes: 30
    needs: test-make-seed
    strategy:
      fail-fast: false
      matrix:
        psql:
          - 14-alpine
          - 18-alpine
        run_id: [1, 2, 3, 4, 5]
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
        with:
          postgresql_version: ${{ matrix.psql }}
      - name: run unittest
        env:
          CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
          CI_RUN_ID: ${{ matrix.run_id }}
          CI_TOTAL_RUNS: "5"
        run: |
          uv run make ci-test
      - uses: ./.github/actions/test-results
        if: ${{ always() }}
        with:
          flags: unit
  test-integration:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
      - name: Create k8s Kind Cluster
        uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
      - name: run integration
        run: |
          uv run coverage run manage.py test tests/integration
          uv run coverage xml
      - uses: ./.github/actions/test-results
        if: ${{ always() }}
        with:
          flags: integration
  test-e2e:
    name: test-e2e (${{ matrix.job.name }})
    runs-on: ubuntu-latest
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        job:
          - name: proxy
            glob: tests/e2e/test_provider_proxy*
          - name: oauth
            glob: tests/e2e/test_provider_oauth2* tests/e2e/test_source_oauth*
          - name: oauth-oidc
            glob: tests/e2e/test_provider_oidc*
          - name: saml
            glob: tests/e2e/test_provider_saml* tests/e2e/test_source_saml*
          - name: ldap
            glob: tests/e2e/test_provider_ldap* tests/e2e/test_source_ldap*
          - name: ws-fed
            glob: tests/e2e/test_provider_ws_fed*
          - name: radius
            glob: tests/e2e/test_provider_radius*
          - name: scim
            glob: tests/e2e/test_source_scim*
          - name: flows
            glob: tests/e2e/test_flows*
          - name: endpoints
            glob: tests/e2e/test_endpoints_*
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
      - name: Setup e2e env (chrome, etc)
        run: |
          docker compose -f tests/e2e/compose.yml up -d --quiet-pull
      - id: cache-web
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4
        with:
          path: web/dist
          key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
      - name: prepare web ui
        if: steps.cache-web.outputs.cache-hit != 'true'
        working-directory: web
        run: |
          npm ci
          make -C .. gen-client-ts
          npm run build
          npm run build:sfe
      - name: run e2e
        run: |
          uv run coverage run manage.py test ${{ matrix.job.glob }}
          uv run coverage xml
      - uses: ./.github/actions/test-results
        if: ${{ always() }}
        with:
          flags: e2e
  test-openid-conformance:
    name: test-openid-conformance (${{ matrix.job.name }})
    runs-on: ubuntu-latest
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        job:
          - name: basic
            glob: tests/openid_conformance/test_basic.py
          - name: implicit
            glob: tests/openid_conformance/test_implicit.py
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
      - name: Setup authentik env
        uses: ./.github/actions/setup
      - name: Setup e2e env (chrome, etc)
        run: |
          docker compose -f tests/e2e/compose.yml up -d --quiet-pull
      - name: Setup conformance suite
        run: |
          docker compose -f tests/openid_conformance/compose.yml up -d --quiet-pull
      - id: cache-web
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4
        with:
          path: web/dist
          key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
      - name: prepare web ui
        if: steps.cache-web.outputs.cache-hit != 'true'
        working-directory: web
        run: |
          npm ci
          make -C .. gen-client-ts
          npm run build
          npm run build:sfe
      - name: run conformance
        run: |
          uv run coverage run manage.py test ${{ matrix.job.glob }}
          uv run coverage xml
      - uses: ./.github/actions/test-results
        if: ${{ always() }}
        with:
          flags: conformance
      - if: ${{ !cancelled() }}
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
        with:
          name: conformance-certification-${{ matrix.job.name }}
          path: tests/openid_conformance/exports/
  ci-core-mark:
    if: always()
    needs:
      - lint
      - test-migrations
      - test-migrations-from-stable
      - test-unittest
      - test-integration
      - test-e2e
    runs-on: ubuntu-latest
    steps:
      - uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
        with:
          jobs: ${{ toJSON(needs) }}
  build:
    permissions:
      # Needed to upload container images to ghcr.io
      packages: write
      # Needed for attestation
      id-token: write
      attestations: write
      # Needed for checkout
      contents: read
    needs: ci-core-mark
    uses: ./.github/workflows/_reusable-docker-build.yml
    secrets: inherit
    with:
      image_name: ${{ github.repository == 'goauthentik/authentik-internal' && 'ghcr.io/goauthentik/internal-server' || 'ghcr.io/goauthentik/dev-server' }}
      release: false
  pr-comment:
    needs:
      - build
    runs-on: ubuntu-latest
    if: ${{ github.event_name == 'pull_request' }}
    permissions:
      # Needed to write comments on PRs
      pull-requests: write
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - name: prepare variables
        uses: ./.github/actions/docker-push-variables
        id: ev
        env:
          DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
        with:
          image-name: ghcr.io/goauthentik/dev-server
      - name: Comment on PR
        if: ${{ steps.ev.outputs.shouldPush == 'true' }}
        uses: ./.github/actions/comment-pr-instructions
        with:
          tag: ${{ steps.ev.outputs.imageMainTag }}
