name: GH Actions CI

on:
  push:
    branches:
      - 'main'
  pull_request:
    branches:
      - 'main'

permissions: { } # none

# See https://github.com/hibernate/hibernate-orm/pull/4615 for a description of the behavior we're getting.
concurrency:
  # Consider that two builds are in the same concurrency group (cannot run concurrently)
  # if they use the same workflow and are about the same branch ("ref") or pull request.
  group: "workflow = ${{ github.workflow }}, ref = ${{ github.event.ref }}, pr = ${{ github.event.pull_request.id }}"
  # Cancel previous builds in the same concurrency group even if they are in progress
  # for pull requests or pushes to forks (not the upstream repository).
  cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'hibernate/hibernate-orm' }}

jobs:

  # Main job for h2/docker DBs.
  build:
    permissions:
      contents: read
    name: OpenJDK 17 - ${{matrix.rdbms}}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - rdbms: h2
          - rdbms: hsqldb
          - rdbms: mysql
          - rdbms: mariadb
          - rdbms: postgresql
          - rdbms: edb
          - rdbms: oracle
          - rdbms: db2
          - rdbms: mssql
          - rdbms: sybase
    # Running with CockroachDB requires at least 2-4 vCPUs, which we don't have on GH Actions runners
    #          - rdbms: cockroachdb
    # Running with HANA requires at least 8GB memory just for the database, which we don't have on GH Actions runners
    #          - rdbms: hana
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Reclaim Disk Space
        run: .github/ci-prerequisites.sh
      - name: Start database
        env:
          RDBMS: ${{ matrix.rdbms }}
        run: ci/database-start.sh
      - name: Set up Java 17
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Generate cache key
        id: cache-key
        run: |
          CURRENT_BRANCH="${{ github.repository != 'hibernate/hibernate-orm' && 'fork' || github.base_ref || github.ref_name }}"
          CURRENT_MONTH=$(/bin/date -u "+%Y-%m")
          CURRENT_DAY=$(/bin/date -u "+%d")
          ROOT_CACHE_KEY="buildtool-cache"
          echo "buildtool-monthly-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}" >> $GITHUB_OUTPUT
          echo "buildtool-monthly-branch-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}" >> $GITHUB_OUTPUT
          echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT
      - name: Cache Maven/Gradle Dependency/Dist Caches
        id: cache-maven
        uses: actions/cache@v4
        # if it's not a pull request, we restore and save the cache
        if: github.event_name != 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          # A new cache will be stored daily. After that first store of the day, cache save actions will fail because the cache is immutable but it's not a problem.
          # The whole cache is dropped monthly to prevent unlimited growth.
          # The cache is per branch but in case we don't find a branch for a given branch, we will get a cache from another branch.
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-
      - name: Restore Maven/Gradle Dependency/Dist Caches
        uses: actions/cache/restore@v4
        # if it a pull request, we restore the cache but we don't save it
        if: github.event_name == 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-

      - name: Run build script
        run: ./ci/build-github.sh
        shell: bash
        env:
          RDBMS: ${{ matrix.rdbms }}
          # For jobs running on 'push', publish build scan and cache immediately.
          # This won't work for pull requests, since they don't have access to secrets.
          POPULATE_REMOTE_GRADLE_CACHE: ${{ github.event_name == 'push' && github.repository == 'hibernate/hibernate-orm' && 'true' || 'false' }}
          DEVELOCITY_ACCESS_KEY: "${{ secrets.DEVELOCITY_ACCESS_KEY }}"

      # For jobs running on 'pull_request', upload build scan data.
      # The actual publishing must be done in a separate job (see ci-report.yml).
      # We don't write to the remote cache as that would be unsafe.
      - name: Upload GitHub Actions artifact for the Develocity build scan
        uses: actions/upload-artifact@v4
        if: "${{ github.event_name == 'pull_request' && !cancelled() }}"
        with:
          name: build-scan-data-${{ matrix.rdbms }}
          path: ~/.gradle/build-scan-data

      - name: Upload test reports (if Gradle failed)
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: test-reports-java11-${{ matrix.rdbms }}
          path: |
            ./**/target/reports/tests/
      - name: Omit produced artifacts from build cache
        run: ./ci/before-cache.sh

  # Job for builds on Atlas (Oracle) infrastructure.
  # This is untrusted, even for pushes, see below.
  atlas:
    permissions:
      contents: read
    name: GraalVM 21 - ${{matrix.rdbms}}
    #    runs-on: ubuntu-latest
    runs-on: [ self-hosted, Linux, X64, OCI ]
    strategy:
      fail-fast: false
      matrix:
        include:
          - rdbms: oracle_atps
          - rdbms: oracle_db19c
          - rdbms: oracle_db21c
          - rdbms: oracle_db23c
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Reclaim disk space and sanitize user home
        run: .github/ci-prerequisites-atlas.sh
      - name: Start database
        env:
          RDBMS: ${{ matrix.rdbms }}
          RUNID: ${{ github.run_number }}
        run: ci/database-start.sh
      - name: Set up Java 21
        uses: graalvm/setup-graalvm@v1
        with:
          distribution: 'graalvm'
          java-version: '21'

      - name: Generate cache key
        id: cache-key
        run: |
          CURRENT_BRANCH="${{ github.repository != 'hibernate/hibernate-orm' && 'fork' || github.base_ref || github.ref_name }}"
          CURRENT_MONTH=$(/bin/date -u "+%Y-%m")
          CURRENT_DAY=$(/bin/date -u "+%d")
          ROOT_CACHE_KEY="buildtool-cache-atlas"
          echo "buildtool-monthly-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}" >> $GITHUB_OUTPUT
          echo "buildtool-monthly-branch-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}" >> $GITHUB_OUTPUT
          echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT
      - name: Cache Maven/Gradle Dependency/Dist Caches
        id: cache-maven
        uses: actions/cache@v4
        # if it's not a pull request, we restore and save the cache
        if: github.event_name != 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          # A new cache will be stored daily. After that first store of the day, cache save actions will fail because the cache is immutable but it's not a problem.
          # The whole cache is dropped monthly to prevent unlimited growth.
          # The cache is per branch but in case we don't find a branch for a given branch, we will get a cache from another branch.
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-
      - name: Restore Maven/Gradle Dependency/Dist Caches
        uses: actions/cache/restore@v4
        # if it a pull request, we restore the cache but we don't save it
        if: github.event_name == 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-

      - name: Run build script
        env:
          RDBMS: ${{ matrix.rdbms }}
          RUNID: ${{ github.run_number }}
          # These runners have no HOME variable set by default, we need to explicitly set it to make the build work
          HOME: /root
        run: ./ci/build-github.sh
        shell: bash
      # Upload build scan data.
      # The actual publishing must be done in a separate job (see ci-report.yml).
      # We don't write to the remote cache as that would be unsafe.
      # That's even on push, because we do not trust Atlas runners to hold secrets: they are shared infrastructure.
      - name: Upload GitHub Actions artifact for the Develocity build scan
        uses: actions/upload-artifact@v4
        if: "${{ !cancelled() }}"
        with:
          name: build-scan-data-${{ matrix.rdbms }}
          path: ~/.gradle/build-scan-data
      - name: Upload test reports (if Gradle failed)
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: test-reports-java11-${{ matrix.rdbms }}
          path: |
            ./**/target/reports/tests/
      - name: Omit produced artifacts from build cache
        run: ./ci/before-cache.sh

  # Static code analysis check
  format_checks:
    permissions:
      contents: read
    name: Static code analysis
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Reclaim disk space and sanitize user home
        run: .github/ci-prerequisites-atlas.sh
      - name: Set up Java 17
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Generate cache key
        id: cache-key
        run: |
          CURRENT_BRANCH="${{ github.repository != 'hibernate/hibernate-orm' && 'fork' || github.base_ref || github.ref_name }}"
          CURRENT_MONTH=$(/bin/date -u "+%Y-%m")
          CURRENT_DAY=$(/bin/date -u "+%d")
          ROOT_CACHE_KEY="buildtool-cache-atlas"
          echo "buildtool-monthly-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}" >> $GITHUB_OUTPUT
          echo "buildtool-monthly-branch-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}" >> $GITHUB_OUTPUT
          echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT
      - name: Cache Maven/Gradle Dependency/Dist Caches
        id: cache-maven
        uses: actions/cache@v4
        # if it's not a pull request, we restore and save the cache
        if: github.event_name != 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          # A new cache will be stored daily. After that first store of the day, cache save actions will fail because the cache is immutable but it's not a problem.
          # The whole cache is dropped monthly to prevent unlimited growth.
          # The cache is per branch but in case we don't find a branch for a given branch, we will get a cache from another branch.
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-
      - name: Restore Maven/Gradle Dependency/Dist Caches
        uses: actions/cache/restore@v4
        # if it a pull request, we restore the cache but we don't save it
        if: github.event_name == 'pull_request'
        with:
          path: |
            ~/.m2/repository/
            ~/.m2/wrapper/
            ~/.gradle/caches/modules-2
            ~/.gradle/wrapper/
          key: ${{ steps.cache-key.outputs.buildtool-cache-key }}
          restore-keys: |
            ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}-
            ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}-    

      - name: Run build script
        run: ./gradlew formatChecks
        env:
          # For jobs running on 'push', publish build scan and cache immediately.
          # This won't work for pull requests, since they don't have access to secrets.
          POPULATE_REMOTE_GRADLE_CACHE: ${{ github.event_name == 'push' && github.repository == 'hibernate/hibernate-orm' && 'true' || 'false' }}
          DEVELOCITY_ACCESS_KEY: "${{ secrets.DEVELOCITY_ACCESS_KEY }}"

      # For jobs running on 'pull_request', upload build scan data.
      # The actual publishing must be done in a separate job (see ci-report.yml).
      # We don't write to the remote cache as that would be unsafe.
      - name: Upload GitHub Actions artifact for the Develocity build scan
        uses: actions/upload-artifact@v4
        if: "${{ github.event_name == 'pull_request' && !cancelled() }}"
        with:
          name: build-scan-data-sca
          path: ~/.gradle/build-scan-data

      - name: Upload test reports (if Gradle failed)
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: test-reports-java11-sca
          path: |
            ./**/target/reports/tests/
      - name: Omit produced artifacts from build cache
        run: ./ci/before-cache.sh