name: CI
on:
  workflow_dispatch:
  push:
    branches-ignore:
      - dependabot/**
    tags:
      - v[0-9]+\.[0-9]+\.[0-9]+
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.repository }}/${{ github.ref }}
  cancel-in-progress: true

env:
  JAVA_VERSION: 17
  NODE_VERSION: 16.14.2

  ARTIFACTS_CYPRESS_TESTS_NAME: cypress-tests
  ARTIFACTS_CYPRESS_TESTS_PATH: web/cypress
  ARTIFACTS_TASKANA_JARS_NAME: taskana-jars
  ARTIFACTS_TASKANA_JARS_PATH: ~/.m2/repository/pro/taskana
  ARTIFACTS_TASKANA_WEB_NAME: taskana-web
  ARTIFACTS_TASKANA_WEB_PATH: web/dist
  ARTIFACTS_JACOCO_REPORTS_NAME: jacoco-reports
  ARTIFACTS_JACOCO_REPORTS_PATH: "**/jacoco.exec"

  CACHE_WEB_NAME: web
  # IMPORTANT: this cannot start with CACHE_MAVEN_NAME's value
  # because the 'compile_backend' job would otherwise use this as a fallback cache.
  CACHE_MAVEN_FOR_WEB_NAME: mvn-for-web
  CACHE_MAVEN_NAME: maven
  CACHE_SONAR_NAME: sonar

