name: Deployment

on:
  push:
    branches:
      - main
      - main-release
    paths-ignore:
      - 'docs/**'
      - 'src/test/**'
      - 'README.md'
    tags:
       - '*'
  pull_request:
  merge_group:
  workflow_dispatch:
    inputs:
      notarization:
        type: boolean
        required: false
        default: false

env:
  SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }}
  AstrophysicsDataSystemAPIKey: ${{ secrets.AstrophysicsDataSystemAPIKey }}
  IEEEAPIKey: ${{ secrets.IEEEAPIKey }}
  BiodiversityHeritageApiKey: ${{ secrets.BiodiversityHeritageApiKey}}
  OSXCERT: ${{ secrets.OSX_SIGNING_CERT }}
  GRADLE_OPTS: -Xmx4g -Dorg.gradle.vfs.watch=false
  JAVA_OPTS: -Xmx4g

concurrency:
  group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}"
  cancel-in-progress: true

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-13]
        include:
          - os: ubuntu-latest
            displayName: linux
            archivePortable: tar -c -C build/distribution JabRef | pigz --rsyncable > build/distribution/JabRef-portable_linux.tar.gz && rm -R build/distribution/JabRef
          - os: windows-latest
            displayName: windows
            archivePortable: 7z a -r build/distribution/JabRef-portable_windows.zip ./build/distribution/JabRef && rm -R build/distribution/JabRef
          - os: macos-13  # intel image
            displayName: macOS
    runs-on: ${{ matrix.os }}
    outputs:
      major: ${{ steps.gitversion.outputs.Major }}
      minor: ${{ steps.gitversion.outputs.Minor }}
      branchname: ${{ steps.gitversion.outputs.branchName }}
    name: ${{ matrix.displayName }} installer and portable version
    steps:
      - name: Check secrets presence
        id: checksecrets
        shell: bash
        run: |
          if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
            echo "secretspresent=NO" >> $GITHUB_OUTPUT
            echo "❌ Secret BUILDJABREFPRIVATEKEY not present"
          else
            echo "secretspresent=YES" >> $GITHUB_OUTPUT
            echo "✔️ Secret BUILDJABREFPRIVATEKEY present"
          fi
        env:
          BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
      - name: Fetch all history for all tags and branches
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          submodules: 'true'
          show-progress: 'false'
      - name: Install pigz and cache (linux)
        if: (matrix.os == 'ubuntu-latest')
        uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: pigz
          version: 1.0
      - name: Install GitVersion
        uses: gittools/actions/gitversion/setup@v3.0.0
        with:
          versionSpec: "5.x"
      - name: Run GitVersion
        id: gitversion
        uses: gittools/actions/gitversion/execute@v3.0.0
      - name: Setup JDK
        uses: actions/setup-java@v4
        with:
          java-version: 21.0.2
          distribution: 'temurin'
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4
        with:
          gradle-home-cache-cleanup: true
      - name: Prepare merged jars and modules dir (macOS)
        # prepareModulesDir is executing a build, which should run through even if no upload to builds.jabref.org is made
        if: (matrix.os == 'macos-13') || (steps.checksecrets.outputs.secretspresent == 'NO')
        run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir
      - name: Setup macOS key chain
        if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d
        with:
          p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }}
          p12-password: ${{ secrets.OSX_CERT_PWD }}
          keychain-password: jabref
      - name: Setup macOS key chain for app id cert
        if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d
        with:
          p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }}
          p12-password: ${{ secrets.OSX_CERT_PWD }}
          create-keychain: false
          keychain-password: jabref
      - name: Build dmg (macOS)
        if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          jpackage \
          --module org.jabref/org.jabref.Launcher \
          --module-path ${{env.JAVA_HOME}}/jmods/:build/jlinkbase/jlinkjars \
          --add-modules org.jabref,org.jabref.merged.module  \
          --add-modules jdk.incubator.vector \
          --dest build/distribution \
          --app-content buildres/mac/jabrefHost.py \
          --app-content buildres/mac/native-messaging-host \
          --name JabRef \
          --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \
          --verbose \
          --mac-sign \
          --vendor "JabRef e.V." \
          --mac-package-identifier JabRef \
          --mac-package-name JabRef \
          --type dmg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \
          --mac-package-signing-prefix org.jabref \
          --mac-entitlements buildres/mac/jabref.entitlements \
          --icon src/main/resources/icons/jabref.icns \
          --resource-dir buildres/mac \
          --file-associations buildres/mac/bibtexAssociations.properties \
          --jlink-options --bind-services \
          --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref.merged.module \
          --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \
          --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \
          --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.base/javafx.collections=org.jabref \
          --java-options --add-opens=javafx.base/javafx.collections.transformation=org.jabref \
          --java-options --add-modules=jdk.incubator.vector
      - name: Build pkg (macOS)
        if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          jpackage \
          --module org.jabref/org.jabref.Launcher \
          --module-path ${{env.JAVA_HOME}}/jmods/:build/jlinkbase/jlinkjars \
          --add-modules org.jabref,org.jabref.merged.module  \
          --add-modules jdk.incubator.vector \
          --dest build/distribution \
          --app-content buildres/mac/jabrefHost.py \
          --app-content buildres/mac/native-messaging-host \
          --name JabRef \
          --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \
          --verbose \
          --mac-sign \
          --vendor "JabRef e.V." \
          --mac-package-identifier JabRef \
          --mac-package-name JabRef \
          --type pkg --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \
          --mac-package-signing-prefix org.jabref \
          --mac-entitlements buildres/mac/jabref.entitlements \
          --icon src/main/resources/icons/jabref.icns \
          --resource-dir buildres/mac \
          --file-associations buildres/mac/bibtexAssociations.properties \
          --jlink-options --bind-services \
          --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref.merged.module \
          --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \
          --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \
          --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \
          --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref \
          --java-options --add-opens=javafx.base/javafx.collections=org.jabref \
          --java-options --add-opens=javafx.base/javafx.collections.transformation=org.jabref \
          --java-options --add-modules=jdk.incubator.vector
      - name: Build runtime image and installer (linux, Windows)
        if: (matrix.os != 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jpackage jlinkZip
      - name: Package application image (linux, Windows)
        if: (matrix.os != 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: ${{ matrix.archivePortable }}
      - name: Rename files
        if: (matrix.os != 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: pwsh
        run: |
          get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"}
          get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"}
      - name: Repack deb file for Debian
        if: (matrix.os == 'ubuntu-latest') && (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          cd build/distribution
          ar x jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64.deb
          zstd -d < control.tar.zst | xz > control.tar.xz
          zstd -d < data.tar.zst | xz > data.tar.xz
          ar -m -c -a sdsd jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64_repackaged.deb debian-binary control.tar.xz data.tar.xz
          rm debian-binary control.tar.* data.tar.*
          mv -f jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64_repackaged.deb jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64.deb
      - name: Setup rsync (macOS)
        if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && ((matrix.os == 'macos-13') && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true))) }}
        run: brew install rsync
      - name: Setup rsync (Windows)
        if: (matrix.os == 'windows-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue'))
        # We want to have rsync available at this place to avoid uploading and downloading from GitHub artifact store (taking > 5 minutes in total)
        # We cannot use "action-rsyncer", because that requires Docker which is unavailable on Windows
        # We cannot use "setup-rsync", because that does not work on Windows
        # We do not use egor-tensin/setup-cygwin@v4, because it replaces the default shell
        run: choco install --no-progress rsync
      - name: Setup SSH key
        if: ${{ (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && ((matrix.os != 'macos-13') || !((startsWith(github.ref, 'refs/tags/') || (inputs.notarization == true)))) }}
        run: |
          echo "${{ secrets.buildJabRefPrivateKey }}" > sshkey
          chmod 600 sshkey
      - name: Upload to builds.jabref.org (Windows)
        if: (matrix.os == 'windows-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue'))
        shell: cmd
        # for rsync installed by chocolatey, we need the ssh.exe delivered with that installation
        run: |
          rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
      - name: Upload to builds.jabref.org (linux, macOS)
        # macOS: Negated condition of "Upload to GitHub workflow artifacts store (macOS)"
        #        Reason: We either upload the non-notarized files - or notarize the files later (and upload these later)
        # needs to be on one line; multi line does not work
        if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && ((matrix.os == 'ubuntu-latest') || ((matrix.os == 'macos-13') && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)))) }}
        shell: bash
        run: |
          rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
      - name: Upload to GitHub workflow artifacts store (macOS)
        if: (matrix.os == 'macos-13') && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)
        uses: actions/upload-artifact@v4
        with:
          # tbn = to-be-notarized
          name: JabRef-macOS-tbn
          path: build/distribution
          compression-level: 0 # no compression
      - name: Upload to GitHub workflow artifacts store
        if: (steps.checksecrets.outputs.secretspresent != 'YES')
        uses: actions/upload-artifact@v4
        with:
          name: JabRef-${{ matrix.os }}
          path: build/distribution
          compression-level: 0 # no compression
  announce:
    name: Comment on pull request
    runs-on: ubuntu-latest
    needs: [build]
    if: ${{ github.event_name == 'pull_request' }}
    steps:
      - name: Check secrets presence
        id: checksecrets
        shell: bash
        run: |
          if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
            echo "secretspresent=NO" >> $GITHUB_OUTPUT
            echo "❌ Secret BUILDJABREFPRIVATEKEY not present"
          else
            echo "secretspresent=YES" >> $GITHUB_OUTPUT
            echo "✔️ Secret BUILDJABREFPRIVATEKEY present"
          fi
        env:
          BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
      - name: Comment PR
        if: (steps.checksecrets.outputs.secretspresent == 'YES')
        uses: thollander/actions-comment-pull-request@v2
        with:
          message: |
            The build of this PR is available at <https://builds.jabref.org/pull/${{ github.event.pull_request.number }}/merge>.
          comment_tag: download-link
          mode: recreate
  notarize: # outsourced in a separate job to be able to rerun if this fails for timeouts
    name: macOS notarization
    runs-on: macos-13
    needs: [build]
    if: ${{ startsWith(github.ref, 'refs/tags/') || inputs.notarization == true }}
    steps:
      - name: Check secrets presence
        id: checksecrets
        shell: bash
        run: |
          if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
            echo "secretspresent=NO" >> $GITHUB_OUTPUT
            echo "❌ Secret BUILDJABREFPRIVATEKEY not present"
          else
            echo "secretspresent=YES" >> $GITHUB_OUTPUT
            echo "✔️ Secret BUILDJABREFPRIVATEKEY present"
          fi
        env:
          BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
      - name: Download from GitHub workflow artifacts store (macOS)
        if: (steps.checksecrets.outputs.secretspresent == 'YES')
        uses: actions/download-artifact@master
        with:
          name: JabRef-macOS-tbn
          path: build/distribution/
      - name: Notarize dmg
        if: (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          xcrun notarytool store-credentials "notarytool-profile" --apple-id "vorstand@jabref.org" --team-id "6792V39SK3" --password "${{ secrets.OSX_NOTARIZATION_APP_PWD }}"
          xcrun notarytool submit build/distribution/JabRef-${{ needs.build.outputs.major }}.${{ needs.build.outputs.minor }}.dmg --keychain-profile "notarytool-profile" --wait
          xcrun stapler staple build/distribution/JabRef-${{ needs.build.outputs.major }}.${{ needs.build.outputs.minor }}.dmg
      - name: Notarize pkg
        if: (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          xcrun notarytool store-credentials "notarytool-profile" --apple-id "vorstand@jabref.org" --team-id "6792V39SK3" --password "${{ secrets.OSX_NOTARIZATION_APP_PWD }}"
          xcrun notarytool submit build/distribution/JabRef-${{ needs.build.outputs.major }}.${{ needs.build.outputs.minor }}.pkg --keychain-profile "notarytool-profile" --wait
          xcrun stapler staple build/distribution/JabRef-${{ needs.build.outputs.major }}.${{ needs.build.outputs.minor }}.pkg
      - name: Upload to builds.jabref.org
        if: (steps.checksecrets.outputs.secretspresent == 'YES')
        shell: bash
        run: |
          echo "${{ secrets.buildJabRefPrivateKey }}" > sshkey
          chmod 600 sshkey
          rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ needs.build.outputs.branchname }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ needs.build.outputs.branchname }}/