name: Desktop installers

# Produit les installeurs de BUREAU (.msi Windows + AppImage Linux) et les publie
# en tant qu'assets d'une GitHub Release, sur tag `v*`.
#
# Complementaire au pipeline Gitea Actions (.gitea/workflows/release.yml) qui,
# lui, build et pousse les IMAGES Docker. Ici on est sur GitHub car jpackage ne
# sait PAS cross-compiler : le .msi DOIT etre construit sur un runner Windows et
# l'AppImage sur un runner Linux — GitHub fournit les deux gratuitement.
#
# Prerequis : le depot Gitea doit etre mirrore vers GitHub (push mirror, tags
# inclus) pour que le tag declenche ce workflow.
#
# Tag stable  vX.Y.Z       -> GitHub Release PUBLIQUE avec le .msi attache.
# Tag beta    vX.Y.Z-beta* -> AUCUNE publication publique. Le .msi est depose en
#                             ARTEFACT PRIVE du run (telechargeable seulement par
#                             toi via l'onglet Actions) ; tu le joins ensuite a un
#                             post Patreon reserve a un palier. Patreon = la
#                             barriere d'acces (equivalent du registry prive +
#                             relais pour les images Docker beta).

on:
  push:
    tags: ['v*']
  # Declenchement MANUEL depuis l'onglet Actions ("Run workflow"). Utile quand un
  # tag a ete pousse AVANT que le workflow existe sur GitHub (ne se redeclenche
  # pas tout seul), ou pour rejouer un build. Saisir la version SANS le "v".
  workflow_dispatch:
    inputs:
      version:
        description: "Version a builder (doit correspondre a un tag existant, ex: 0.15.0 ou 0.15.0-beta)"
        required: true

permissions:
  contents: write   # requis pour creer la Release et y attacher le .msi