jobs:
  compile_backend:
    name: Compile all maven modules
    runs-on: ubuntu-20.04
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Cache maven dependencies
        id: cache
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}
      - name: Change versions to match tag
        run: ci/change_version.sh -m .
      - name: Compile & build
        run: ./mvnw -B install -DskipTests -Dasciidoctor.skip -Djacoco.skip
      - name: Populate cache
        if: steps.cache.outputs.cache-hit != 'true'
        run: |
          ./mvnw -B dependency:go-offline
          ./mvnw -B test -Dtest=GibtEsNet -Dsurefire.failIfNoSpecifiedTests=false
      - name: Upload taskana artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_JARS_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
          if-no-files-found: error
      - name: Remove taskana artifacts from cache
        run: rm -rf ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  compile_frontend:
    name: Compile taskana-web
    runs-on: ubuntu-20.04
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Use Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4.0.2
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Cache web dependencies
        id: web-cache
        uses: actions/cache@v4
        with:
          path: web/node_modules
          key: ${{ runner.OS }}-${{ env.CACHE_WEB_NAME }}-${{ hashFiles('**/yarn.lock') }}
      - name: Cache maven dependencies (for web)
        id: maven-cache
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.OS }}-${{ env.CACHE_MAVEN_FOR_WEB_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.OS }}-${{ env.CACHE_MAVEN_FOR_WEB_NAME }}
      - name: Populate maven cache
        run: ./mvnw -B dependency:go-offline -pl :taskana-web -am
        if: steps.maven-cache.outputs.cache-hit != 'true'
      - name: Install Dependencies
        if: steps.web-cache.outputs.cache-hit != 'true'
        working-directory: web
        run: yarn ci
      - name: Compile & build
        working-directory: web
        run: |
          yarn lint
          yarn build:prod
      - name: Build maven artifact
        run: ./mvnw -B install -pl :taskana-web -am
      - name: Upload taskana-web dist artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_WEB_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_WEB_PATH }}
          if-no-files-found: error
      - name: Remove taskana artifacts from cache
        run: rm -rf ~/.m2/repository/pro/taskana
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  test_frontend:
    runs-on: ubuntu-20.04
    name: Test taskana-web
    needs: [ compile_frontend ]
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Use Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4.0.2
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Cache web dependencies
        id: web-cache
        uses: actions/cache@v4
        with:
          path: web/node_modules
          key: ${{ runner.OS }}-${{ env.CACHE_WEB_NAME }}-${{ hashFiles('**/yarn.lock') }}
      # Theoretically this step below not necessary because we reuse the cache from the 'compile_frontend' job.
      # Sometimes the cache is not created, therefore this is a fallback.
      - name: Install Dependencies
        if: steps.web-cache.outputs.cache-hit != 'true'
        working-directory: web
        run: yarn ci
      - name: Cache maven dependencies (for web)
        id: maven-cache
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.OS }}-${{ env.CACHE_MAVEN_FOR_WEB_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.OS }}-${{ env.CACHE_MAVEN_FOR_WEB_NAME }}
      # Theoretically this step below not necessary because we reuse the cache from the 'compile_frontend' job.
      # Sometimes the cache is not created, therefore this is a fallback.
      - name: Populate cache
        run: ./mvnw -B dependency:go-offline -pl :taskana-web -am
        if: steps.maven-cache.outputs.cache-hit != 'true'
      - name: Test
        working-directory: web
        run: yarn run test -- --coverageReporters text-summary
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  test_e2e:
    runs-on: ubuntu-20.04
    name: Test E2E
    needs: [ compile_frontend, compile_backend ]
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Use Node.js ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4.0.2
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Cache web dependencies
        id: web-cache
        uses: actions/cache@v4
        with:
          path: web/node_modules
          key: ${{ runner.OS }}-${{ env.CACHE_WEB_NAME }}-${{ hashFiles('**/yarn.lock') }}
      # Theoretically this step below not necessary because we reuse the cache from the 'compile_frontend' job.
      # Sometimes the cache is not created, therefore this is a fallback.
      - name: Install Dependencies
        if: steps.web-cache.outputs.cache-hit != 'true'
        working-directory: web
        run: yarn ci
      - name: Cache maven dependencies
        id: maven-cache
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
      - name: Download taskana artifacts
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_JARS_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
      - name: Change versions to match tag
        run: ci/change_version.sh -m .
        # Theoretically this step below not necessary because we reuse the cache from the 'compile_frontend' job.
        # Sometimes the cache is not created, therefore this is a fallback.
      - name: Populate cache
        run: ./mvnw -B dependency:go-offline -pl :taskana-rest-spring-example-boot -am
        if: steps.maven-cache.outputs.cache-hit != 'true'
      - name: Download taskana-web dist artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_WEB_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_WEB_PATH }}
      - name: Build frontend
        run: ./mvnw install -pl :taskana-web
      - name: Cypress tests
        working-directory: web
        run: |
          ../mvnw -B spring-boot:run -P history.plugin -f .. -pl :taskana-rest-spring-example-boot &> /dev/null &
          npx wait-port -t 30000 localhost:8080 && yarn run e2e-standalone --spec "cypress/integration/monitor/**"
      - name: Upload Cypress tests
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_CYPRESS_TESTS_NAME }}
          path: ${{ env.ARTIFACTS_CYPRESS_TESTS_PATH }}
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  test_backend:
    runs-on: ubuntu-20.04
    name: Test ${{ matrix.module }} on ${{ matrix.database }}
    needs: [ compile_backend ]
    strategy:
      matrix:
        module:
          - taskana-common
          - taskana-common-security
          - taskana-common-data
          - taskana-common-logging
          - taskana-common-test
          - taskana-core
          - taskana-core-test
          - taskana-cdi
          - taskana-cdi-example
          - taskana-test-api
          - taskana-spring
          - taskana-spring-example
          - taskana-spi-routing-dmn-router
          - taskana-routing-rest
          - taskana-rest-spring
          - taskana-rest-spring-test-lib
          - taskana-rest-spring-example-common
          - taskana-loghistory-provider
          - taskana-simplehistory-provider
          - taskana-simplehistory-rest-spring
        database:
          - H2
        include:
          - module: taskana-core
            database: POSTGRES
          - module: taskana-core
            database: DB2
          - module: taskana-core
            database: ORACLE
          - module: taskana-core-test
            database: POSTGRES
          - module: taskana-core-test
            database: DB2
          - module: taskana-core-test
            database: ORACLE
          - module: taskana-test-api
            database: POSTGRES
          - module: taskana-test-api
            database: DB2
          - module: taskana-test-api
            database: ORACLE
          - module: taskana-simplehistory-provider
            database: DB2
          - module: taskana-simplehistory-provider
            database: POSTGRES
          - module: taskana-simplehistory-provider
            database: ORACLE
          - module: taskana-rest-spring-example-boot
            database: DB2
          - module: taskana-rest-spring-example-boot
            database: ORACLE
          - module: taskana-rest-spring-example-wildfly
            database: POSTGRES
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Cache maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}
      - name: Download taskana artifacts
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_JARS_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
      - name: Change versions to match tag
        run: |
          ci/change_version.sh -m .
          ci/update_taskana_dependency_for_wildfly.sh
      - name: Generate JavaDoc for Rest Documentation
        if: matrix.module == 'taskana-simplehistory-rest-spring'
        run: ./mvnw -B validate -pl :taskana-rest-spring
      - name: Test
        run: ./mvnw -B verify -pl :${{matrix.module}} -Dcheckstyle.skip
        env:
          DB: ${{ matrix.database }}
      - name: Upload JaCoCo Report
        if: matrix.database == 'H2'
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_JACOCO_REPORTS_NAME }}-${{ matrix.module }}
          path: ${{ env.ARTIFACTS_JACOCO_REPORTS_PATH }}
          if-no-files-found: ignore
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  release_artifacts:
    runs-on: ubuntu-22.04
    name: Release artifacts to OSS Sonatype
    if: github.repository == 'Taskana/taskana' && ( startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/master' ) && github.head_ref == ''
    needs: [ test_frontend, test_e2e, test_backend ]
    # as documented in the gpg manual (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html)
    # we should execute this command before interacting with gpg (otherwise gpg won't work)
    env:
      GPG_TTY: $(tty)
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # necessary for push back
          # NOTE @v2 uses the token as an auth http header. Set it to
          # a Personal Access Token instead of secrets.GITHUB_TOKEN
          # so that tag pushes trigger repo push events.
          # source: https://github.community/t/action-does-not-trigger-another-on-push-tag-action/17148/7
          token: ${{ secrets.ADMIN_PERSONAL_ACCESS_TOKEN }}
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Cache maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}
      - name: Download taskana artifacts
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_JARS_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
      - name: Download taskana-web dist artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_WEB_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_WEB_PATH }}
      - name: Import GPG Key
        run: echo -n "$GPG_KEY" | base64 --decode | gpg --batch --import
        env:
          GPG_KEY: ${{ secrets.GPG_KEY }}
      - name: Change versions to match tag
        run: ci/change_version.sh -m .
      - name: Release artifacts to OSS Sonatype
        run: |
          ./mvnw -B deploy -P $([[ "$GITHUB_REF" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "release" || echo "snapshot") \
          --settings ci/mvnsettings.xml -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip \
          -pl :taskana-parent,\
          :taskana-common-parent,:taskana-common-logging,:taskana-common,:taskana-common-security,\
          :taskana-common-data,:taskana-common-test,\
          :taskana-lib-parent,:taskana-core,:taskana-cdi,:taskana-spring,\
          :taskana-rest-parent,:taskana-web,:taskana-rest-spring,\
          :taskana-history-parent,:taskana-simplehistory-provider,:taskana-simplehistory-rest-spring,:taskana-loghistory-provider,\
          :taskana-routing-parent,:taskana-spi-routing-dmn-router,:taskana-routing-rest
        env:
          GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }}
          GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
          OSSRH_JIRA_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }}
          OSSRH_JIRA_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }}
      - name: Update version to next snapshot and push back
        run: |
          ci/change_version.sh -i -m .
          ci/update_taskana_dependency_for_wildfly.sh -i
          ci/commitPoms.sh rest/taskana-rest-spring-example-wildfly/src/test/java/pro/taskana/example/wildfly/AbstractAccTest.java
        env:
          GH_EMAIL: ${{ secrets.GH_EMAIL }}
          GH_USERNAME: ${{ secrets.GH_USERNAME }}
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  deploy_to_azure:
    runs-on: ubuntu-20.04
    name: Deploy demo app to Microsoft Azure
    if: github.repository == 'Taskana/taskana' && github.ref == 'refs/heads/master' && github.head_ref == ''
    needs: [ test_frontend, test_e2e, test_backend ]
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Cache maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}
      - name: Download taskana artifacts
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_JARS_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_JARS_PATH }}
      - name: Download taskana-web dist artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACTS_TASKANA_WEB_NAME }}
          path: ${{ env.ARTIFACTS_TASKANA_WEB_PATH }}
      - name: Build taskana-web
        run: ./mvnw -B install -pl :taskana-web
      - name: Generate Javadoc
        run: ./mvnw -B clean javadoc:jar -pl :taskana-core,:taskana-cdi,:taskana-spring
      - name: Generate Rest Documentation
        run: ./mvnw -B test asciidoctor:process-asciidoc -Dtest=*DocTest -pl :taskana-rest-spring,:taskana-simplehistory-rest-spring,:taskana-routing-rest -Dcheckstyle.skip -Djacoco.skip
      - name: Build Example Application
        run: ./mvnw -B install -P history.plugin -P dmn-routing.plugin -pl :taskana-rest-spring-example-boot -DskipTests -Dcheckstyle.skip -Dmaven.javadoc.skip -Djacoco.skip
      - name: Verify Example Application contains documentation
        run: ci/verify_docs_jar.sh
      - name: Login to Microsoft Azure
        uses: Azure/login@v2
        with:
          creds: '{"clientId":"${{ secrets.CLIENT_ID }}","clientSecret":"${{ secrets.CLIENT_SECRET }}","subscriptionId":"${{ secrets.SUBSCRIPTION_ID }}","tenantId":"${{ secrets.TENANT_ID }}"}'
      - name: Deploy to Microsoft Azure
        uses: Azure/webapps-deploy@v3
        with:
          app-name: taskana
          package: rest/taskana-rest-spring-example-boot/target/taskana-rest-spring-example-boot.jar
      - name: Wait for Azure for 60 seconds
        uses: jakejarvis/wait-action@master
        with:
          time: '60s'
      - name: Smoke test documentation
        run: ci/verify_docs_alive.sh
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4

  upload_to_sonar:
    runs-on: ubuntu-20.04
    name: Upload SonarQube analysis to sonarcloud
    # no pull request and not on release
    if: github.head_ref == '' && !startsWith(github.ref, 'refs/tags')
    needs: [ test_frontend, test_e2e, test_backend ]
    steps:
      - name: Git checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
      - name: Set up JDK ${{ env.JAVA_VERSION }}
        uses: actions/setup-java@v4
        with:
          distribution: adopt
          java-version: ${{ env.JAVA_VERSION }}
      - name: Cache SonarCloud packages
        uses: actions/cache@v4
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-${{ env.CACHE_SONAR_NAME }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_SONAR_NAME }}
      - name: Cache maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-${{ env.CACHE_MAVEN_NAME }}
      - name: Download JaCoCo reports
        uses: actions/download-artifact@v4
        with:
          pattern: ${{ env.ARTIFACTS_JACOCO_REPORTS_NAME }}-*
          merge-multiple: true
      - name: Install taskana
        run: ./mvnw -B install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Dmaven.javadoc.skip
      - name: Upload SonarQube analysis
        run: ./mvnw -B sonar:sonar
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }}
          SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }}
      - name: Cancel workflow
        if: failure()
        uses: andymckay/cancel-action@0.4