jobs:
  # GATE : les 3 suites unitaires (Java / Python / Angular) doivent passer avant
  # de construire le .msi. Tourne sur ubuntu-latest (moins cher/plus rapide que
  # windows) ; le build natif lui-meme reste sur windows-latest via `needs`.
  # Auto-suffisant : ne depend PAS du resultat de Gitea (CI separee), il rejoue
  # les memes tests ici. Un test rouge => pas d'installeur publie.
  tests:
    runs-on: ubuntu-latest
    # Base PostgreSQL réelle pour les tests Core. Sur les runners GitHub (job sur
    # la VM, pas en conteneur), le service est joignable via localhost + le port mappé.
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_DB: loremind_test
          POSTGRES_USER: loremind_test
          POSTGRES_PASSWORD: loremind_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U loremind_test -d loremind_test"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event_name == 'workflow_dispatch' && format('v{0}', inputs.version) || github.ref }}

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
          cache: maven
      - name: Core — mvn test (+ JaCoCo check)
        working-directory: core
        env:
          SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/loremind_test
          SPRING_DATASOURCE_USERNAME: loremind_test
          SPRING_DATASOURCE_PASSWORD: loremind_test
        run: |
          chmod +x ./mvnw
          ./mvnw -B test

      - name: Set up Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: pip
          cache-dependency-path: brain/requirements-dev.txt
      - name: Brain — pytest (+ couverture)
        working-directory: brain
        run: |
          pip install -r requirements-dev.txt
          pytest --cov=app --cov-report=term-missing --cov-fail-under=50

      - name: Set up Node 20
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
          cache-dependency-path: web/package-lock.json
      - name: Web — vitest (+ couverture)
        working-directory: web
        run: |
          npm ci --no-audit --no-fund
          npm run test:unit:coverage

  windows:
    needs: tests
    runs-on: windows-latest
    steps:
      # En declenchement manuel, on checkout le TAG correspondant a la version
      # saisie (sinon checkout prendrait la branche par defaut). En push de tag,
      # on prend la ref poussee.
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event_name == 'workflow_dispatch' && format('v{0}', inputs.version) || github.ref }}

      # Apporte jpackage (lanceur d'empaquetage natif) dans le PATH.
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      # Python 3.12 = meme version que l'image Docker du Brain (coherence runtime).
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      # jpackage genere le MSI via WiX Toolset v3 (candle.exe/light.exe). WiX 4+
      # ne convient pas (outils renommes). Le paquet choco `wixtoolset` est la
      # ligne 3.x et s'ajoute au PATH.
      - name: Install WiX Toolset 3
        shell: pwsh
        run: choco install wixtoolset -y --no-progress

      # Tesseract OCR : non preinstalle sur windows-latest. Requis pour que
      # build-windows.ps1 embarque l'OCR des PDF scannes (sinon il skip en
      # degradation gracieuse). Installe dans %ProgramFiles%\Tesseract-OCR.
      - name: Install Tesseract OCR
        shell: pwsh
        run: choco install tesseract -y --no-progress

      # Version de l'installeur = version du tag (push) OU de l'input (manuel).
      # Sorties : version (numerique X.Y.Z pour le MSI), tag (vX.Y.Z[-beta]),
      # isbeta (true/false) — independant du nom de ref (qui est une branche en manuel).
      - name: Derive version
        id: ver
        shell: pwsh
        run: |
          if ('${{ github.event_name }}' -eq 'workflow_dispatch') {
            $raw = '${{ inputs.version }}'
          } else {
            $raw = '${{ github.ref_name }}'
          }
          $raw = $raw -replace '^v',''            # 0.15.0  ou  0.15.0-beta
          $num = ($raw -split '-')[0]             # 0.15.0
          $isbeta = if ($raw -like '*-beta*') { 'true' } else { 'false' }
          "version=$num"   >> $env:GITHUB_OUTPUT
          "tag=v$raw"      >> $env:GITHUB_OUTPUT
          "isbeta=$isbeta" >> $env:GITHUB_OUTPUT

      - name: Build Windows installer
        shell: pwsh
        run: .\installers\desktop\build-windows.ps1 -Version ${{ steps.ver.outputs.version }}

      # STABLE uniquement : Release GitHub publique avec le .msi.
      # tag_name explicite : en declenchement manuel, github.ref est une branche,
      # donc on cible le tag derive (la release est attachee au bon tag).
      - name: Publish installer to GitHub Release (stable)
        if: ${{ steps.ver.outputs.isbeta == 'false' }}
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.ver.outputs.tag }}
          files: core/target/dist-out/*.msi
          fail_on_unmatched_files: true
          generate_release_notes: true

      # BETA uniquement : artefact PRIVE (pas de release publique). A recuperer
      # via l'onglet Actions puis a joindre a un post Patreon gate par palier.
      - name: Upload installer as private artifact (beta)
        if: ${{ steps.ver.outputs.isbeta == 'true' }}
        uses: actions/upload-artifact@v4
        with:
          name: loremind-beta-${{ steps.ver.outputs.version }}-msi
          path: core/target/dist-out/*.msi
          retention-days: 90

  # LINUX : AppImage (1 fichier, toutes distros) attache a la MEME release.
  # Tourne sur ubuntu-latest (jpackage ne cross-compile pas -> build natif Linux).
  # Brain empaquete via python-build-standalone (pas de Python embeddable Linux
  # officiel ; pas de PyInstaller -> pas de faux positif AV). Memes regles de
  # publication que windows : stable -> release publique ; beta -> artefact prive.
  linux:
    needs: tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event_name == 'workflow_dispatch' && format('v{0}', inputs.version) || github.ref }}

      # Apporte jpackage (empaquetage natif) dans le PATH.
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      # Pas de setup-python : le Brain utilise SON python-build-standalone
      # (telecharge par le script, avec son propre pip). Le host n'en a pas besoin.

      # Version (numerique X.Y.Z) + tag + isbeta, comme le job windows.
      - name: Derive version
        id: ver
        run: |
          if [ '${{ github.event_name }}' = 'workflow_dispatch' ]; then
            raw='${{ inputs.version }}'
          else
            raw='${{ github.ref_name }}'
          fi
          raw="${raw#v}"                       # 0.15.0  ou  0.15.0-beta
          num="${raw%%-*}"                     # 0.15.0
          case "$raw" in *-beta*) isbeta=true ;; *) isbeta=false ;; esac
          echo "version=$num"   >> "$GITHUB_OUTPUT"
          echo "tag=v$raw"      >> "$GITHUB_OUTPUT"
          echo "isbeta=$isbeta" >> "$GITHUB_OUTPUT"

      - name: Build Linux AppImage
        env:
          # Authentifie l'appel API GitHub (resolution python-build-standalone) :
          # evite le rate limit 60/h anonyme des runners partages.
          GITHUB_TOKEN: ${{ github.token }}
        run: bash ./installers/desktop/build-linux.sh --version ${{ steps.ver.outputs.version }}

      # STABLE uniquement : attache l'AppImage a la release publique (meme tag que
      # le .msi -> les deux installeurs sur la meme release).
      - name: Publish AppImage to GitHub Release (stable)
        if: ${{ steps.ver.outputs.isbeta == 'false' }}
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.ver.outputs.tag }}
          files: core/target/dist-out/DM_Loremind*.AppImage
          fail_on_unmatched_files: true

      # BETA uniquement : artefact PRIVE (pas de release publique).
      - name: Upload AppImage as private artifact (beta)
        if: ${{ steps.ver.outputs.isbeta == 'true' }}
        uses: actions/upload-artifact@v4
        with:
          name: loremind-beta-${{ steps.ver.outputs.version }}-appimage
          path: core/target/dist-out/DM_Loremind*.AppImage
          retention-days: 90
