name: start and run agent cucumber integration tests
on:
  workflow_dispatch:
  pull_request_target:
    types: [labeled]
concurrency:
  group: agent-integration-test-${{ github.event.number || github.run_id }}
  cancel-in-progress: true
env:
  GH_TOKEN: ${{ secrets.APK_BOT_TOKEN }}
jobs:
  runs_agent_cucumber_integration_tests_on_pull_request:
    if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
    runs-on: ubuntu-latest
    steps:
    - uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}
    - name: Create AKS Cluster and set context
      uses: azure/CLI@v1
      with:
        azcliversion: 2.44.1
        inlineScript: |
          az aks create --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" --name "agent-integ-${{ secrets.AKS_CLUSTER_NAME }}-${{ github.event.number || github.run_id }}" --enable-cluster-autoscaler --min-count 1 --max-count 3 --location "southeastasia" --node-vm-size Standard_DS4_v2 --generate-ssh-keys
    - uses: azure/aks-set-context@v3
      with:
         resource-group: '${{ secrets.AZURE_RESOURCE_GROUP }}'
         cluster-name: 'agent-integ-${{ secrets.AKS_CLUSTER_NAME }}-${{ github.event.number || github.run_id }}'
    - name: Create Namespace apk
      shell: sh
      run: |
        kubectl create namespace apk
        kubectl get ns
    - name: Checkout apk-repo.
      uses: actions/checkout@v3
      with:
        fetch-depth: "0"
        path: apk-repo
        token: ${{ secrets.APK_BOT_TOKEN }}
    - name: Set release username and email
      shell: sh
      run: |
        git config --global user.name ${{ secrets.APK_BOT_USER }}
        git config --global user.email ${{ secrets.APK_BOT_EMAIL }}

    - name: checkout pull request and merge.
      shell: sh
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      run: |
        cd apk-repo
        gh pr checkout ${{ github.event.number }} -b pr-${{ github.event.number }}
        git checkout pr-${{ github.event.number }}
        git merge origin/main

    - name: Helm release deploy APIM CP
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        helm repo add wso2apim https://github.com/wso2/helm-apim/releases/download/cp-4.3.0
        helm repo update
        helm install apim wso2apim/wso2am-cp --version 4.3.0 -f https://raw.githubusercontent.com/wso2/apk/main/helm-charts/samples/apim/cp/values.yaml -n apk --debug --wait --timeout 5m0s
        kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
        kubectl get pods -n apk
        kubectl get svc -n apk
        kubectl get ing -n apk
    - name: Helm release deploy APK DP
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        cd apk-repo/helm-charts
        helm repo add bitnami https://charts.bitnami.com/bitnami
        helm repo add jetstack https://charts.jetstack.io
        helm dependency build
        helm install apk -n apk . --debug --wait --timeout 15m0s \
        --set wso2.apk.cp.enabledSubscription=true \
        --set wso2.apk.cp.host="apim-apk-agent-service.apk.svc.cluster.local" \
        --set wso2.apk.cp.skipSSLVerification=true \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.skipSSLVerification=false \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.hostnameVerifier="AllowAll"
        kubectl get pods -n apk
        kubectl get svc -n apk

    - name: Helm release deploy APIM APK Agent
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        cd apk-repo/test/apim-apk-agent-test/agent-helm-chart
        helm dependency build
        helm install apim-apk-agent -n apk . --debug --wait --timeout 2m0s \
        --set controlPlane.serviceURL=https://apim-wso2am-cp-1-service.apk.svc.cluster.local:9443/ \
        --set controlPlane.eventListeningEndpoints="amqp://admin:admin@apim-wso2am-cp-1-service.apk.svc.cluster.local:5672?retries='10'&connectdelay='30'" \
        --set dataPlane.k8ResourceEndpoint=https://apk-wso2-apk-config-ds-service.apk.svc.cluster.local:9443/api/configurator/apis/generate-k8s-resources
        kubectl get pods -n apk
        kubectl get svc -n apk
    - name: Run test cases(CP to DP Flow)
      shell: sh
      run: |
        cd apk-repo/test/cucumber-tests
        sh ./scripts/agent-setup-hosts.sh
        ./gradlew runCpToDpTests
    - name: Helm release undeploy
      if: always()
      shell: sh
      run: |
        cd apk-repo/helm-charts
        kubectl describe pods -n apk
        kubectl get pods -n apk
        kubectl get svc -n apk
        kubectl get apis -n apk
        kubectl get applications -n apk
        kubectl get subscriptions -n apk
        kubectl get tokenissuers -n apk
        kubectl get httproutes -n apk
        kubectl get ing -n apk
        kubectl get pods -l app.kubernetes.io/name=apim-apk-agent | awk '{print $1}' | xargs -I{} kubectl logs {} -n apk
        helm uninstall apk -n apk
        helm uninstall apim -n apk
        helm uninstall apim-apk-agent -n apk
        kubectl delete namespace apk
    - name: Publish Test Report(CP to DP Flow)
      if: always()
      uses: malinthaprasan/action-surefire-report@v1
      with:
       report_paths: 'apk-repo/test/cucumber-tests/build/test-output/junitreports/*.xml'
       fail_on_test_failures: true
    - name: Create Namespace apk
      shell: sh
      run: |
        kubectl create namespace apk
        kubectl get ns
    - name: Checkout apk-repo.
      uses: actions/checkout@v3
      with:
        fetch-depth: "0"
        path: apk-repo
        token: ${{ secrets.APK_BOT_TOKEN }}
    - name: Set release username and email
      shell: sh
      run: |
        git config --global user.name ${{ secrets.APK_BOT_USER }}
        git config --global user.email ${{ secrets.APK_BOT_EMAIL }}

    - name: checkout pull request and merge.
      shell: sh
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      run: |
        cd apk-repo
        gh pr checkout ${{ github.event.number }} -b pr-${{ github.event.number }}
        git checkout pr-${{ github.event.number }}
        git merge origin/main

    - name: Helm release deploy APIM CP
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        helm repo add wso2apim https://github.com/wso2/helm-apim/releases/download/cp-4.3.0
        helm repo update
        helm install apim wso2apim/wso2am-cp --version 4.3.0 -f https://raw.githubusercontent.com/wso2/apk/main/helm-charts/samples/apim/cp/apk-cp/values.yaml -n apk --debug --wait --timeout 5m0s
        kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
        kubectl get pods -n apk
        kubectl get svc -n apk
        kubectl get ing -n apk
    - name: Helm release deploy APK DP(DP to CP Flow)
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        cd apk-repo/helm-charts
        helm repo add bitnami https://charts.bitnami.com/bitnami
        helm repo add jetstack https://charts.jetstack.io
        helm dependency build
        helm install apk -n apk . \
        --set wso2.apk.cp.enabledSubscription=true \
        --set wso2.apk.cp.enableApiPropagation=true \
        --set wso2.apk.cp.host="apim-apk-agent-service.apk.svc.cluster.local" \
        --set wso2.apk.cp.skipSSLVerification=true \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.mandateSubscriptionValidation=true \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.mandateInternalKeyValidation=true \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.skipSSLVerification=false \
        --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.hostnameVerifier="AllowAll"
        kubectl get pods -n apk
        kubectl get svc -n apk

    - name: Helm release deploy APIM APK Agent(DP to CP Flow)
      if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action')
      shell: sh
      run: |
        cd apk-repo/test/apim-apk-agent-test/agent-helm-chart
        helm dependency build
        helm install apim-apk-agent -n apk . --debug --wait --timeout 2m0s \
        --set controlPlane.serviceURL=https://apim-wso2am-cp-1-service.apk.svc.cluster.local:9443/ \
        --set controlPlane.eventListeningEndpoints="amqp://admin:admin@apim-wso2am-cp-1-service.apk.svc.cluster.local:5672?retries='10'&connectdelay='30'" \
        --set dataPlane.k8ResourceEndpoint=https://apk-wso2-apk-config-ds-service.apk.svc.cluster.local:9443/api/configurator/apis/generate-k8s-resources
        --set agent.mode=DPtoCP
        kubectl get pods -n apk
        kubectl get svc -n apk
    - name: Run test cases(DP to CP Flow)
      shell: sh
      run: |
        cd apk-repo/test/cucumber-tests
        sh ./scripts/agent-setup-hosts.sh
        ./gradlew runDpToCpTests
    - name: Helm release undeploy
      if: always()
      shell: sh
      run: |
        cd apk-repo/helm-charts
        kubectl describe pods -n apk
        kubectl get pods -n apk
        kubectl get svc -n apk
        kubectl get apis -n apk
        kubectl get applications -n apk
        kubectl get subscriptions -n apk
        kubectl get tokenissuers -n apk
        kubectl get httproutes -n apk
        kubectl get ing -n apk
        kubectl get pods -l app.kubernetes.io/name=apim-apk-agent | awk '{print $1}' | xargs -I{} kubectl logs {} -n apk
        helm uninstall apk -n apk
        helm uninstall apim -n apk
        helm uninstall apim-apk-agent -n apk
    - name: Delete AKS cluster
      if: always()
      uses: azure/CLI@v1
      with:
        azcliversion: 2.44.1
        inlineScript: |
          az aks delete --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --name agent-integ-${{ secrets.AKS_CLUSTER_NAME }}-${{ github.event.number || github.run_id }} --yes
    - name: Logout from azure
      if: always()
      uses: azure/CLI@v1
      with:
        azcliversion: 2.44.1
        inlineScript: |
          az logout
    - name: Publish Test Report(DP to CP Flow)
      if: always()
      uses: malinthaprasan/action-surefire-report@v1
      with:
       report_paths: 'apk-repo/test/cucumber-tests/build/test-output/junitreports/*.xml'
       fail_on_test_failures: true


Jfrog Scan

name: Jfrog Scan
on:
  workflow_dispatch:
  schedule:
    - cron: "0 20 * * *"
jobs:
  scan-images:
    runs-on: ubuntu-latest
    steps:
      - name: Setup JFrog CLI
        uses: jfrog/setup-jfrog-cli@v4
        env:
            JF_URL: ${{ secrets.JF_URL }}
            JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }}
      - name: Scan config-deployer-service
        shell: sh
        run: |
          docker pull wso2/apk-config-deployer-service:latest
          jf docker scan wso2/apk-config-deployer-service:latest
      - name: Scan IDP Domain Service
        shell: sh
        run: |
          docker pull wso2/apk-idp-domain-service:latest
          jf docker scan wso2/apk-idp-domain-service:latest
      - name: Scan IDP UI Service
        shell: sh
        run: |
          docker pull wso2/apk-idp-ui:latest
          jf docker scan wso2/apk-idp-ui:latest
      - name: Scan Adapter
        shell: sh
        run: |
          docker pull wso2/apk-adapter:latest
          jf docker scan wso2/apk-adapter:latest
      - name: Scan Common-controller
        shell: sh
        run: |
          docker pull wso2/apk-common-controller:latest
          jf docker scan wso2/apk-common-controller:latest
      - name: Scan Enforcer
        shell: sh
        run: |
          docker pull wso2/apk-enforcer:latest
          jf docker scan wso2/apk-enforcer:latest
      - name: Scan Router
        shell: sh
        run: |
          docker pull wso2/apk-router:latest
          jf docker scan wso2/apk-router:latest
      - name: Scan ratelimitter
        shell: sh
        run: |
          docker pull wso2/apk-ratelimiter:latest
          jf docker scan wso2/apk-ratelimiter:latest
      - name: Scan APIM APK Agent
        shell: sh
        run: |
          docker pull wso2/apim-apk-agent:latest
          jf docker scan wso2/apim-apk-agent:latest


Celeborn Benchmark

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Celeborn Benchmark

on:
  repository_dispatch:
    types: [ benchmark-command ]

jobs:
  regression:
    runs-on: self-hosted
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }}
          ref: ${{ github.event.client_payload.pull_request.head.ref }}
          token: ${{ secrets.REPO_ACCESS_TOKEN }}

      - name: Build
        run: |
          build/make-distribution.sh -Pspark-3.2
          rm -rf /home/hadoop/celeborn-toolkit/dist/*
          mv apache-celeborn-*.tgz /home/hadoop/celeborn-toolkit/dist/

      - name: Benchmark
        run: /home/hadoop/celeborn-toolkit/reg.sh benchmark

      - name: Upload Result
        uses: actions/upload-artifact@v3
        with:
          name: benchmark result
          path: /home/hadoop/celeborn-toolkit/result/


Celeborn User Triggered Benchmark

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Celeborn User Triggered Benchmark

on:
  workflow_dispatch:
    inputs:
      benchmark:
        description: 'benchmark'
        required: true
        default: 'default-benchmark'

jobs:
  regression:
    runs-on: self-hosted
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build
        run: |
          build/make-distribution.sh -Pspark-3.2
          rm -rf /home/hadoop/celeborn-toolkit/dist/*
          mv apache-celeborn-*.tgz /home/hadoop/celeborn-toolkit/dist/

      - name: Benchmark
        run: /home/hadoop/celeborn-toolkit/reg.sh benchmark

      - name: Upload Result
        uses: actions/upload-artifact@v3
        with:
          name: benchmark result
          path: /home/hadoop/celeborn-toolkit/result/


Mark stale issues and pull requests

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see: https://github.com/actions/stale
#

name: Mark stale issues and pull requests

on:
  schedule:
  - cron: '30 8 * * *'

jobs:
  stale:
    runs-on: ubuntu-22.04
    permissions:
      pull-requests: write
    steps:
      - uses: actions/stale@v5
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          days-before-pr-stale: 20
          days-before-pr-close: 10
          stale-pr-message: 'This PR is stale because it has been open 20 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
          close-pr-message: 'This issue was closed because it has been staled for 10 days with no activity.'


Dependency

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Dependency

on:
  pull_request:
    branches:
      - main
      - branch-*
    paths:
      - '**/pom.xml'
      - 'project/**'
      - '.github/workflows/deps.yml'
      - 'dev/dependencies.sh'
      - 'dev/deps/**'

concurrency:
  group: deps-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  sbt:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        module:
          - 'service'
          - 'spark-2.4'
          - 'spark-3.0'
          - 'spark-3.1'
          - 'spark-3.2'
          - 'spark-3.3'
          - 'spark-3.4'
          - 'spark-3.5'
          - 'flink-1.14'
          - 'flink-1.15'
          - 'flink-1.17'
          - 'flink-1.18'
          - 'flink-1.19'
          - 'mr'
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK 8
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: 8
        check-latest: false
    - name: SBT Check dependency list
      run: |
        ./dev/dependencies.sh --sbt --module ${{ matrix.module }} --check

  maven:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        module:
          - 'service'
          - 'spark-2.4'
          - 'spark-3.0'
          - 'spark-3.1'
          - 'spark-3.2'
          - 'spark-3.3'
          - 'spark-3.4'
          - 'spark-3.5'
          - 'flink-1.14'
          - 'flink-1.15'
          - 'flink-1.17'
          - 'flink-1.18'
          - 'flink-1.19'
          - 'mr'
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK 8
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: 8
        cache: maven
        check-latest: false
    - name: Maven Check dependency list
      run: |
        ./dev/dependencies.sh --module ${{ matrix.module }} --check


Integration Test

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Integration Test

on:
  push:
    branches:
      - main
      - branch-*
  pull_request:
    branches:
      - main
      - branch-*
env:
  MINIKUBE_VERSION: v1.29.0
  KUBERNETES_VERSION: v1.26.1

jobs:
  celeborn_integration_test:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v2
        name: Setup JDK with Maven
        with:
          distribution: zulu
          java-version: 8
          cache: maven
          check-latest: false
      - name: Build Celeborn Binary
        run:
          ./build/make-distribution.sh -Pspark-3.3 -Pgoogle-mirror
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      - name: Build Celeborn Docker Image
        uses: docker/build-push-action@v2
        with:
          context: ./dist
          file: ./docker/Dockerfile
          load: true
          tags: apache/celeborn:latest
      - name: Setup Helm
        uses: azure/setup-helm@v3
        with:
          version: v3.10.0
      - name: Setup Minikube
        run: |
          # https://minikube.sigs.k8s.io/docs/start/
          curl -LO https://github.com/kubernetes/minikube/releases/download/${MINIKUBE_VERSION}/minikube-linux-amd64
          sudo install minikube-linux-amd64 /usr/local/bin/minikube
          minikube start --cpus 2 --memory 4096 --kubernetes-version=${KUBERNETES_VERSION} --force
      - name: Deploy Celeborn
        run: |
          # https://minikube.sigs.k8s.io/docs/handbook/pushing/#7-loading-directly-to-in-cluster-container-runtime
          minikube image load apache/celeborn:latest
          helm install celeborn charts/celeborn -f ./tests/kubernetes-it/docker/helm/values.yaml
      - name: Wait Celeborn Ready
        run: >-
          ./build/mvn clean install
          -Pkubernetes-it
          -Pspark-3.3
          -Dtest=none
          -DwildcardSuites=org.apache.celeborn.integration.DeploySuite


License check

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: License check

# This GitHub workflow checks Apache License v2.0 Header is added if needed

on:
  pull_request:
    branches:
      - main
      - branch-*

concurrency:
  group: license-${{ github.ref }}
  cancel-in-progress: true

jobs:
  rat:
    name: License
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK 8
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: 8
          cache: 'maven'
          check-latest: false
      - run: |
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,flink-1.14
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,flink-1.15
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,flink-1.17
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,flink-1.18
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,spark-2.4
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,spark-3.3
          build/mvn org.apache.rat:apache-rat-plugin:check -Pgoogle-mirror,mr
      - name: Upload rat report
        if: failure()
        uses: actions/upload-artifact@v2
        with:
          name: rat-report
          path: "**/target/rat*.txt"


Celeborn CI

# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Celeborn CI

on:
  push:
    branches:
      - main
      - branch-*
  pull_request:
    branches:
      - main
      - branch-*

jobs:
  service:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
          - 17
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK ${{ matrix.java }}
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: ${{ matrix.java }}
        cache: maven
        check-latest: false
    - name: Test Service with Maven
      run: build/mvn -Pgoogle-mirror test
    - name: Upload test log
      if: failure()
      uses: actions/upload-artifact@v3
      with:
        name: service-unit-test-log
        path: |
          **/target/unit-tests.log
    - name: Upload coverage to Codecov
      if: matrix.java == 8
      uses: codecov/codecov-action@v3

  spark2:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
        spark:
          - '2.4'
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          cache: maven
          check-latest: false
      - name: Test with Maven
        run: |
          SPARK_BINARY_VERSION=${{ matrix.spark }}
          SPARK_MAJOR_VERSION=${SPARK_BINARY_VERSION%%.*}
          PROFILES="-Pgoogle-mirror,spark-${{ matrix.spark }}"
          TEST_MODULES="client-spark/common,client-spark/spark-${SPARK_MAJOR_VERSION},client-spark/spark-${SPARK_MAJOR_VERSION}-shaded,tests/spark-it"
          build/mvn $PROFILES -pl $TEST_MODULES -am clean install -DskipTests
          build/mvn $PROFILES -pl $TEST_MODULES test
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: spark-${{ matrix.spark }}-unit-test-log
          path: |
            **/target/unit-tests.log

  spark3:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
          - 17
        spark:
          - '3.0'
          - '3.1'
          - '3.2'
          - '3.3'
          - '3.4'
          - '3.5'
        shuffle-plugin-class:
          - 'org.apache.spark.shuffle.sort.io.LocalDiskShuffleDataIO'
          - 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
        exclude:
          # SPARK-33772: Spark supports JDK 17 since 3.3.0
          - java: 17
            spark: '3.0'
          - java: 17
            spark: '3.1'
          - java: 17
            spark: '3.2'
          # ShuffleDriverComponents#supportsReliableStorage was introduced in 3.5.0
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.0'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.1'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.2'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.3'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.4'
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK ${{ matrix.java }}
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: ${{ matrix.java }}
        cache: maven
        check-latest: false
    - name: Test with Maven
      run: |
        SPARK_BINARY_VERSION=${{ matrix.spark }}
        SPARK_MAJOR_VERSION=${SPARK_BINARY_VERSION%%.*}
        PROFILES="-Pgoogle-mirror,spark-${{ matrix.spark }}"
        TEST_MODULES="client-spark/common,client-spark/spark-${SPARK_MAJOR_VERSION},client-spark/spark-${SPARK_MAJOR_VERSION}-shaded,tests/spark-it"
        build/mvn $PROFILES -pl $TEST_MODULES -am clean install -DskipTests
        build/mvn $PROFILES -pl $TEST_MODULES -Dspark.shuffle.sort.io.plugin.class=${{ matrix.shuffle-plugin-class }} test
    - name: Upload test log
      if: failure()
      uses: actions/upload-artifact@v3
      with:
        name: spark-${{ matrix.spark }}-unit-test-log
        path: |
          **/target/unit-tests.log

  flink:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
        flink:
          - '1.14'
          - '1.15'
          - '1.17'
          - '1.18'
          - '1.19'
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          cache: maven
          check-latest: false
      - name: Test with Maven
        run: |
          PROFILES="-Pgoogle-mirror,flink-${{ matrix.flink }}"
          TEST_MODULES="client-flink/common,client-flink/flink-${{ matrix.flink }},client-flink/flink-${{ matrix.flink }}-shaded,tests/flink-it"
          build/mvn $PROFILES -pl $TEST_MODULES -am clean install -DskipTests
          build/mvn $PROFILES -pl $TEST_MODULES test
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: flink-${{ matrix.flink }}-unit-test-log
          path: |
            **/target/unit-tests.log

  mr:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          cache: maven
          check-latest: false
      - name: Test with Maven
        run: |
          PROFILES="-Pgoogle-mirror,mr"
          TEST_MODULES="client-mr/mr,client-mr/mr-shaded,tests/mr-it"
          build/mvn $PROFILES -pl $TEST_MODULES -am clean install -DskipTests
          build/mvn $PROFILES -pl $TEST_MODULES test
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: mr-unit-test-log
          path: |
            **/target/test/


Celeborn Regression

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Celeborn Regression

on:
  repository_dispatch:
    types: [ regression-command ]

jobs:
  regression:
    runs-on: self-hosted
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }}
          ref: ${{ github.event.client_payload.pull_request.head.ref }}
          token: ${{ secrets.REPO_ACCESS_TOKEN }}

      - name: Build
        run: |
          build/make-distribution.sh -Pspark-3.2
          rm -rf /home/hadoop/celeborn-toolkit/dist/*
          mv apache-celeborn-*.tgz /home/hadoop/celeborn-toolkit/dist/

      - name: Regression
        run: /home/hadoop/celeborn-toolkit/reg.sh regression

      - name: Upload Result
        uses: actions/upload-artifact@v3
        with:
          name: regression result
          path: /home/hadoop/celeborn-toolkit/result/


Celeborn User Triggered Regression

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Celeborn User Triggered Regression

on:
  workflow_dispatch:
    inputs:
      regression:
        description: "regression"
        required: true
        default: "default-regression"

jobs:

  regression:
    runs-on: self-hosted
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build
        run: |
          build/make-distribution.sh -Pspark-3.2
          rm -rf /home/hadoop/celeborn-toolkit/dist/*
          mv apache-celeborn-*.tgz /home/hadoop/celeborn-toolkit/dist/

      - name: Regression
        run: /home/hadoop/celeborn-toolkit/reg.sh regression

      - name: Upload Result
        uses: actions/upload-artifact@v3
        with:
          name: regression result
          path: /home/hadoop/celeborn-toolkit/result/


Celeborn SBT CI

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Celeborn SBT CI

on:
  push:
    branches:
      - main
      - branch-*
  pull_request:
    branches:
      - main
      - branch-*

jobs:
  service:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
          - 17
        scala:
          - '2.12.15'
          - '2.13.5'
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK ${{ matrix.java }}
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: ${{ matrix.java }}
        check-latest: false
    - name: Test Service with SBT
      run: |
        build/sbt ++${{ matrix.scala }} "clean; test"
    - name: Upload test log
      if: failure()
      uses: actions/upload-artifact@v3
      with:
          name: service-java-${{ matrix.java }}-scala-${{ matrix.scala }}-unit-test-log
          path: |
              **/target/test-reports/**

  spark2:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
        spark:
          - '2.4'
        scala:
          - '2.11.12'
          - '2.12.10'
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          check-latest: false
      - name: Test with SBT
        run: |
          build/sbt -Pspark-${{ matrix.spark }} ++${{ matrix.scala }} "clean; celeborn-spark-group/test"
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
            name: spark-${{ matrix.spark }}-unit-test-log
            path: |
                **/target/test-reports/**

  spark3:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        spark:
          - '3.0'
          - '3.1'
          - '3.2'
          - '3.3'
          - '3.4'
          - '3.5'
        java:
          - 8
          - 11
          - 17
        scala-binary:
          - '2.12'
          - '2.13'
        shuffle-plugin-class:
          - 'org.apache.spark.shuffle.sort.io.LocalDiskShuffleDataIO'
          - 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
        exclude:
          # SPARK-33772: Spark supports JDK 17 since 3.3.0
          - java: 17
            spark: '3.0'
          - java: 17
            spark: '3.1'
          - java: 17
            spark: '3.2'
          # Spark supports scala 2.13 since 3.2.0
          - scala-binary: '2.13'
            spark: '3.0'
          - scala-binary: '2.13'
            spark: '3.1'
          # ShuffleDriverComponents#supportsReliableStorage was introduced in 3.5.0
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.0'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.1'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.2'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.3'
          - shuffle-plugin-class: 'org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO'
            spark: '3.4'
        include:
          # Spark 3.0
          - spark: '3.0'
            scala-binary: '2.12'
            scala: '2.12.10'
          # Spark 3.1
          - spark: '3.1'
            scala-binary: '2.12'
            scala: '2.12.10'
          - spark: '3.1'
          # Spark 3.2
          - spark: '3.2'
            scala-binary: '2.12'
            scala: '2.12.15'
          - spark: '3.2'
            scala-binary: '2.13'
            scala: '2.13.5'
          # Spark 3.3
          - spark: '3.3'
            scala-binary: '2.12'
            scala: '2.12.15'
          - spark: '3.3'
            scala-binary: '2.13'
            scala: '2.13.5'
          # Spark 3.4
          - spark: '3.4'
            scala-binary: '2.12'
            scala: '2.12.17'
          - spark: '3.4'
            scala-binary: '2.13'
            scala: '2.13.5'
          # Spark 3.5
          - spark: '3.5'
            scala-binary: '2.12'
            scala: '2.12.18'
          - spark: '3.5'
            scala-binary: '2.13'
            scala: '2.13.5'
    steps:
    - uses: actions/checkout@v2
    - name: Setup JDK ${{ matrix.java }}
      uses: actions/setup-java@v2
      with:
        distribution: zulu
        java-version: ${{ matrix.java }}
        check-latest: false
    - name: Test with SBT
      run: |
        build/sbt -Dspark.shuffle.plugin.class=${{ matrix.shuffle-plugin-class }} -Pspark-${{ matrix.spark }} ++${{ matrix.scala }} "clean; celeborn-spark-group/test"
    - name: Upload test log
      if: failure()
      uses: actions/upload-artifact@v3
      with:
          name: spark-${{ matrix.spark }}-scala-${{ matrix.scala }}-unit-test-log
          path: |
              **/target/test-reports/**

  flink:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
        flink:
          - '1.14'
          - '1.15'
          - '1.17'
          - '1.18'
          - '1.19'
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          check-latest: false
      - name: Test with SBT
        run: |
          build/sbt -Pflink-${{ matrix.flink }} "clean; celeborn-flink-group/test"
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
            name: flink-${{ matrix.flink }}-unit-test-log
            path: |
                **/target/test-reports/**

  mr:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        java:
          - 8
          - 11
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK ${{ matrix.java }}
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: ${{ matrix.java }}
          check-latest: false
      - name: Test with SBT
        run: |
          build/sbt -Pmr "clean; celeborn-mr-group/test"
      - name: Upload test log
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: mr-unit-test-log
          path: |
            **/target/test/


Style check

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Style check

on:
  push:
    branches:
      - main
      - branch-*
  pull_request:
    branches:
      - main
      - branch-*

concurrency:
  group: style-${{ github.ref }}
  cancel-in-progress: true

jobs:
  rat:
    name: License
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
    steps:
      - uses: actions/checkout@v2
      - name: Setup JDK 8
        uses: actions/setup-java@v2
        with:
          distribution: zulu
          java-version: 8
          cache: 'maven'
          check-latest: false
      - run: |
          build/mvn spotless:check -Pgoogle-mirror,flink-1.14
          build/mvn spotless:check -Pgoogle-mirror,flink-1.15
          build/mvn spotless:check -Pgoogle-mirror,flink-1.17
          build/mvn spotless:check -Pgoogle-mirror,flink-1.18
          build/mvn spotless:check -Pgoogle-mirror,flink-1.19
          build/mvn spotless:check -Pgoogle-mirror,spark-2.4
          build/mvn spotless:check -Pgoogle-mirror,spark-3.3
          build/mvn spotless:check -Pgoogle-mirror,mr


Lint Check for Web

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Lint Check for Web

on:
  push:
    branches:
      - main
      - branch-*
  pull_request:
    branches:
      - main
      - branch-*

concurrency:
  group: style-web-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
    - uses: pnpm/action-setup@v2
      with:
        version: 8

    - name: Checkout code
      uses: actions/checkout@v4

    # When updating node and pnpm version, also need to update the version in `web/pom.xml`
    - name: Use Node.js v20.11.0
      uses: actions/setup-node@v4
      with:
        node-version: 20.11.0

    - name: Use pnpm v8.14.3
      uses: pnpm/action-setup@v2
      with:
        version: 8.14.3

    - name: Install dependencies
      run: pnpm install
      working-directory: ./web

    - name: Lint and format
      run: pnpm run lint
      working-directory: ./web


xpanse-release

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
---
name: xpanse-release

on:
  workflow_dispatch:
    inputs:
      ReleaseType:
        type: choice
        description: Select the version to released
        options:
          - Major Version
          - Minor Version
          - Patch Version

env:
  BOT_USER_NAME: eclipse-xpanse-bot
  BOT_EMAIL_ID: xpanse-bot@eclipse.org
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  release:
    runs-on: ubuntu-latest
    if: github.repository == 'eclipse-xpanse/xpanse'
    outputs:
      next-version: ${{ steps.new_version.outputs.next-version }}
    permissions:
      contents: write
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.BOT_GITHUB_TOKEN }}

      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'
          gpg-private-key: ${{ secrets.ORG_GPG_PRIVATE_KEY }}
          gpg-passphrase: ${{ secrets.ORG_GPG_PASSPHRASE }}
          server-username: ${{ secrets.ORG_OSSRH_USERNAME }}
          server-password: ${{ secrets.ORG_OSSRH_PASSWORD }}
          server-id: maven

      - name: Set current version env variable
        run: |
          echo "CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | sed s/-SNAPSHOT/""/g)" >> $GITHUB_ENV

      - name: Map input to next action
        run: |
          if [ "${{github.event.inputs.ReleaseType}}" = "Major Version" ]; then
            echo "VERSION_FRAGMENT=major" >> $GITHUB_ENV
          elif [ "${{github.event.inputs.ReleaseType}}" = "Minor Version" ]; then
            echo "VERSION_FRAGMENT=feature" >> $GITHUB_ENV
          elif [ "${{github.event.inputs.ReleaseType}}" = "Patch Version" ]; then
            echo "VERSION_FRAGMENT=bug" >> $GITHUB_ENV
          else
            echo "No matching feature type  found"
          fi

      - name: Set next development version environment variable
        id: new_version
        uses: christian-draeger/increment-semantic-version@1.1.0
        with:
          current-version: ${{ env.CURRENT_VERSION }}
          version-fragment: ${{ env.VERSION_FRAGMENT }}

      - name: Update POMs to version to be released
        run: mvn versions:set -DnewVersion=${{ steps.new_version.outputs.next-version }} -DgenerateBackupPoms=false

      - name: Build
        run: mvn clean deploy -PreleaseBuild -DskipTests

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Github Packages
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ env.BOT_USER_NAME }}
          password: ${{ secrets.BOT_GITHUB_DOCKER_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5.5.1
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build Docker Image and Push
        uses: docker/build-push-action@v6.0.0
        with:
          context: .
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.new_version.outputs.next-version }}
          labels: ${{ steps.meta.outputs.labels }}
          provenance: false

      - name: Push POM updates with release version
        uses: EndBug/add-and-commit@v9
        with:
          message: "[GitHub Action] Update release version"
          author_name: ${{ env.BOT_USER_NAME }}
          committer_name: ${{ env.BOT_USER_NAME }}
          author_email: ${{ env.BOT_EMAIL_ID }}
          committer_email: ${{ env.BOT_EMAIL_ID }}

      - name: Release Application
        env:
          JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME: ${{ secrets.ORG_OSSRH_USERNAME }}
          JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD: ${{ secrets.ORG_OSSRH_PASSWORD }}
          JRELEASER_GPG_PASSPHRASE: ${{ secrets.ORG_GPG_PASSPHRASE }}
          JRELEASER_GPG_SECRET_KEY: ${{ secrets.ORG_GPG_PRIVATE_KEY }}
          JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.ORG_GPG_PUBLIC_KEY }}
          JRELEASER_PROJECT_VERSION: ${{ steps.new_version.outputs.next-version }}
        uses: jreleaser/release-action@v2
        with:
          arguments: release

      - name: Upload JReleaser release output
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: jreleaser-release
          path: |
            out/jreleaser/trace.log
            out/jreleaser/output.properties

  upload-to-swagger-hub:
    runs-on: ubuntu-latest
    env:
      SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
      SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }}
    if: github.repository == 'eclipse-xpanse/xpanse'
    needs: release
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: v${{ needs.release.outputs.next-version }}

      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'

      - name: Generate API doc
        run: mvn verify -DskipTests -Pgenerate-openapi-doc

      - name: Install SwaggerHub CLI
        run: npm i -g swaggerhub-cli

      - name: Create API
        # takes the version automatically from the generated API doc.
        run: |
          swaggerhub api:create ${{ env.SWAGGERHUB_USER }}/XpanseRuntimeApi --file runtime/target/openapi.json --visibility=public --published=publish --setdefault

  prepare-for-next-development-cycle:
    runs-on: ubuntu-latest
    if: github.repository == 'eclipse-xpanse/xpanse'
    needs: release
    permissions:
      contents: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}
          token: ${{ secrets.BOT_GITHUB_TOKEN }}

      - name: Update next development version in POMs
        run: mvn versions:set -DnewVersion=${{ needs.release.outputs.next-version }}-SNAPSHOT -DgenerateBackupPoms=false

      - name: Push POM updates for next development cycle
        uses: EndBug/add-and-commit@v9
        with:
          message: "[GitHub Action] Prepare for next development cycle"
          author_name: ${{ env.BOT_USER_NAME }}
          committer_name: ${{ env.BOT_USER_NAME }}
          author_email: ${{ env.BOT_EMAIL_ID }}
          committer_email: ${{ env.BOT_EMAIL_ID }}


xpanse-ci

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
name: xpanse-ci

# Run this workflow every time a new commit push to the repository
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'
      - name: Set up OpenTofu
        uses: opentofu/setup-opentofu@v1
        with:
          tofu_version: 1.6.0
      - name: Build
        run: mvn --batch-mode --update-snapshots --no-transfer-progress verify
      - name: Generate API doc
        run: mvn verify -DskipTests -Pgenerate-openapi-doc
      - name: Validate OpenAPI definition
        uses: char0n/swagger-editor-validate@v1
        with:
          definition-file: runtime/target/openapi.json


xpanse-test-coverage

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
name: xpanse-test-coverage

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    if: github.repository == 'eclipse-xpanse/xpanse'

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: adopt

      - name: Set up OpenTofu
        uses: opentofu/setup-opentofu@v1
        with:
          tofu_version: 1.6.0

      - name: Test
        run: mvn --batch-mode --update-snapshots --no-transfer-progress -Djacoco.skip=false test

      - name: Extract Coverage Percentage
        run: |
          coverage=$(grep -oP '<td class="ctr2">\K(\d+)%' target/coverage-reports/index.html | head -n 1)
          coverage=${coverage%\%}
          echo "COVERAGE_PERCENTAGE=$coverage" >> $GITHUB_ENV

      - name: Create Coverage Badge
        uses: schneegans/dynamic-badges-action@v1.7.0
        with:
          auth: ${{ secrets.GIST_TOKEN }}
          gistID: 3d9c022b98734fbf615c21136abe4add
          filename: xpanse-coverage.json
          label: coverage
          message: ${{ env.COVERAGE_PERCENTAGE }}%
          valColorRange: ${{ env.COVERAGE_PERCENTAGE }}
          maxColorRange: 100
          minColorRange: 0


xpanse-release-docker-only

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
---
name: xpanse-release-docker-only
# This job simply builds a new image of the last available code and application version in the main branch and pushes the
# image to GitHub packages. No changes to POM are pushed to the repository.
on:
  workflow_dispatch:

env:
  BOT_USER_NAME: eclipse-xpanse-bot
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  release:
    runs-on: ubuntu-latest
    if: github.repository == 'eclipse-xpanse/xpanse'
    permissions:
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.BOT_GITHUB_TOKEN }}

      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'

      - name: Set current version env variable
        run: |
          echo "CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | sed s/-SNAPSHOT/""/g)" >> $GITHUB_ENV

      - name: Remove SNAPSHOT from POM version
        run: mvn versions:set -DremoveSnapshot -DgenerateBackupPoms=false

      - name: Build
        run: mvn clean deploy -PreleaseBuild -DskipTests

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Github Packages
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ env.BOT_USER_NAME }}
          password: ${{ secrets.BOT_GITHUB_DOCKER_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5.5.1
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build Docker Image and Push
        uses: docker/build-push-action@v6.0.0
        with:
          context: .
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.CURRENT_VERSION }}
          labels: ${{ steps.meta.outputs.labels }}
          provenance: false


xpanse-license-check

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
name: xpanse-license-check

# Run this workflow every time a new commit push to the repository
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  third-party-license-check:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'
      - name: Check 3rd Party Licenses
        run: mvn license-tool:license-check -Plicense-check


upload-to-swagger-hub-only

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
---
name: upload-to-swagger-hub-only
# This job generates the API doc for the version requested and uploads it to SwaggerHub.
on:
  workflow_dispatch:
    inputs:
      VersionToBeUploaded:
        type: string
        description: Enter the version of the API that must be uploaded.Eg., 1.0.1
      IsDefaultVersion:
        type: boolean
        default: false
        description: Is the version the new default version.

env:
  BOT_USER_NAME: eclipse-xpanse-bot
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  upload-to-swagger-hub:
    runs-on: ubuntu-latest
    env:
      SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
      SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }}
    if: github.repository == 'eclipse-xpanse/xpanse'
    permissions:
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: v${{github.event.inputs.VersionToBeUploaded}}

      - name: Set Up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: 21
          distribution: 'temurin'

      - name: Generate API doc
        run: mvn verify -DskipTests -Pgenerate-openapi-doc

      - name: Install SwaggerHub CLI
        run: npm i -g swaggerhub-cli

      - name: Create API with the version as default
        if: ${{ github.event.inputs.IsDefaultVersion == 'true' }}
        run: swaggerhub api:create ${{ env.SWAGGERHUB_USER }}/XpanseRuntimeApi --file runtime/target/openapi.json --visibility=public --published=publish --setdefault

      - name: Create API as Non-default
        if: ${{ github.event.inputs.IsDefaultVersion== 'false' }}
        run: swaggerhub api:create ${{ env.SWAGGERHUB_USER }}/XpanseRuntimeApi --file runtime/target/openapi.json --visibility=public --published=publish


delete-api-on-swaggerhub

#
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Huawei Inc.
#
---
name: delete-api-on-swaggerhub
# This job deletes API that is already published on swaggerhub.
on:
  workflow_dispatch:
    inputs:
      VersionToBeDeleted:
        type: string
        description: Enter the version of the API that must be deleted.Eg., 1.0.1

env:
  BOT_USER_NAME: eclipse-xpanse-bot
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  upload-to-swagger-hub:
    runs-on: ubuntu-latest
    env:
      SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
      SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }}
    if: github.repository == 'eclipse-xpanse/xpanse'
    permissions:
      packages: write
    steps:
      - name: Install SwaggerHub CLI
        run: npm i -g swaggerhub-cli

      - name: Unpublish API
        run: swaggerhub api:unpublish ${{ env.SWAGGERHUB_USER }}/XpanseRuntimeApi/v${{github.event.inputs.VersionToBeDeleted}}
        continue-on-error: true

      - name: Delete provided API version
        run: swaggerhub api:delete ${{ env.SWAGGERHUB_USER }}/XpanseRuntimeApi/v${{github.event.inputs.VersionToBeDeleted}} --force


JVM and OS matrix

name: JVM and OS matrix

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

jobs:
  build:
    concurrency:
      group: pull_request-${{ github.event_name }}-${{ github.head_ref }}-${{ matrix.os }}-${{ matrix.java-version }}
      cancel-in-progress: true
    runs-on: ${{matrix.os}}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        java-version: [ 17, 21, 22 ] #Latest two LTS + latest non-LTS.
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: ${{matrix.java-version}}
          distribution: 'temurin'
          cache: 'maven'

      - name: Build and test timefold-solver
        run: mvn -B -Dfull verify


Release

# Axioms of the release pipeline:
# - Each release starts from timefold-solver by running this Github Action.
# - Each individual repository can only start its own release when its dependencies are fully released.
#   timefold-solver-enterprise depends on timefold-solver
#   timefold-quickstarts depends on timefold-solver
#   timefold-website releases last
# - Each individual repository uses 999-SNAPSHOT as its development version, even on micro branches.
#
# Should any of these axioms change, the release pipeline will need to be (significantly) refactored.
# 0.8.x releases existed before this pipeline; they are done differently, similarities are coincidental.

name: Release
on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Release version (e.g. 1.0.0)'
        required: true
      sourceBranch:
        description: 'Branch to cut the release from'
        default: main
        required: true
      releaseBranch:
        description: 'Release branch to create (e.g. 1.0.x for version 1.0.0; once created, branch protection rules apply)'
        default: dry_run
        required: true
      dryRun:
        description: 'Do a dry run? (true or false)'
        default: true
        required: true
jobs:
  build:
    env:
      MAVEN_ARGS: "--no-transfer-progress --batch-mode"
    runs-on: ubuntu-latest
    steps:
      - name: Checkout timefold-solver
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.inputs.sourceBranch }}

      - name: Create release branch and switch to it
        run: |
          git config user.name "Timefold Release Bot"
          git config user.email "release@timefold.ai"
          git checkout -b ${{ github.event.inputs.releaseBranch }}

      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'

      # Need Maven 3.9.0+ to recognize MAVEN_ARGS.
      - name: Set up Maven
        uses: stCarolas/setup-maven@v5
        with:
          maven-version: 3.9.3

      # We skip tests in dry run, to make the process faster.
      # Technically, this goes against the main reason for doing a dry run; to eliminate potential problems.
      # But unless something catastrophic happened, PR checks on source branch already ensured that all tests pass.
      - name: Set release version and build release
        run: |
          mvn -Dfull versions:set -DnewVersion=${{ github.event.inputs.version }}
          mvn -Dfull deploy -DskipTests=${{ github.event.inputs.dryRun }} -DaltDeploymentRepository=local::default::file://`pwd`/target/staging-deploy
          cp docs/target/antora-template.yml docs/src/antora.yml
          git add docs/src/antora.yml
          find . -name 'pom.xml' | xargs git add
          git commit -m "build: release version ${{ github.event.inputs.version }}"
          git push origin ${{ github.event.inputs.releaseBranch }}

      - name: Run JReleaser
        uses: jreleaser/release-action@v2
        env:
          JRELEASER_DRY_RUN: ${{ github.event.inputs.dryRun }}
          JRELEASER_PROJECT_VERSION: ${{ github.event.inputs.version }}
          JRELEASER_GITHUB_TOKEN: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
          JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
          JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }}
          JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
          JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME: ${{ secrets.JRELEASER_MAVEN_CENTRAL_TOKEN_USER }}
          JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD: ${{ secrets.JRELEASER_MAVEN_CENTRAL_TOKEN }}

      - name: JReleaser release output
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: jreleaser-release
          path: |
            out/jreleaser/trace.log
            out/jreleaser/output.properties

      # Pull Request will be created with the changes and a summary of next steps.
      - name: Put back the 999-SNAPSHOT version on the release branch
        run: |
          git checkout -B ${{ github.event.inputs.releaseBranch }}-put-back-999-snapshot
          mvn -Dfull versions:set -DnewVersion=999-SNAPSHOT
          git commit -am "build: move back to 999-SNAPSHOT"
          git push origin ${{ github.event.inputs.releaseBranch }}-put-back-999-snapshot
          gh pr create --reviewer triceo --base ${{ github.event.inputs.releaseBranch }} --head ${{ github.event.inputs.releaseBranch }}-put-back-999-snapshot --title "build: move back to 999-SNAPSHOT" --body-file .github/workflows/release-pr-body.md
        env:
          GITHUB_TOKEN: ${{ secrets.JRELEASER_GITHUB_TOKEN }}


SonarCloud

name: SonarCloud
on:
  push:
    branches:
      - main
  pull_request_target: # This workflow will be triggered by the opening, reopening, or updating of a PR, and the first run will not require approval.
    types:
      - opened
      - reopened
      - synchronize
jobs:
  build:
    name: Build and analyze
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
          ref: ${{ github.event.pull_request.head.sha }} # The GHA event will pull the main branch by default, and we must specify the PR reference version
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'temurin'
          cache: 'maven'
      - name: Cache SonarCloud packages
        uses: actions/cache@v4
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
      - name: Build with Maven to measure code coverage # The ENV variables are limited to the scope of the current step. Avoid adding sensitive ENV variables here as the tests could leak them.
        run: mvn -B clean install -Prun-code-coverage
      - name: Run analysis
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Needed to run the SonarCloud analysis
        run: mvn -B -Psonarcloud-analysis validate org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.organization=timefold -Dsonar.projectKey=ai.timefold:timefold-solver -Dsonar.host.url=https://sonarcloud.io -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} -Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }} -Dsonar.scm.revision=${{ github.event.pull_request.head.sha }}


Downstream - Timefold Quickstarts

name: Downstream - Timefold Quickstarts

on:
  pull_request:
    branches: [main, '*.x']
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

jobs:
  build:
    runs-on: ubuntu-latest
    concurrency:
      group: downstream-quickstarts-${{ github.event_name }}-${{ github.head_ref }}
      cancel-in-progress: true
    timeout-minutes: 120
    steps:
      # Clone timefold-solver
      # No need to check for stale repo, as Github merges the main repo into the fork automatically.
      - name: Checkout timefold-solver
        uses: actions/checkout@v4
        with:
          path: ./timefold-solver

      # Clone timefold-quickstarts
      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-quickstarts (PR) # Checkout the PR branch first, if it exists
        if: github.head_ref # Only true if this is a PR.
        id: checkout-quickstarts-pr
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: ${{ github.actor }}/timefold-quickstarts
          ref: ${{ github.head_ref }}
          path: ./timefold-quickstarts
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-quickstarts (development) # Checkout the development branch if the PR branch does not exist
        if: ${{ steps.checkout-quickstarts-pr.outcome != 'success' }}
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-quickstarts
          ref: development
          path: ./timefold-quickstarts
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of quickstarts
        if: ${{ steps.checkout-quickstarts-pr.outcome == 'success' }}
        env:
          BLESSED_REPO: "timefold-quickstarts"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'development' }}
        shell: bash
        working-directory: ./timefold-quickstarts
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Build and test
      - name: Setup Temurin 17 and Maven
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'
      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        shell: bash
        run: mvn -B -Dquickly clean install
      - name: Build and test timefold-quickstarts
        working-directory: ./timefold-quickstarts
        shell: bash
        run: mvn -B clean verify


name: Downstream - Timefold Solver Enterprise Edition

on:
  # Enables the workflow to run on PRs from forks;
  # token sharing is safe here, because enterprise is a private repo and therefore fully under our control.
  pull_request_target:
    branches: [main, '*.x']
    types:
      - opened
      - reopened
      - synchronize
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

jobs:
  build:
    runs-on: ubuntu-latest
    concurrency:
      group: downstream-enterprise-${{ github.event_name }}-${{ github.head_ref }}
      cancel-in-progress: true
    timeout-minutes: 120
    steps:
      # Clone timefold-solver
      # No need to check for stale repo, as Github merges the main repo into the fork automatically.
      - name: Checkout timefold-solver
        uses: actions/checkout@v4
        with:
          path: ./timefold-solver
          ref: ${{ github.event.pull_request.head.sha }} # The GHA event will pull the main branch by default, and we must specify the PR reference version

      # Clone timefold-solver-enterprise
      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-enterprise (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-enterprise
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: ${{ github.head_ref }}
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-enterprise (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-enterprise.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: main
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-enterprise
        env:
          BLESSED_REPO: "timefold-solver-enterprise"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-enterprise
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Build and test
      - name: Setup Temurin 17 and Maven
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'
      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        shell: bash
        run: mvn -B -Dquickly clean install
      - name: Build and test timefold-solver-enterprise
        working-directory: ./timefold-solver-enterprise
        shell: bash
        run: mvn -B clean verify


GraalVM Native

name: GraalVM Native

on:
  pull_request:
    branches: [main]
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

jobs:
  native:
    concurrency:
      group: pull_request_native-${{ github.event_name }}-${{ github.head_ref }}-${{ matrix.module }}-${{ matrix.java-version }}
      cancel-in-progress: true
    runs-on: ubuntu-latest
    strategy:
      matrix:
        module: ["spring-integration", "quarkus-integration"]
        java-version: [ 17, 21, 22 ] #Latest two LTS + latest non-LTS.
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@v4

      - uses: graalvm/setup-graalvm@v1
        with:
          java-version: ${{matrix.java-version}}
          distribution: 'graalvm-community'
          components: 'native-image'
          github-token: ${{ secrets.GITHUB_TOKEN }}
          cache: 'maven'

      - name: Quickly build timefold-solver
        run: mvn -B -Dquickly clean install

      - name: Test timefold-solver in Native mode
        run: |
          cd ${{matrix.module}}
          mvn -B -Dnative verify


Downstream - Integration Tests

name: Downstream - Integration Tests

on:
  # Enables the workflow to run on PRs from forks;
  # token sharing is safe here, because enterprise is a private repo and therefore fully under our control.
  pull_request_target:
    branches: [main] # Benchmarks aren't branched, so they will only ever work against current main.
    types:
      - opened
      - reopened
      - synchronize
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

jobs:
  build:
    runs-on: ubuntu-latest
    concurrency:
      group: pr-${{ github.event_name }}-${{ github.head_ref }}
      cancel-in-progress: true
    timeout-minutes: 120
    steps:
      # Clone timefold-solver
      # No need to check for stale repo, as Github merges the main repo into the fork automatically.
      - name: Checkout timefold-solver
        uses: actions/checkout@v4
        with:
          path: ./timefold-solver
          ref: ${{ github.event.pull_request.head.sha }} # The GHA event will pull the main branch by default, and we must specify the PR reference version

      - name: Setup Temurin 21 and Maven
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: 'maven'

      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        shell: bash
        run: mvn -B -Dquickly clean install

      # Clone timefold-solver-enterprise
      - name: Checkout timefold-solver-enterprise (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-enterprise
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: ${{ github.head_ref }}
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-enterprise (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-enterprise.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: main
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.

      - name: Quickly build timefold-solver-enterprise
        working-directory: ./timefold-solver-enterprise
        shell: bash
        run: mvn -B -Dquickly clean install

      # Clone timefold-solver-benchmarks
      - name: Checkout timefold-solver-benchmarks (PR) # Checkout the PR branch first, if it exists
        if: github.head_ref # Only true if this is a PR.
        id: checkout-solver-benchmarks-pr
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: TimefoldAI/timefold-solver-benchmarks
          ref: ${{ github.head_ref }}
          path: ./timefold-solver-benchmarks
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-benchmarks (main) # Checkout the main branch if the PR branch does not exist
        if: ${{ steps.checkout-solver-benchmarks-pr.outcome != 'success' }}
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-benchmarks
          ref: main
          path: ./timefold-solver-benchmarks
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-benchmarks
        if: ${{ steps.checkout-solver-benchmarks-pr.outcome == 'success' }}
        env:
          BLESSED_REPO: "timefold-solver-benchmarks"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-benchmarks
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      - name: Build and test timefold-solver-benchmarks
        working-directory: ./timefold-solver-benchmarks
        shell: bash
        run: mvn -B -DskipJMH clean verify


Downstream - Timefold Solver for Python

# Tests PRs on multiple operating systems and Python/Java versions
name: Downstream - Timefold Solver for Python

on:
  pull_request:
    types: [opened, synchronize, reopened, labeled]
    branches:
      - main
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'
      - '.ci/**'

defaults:
  run:
    shell: bash

jobs:
  test-build:
    strategy:
      matrix:
        os: [ ubuntu-latest ]
        java-version: [ 17, 21, 22 ]
      fail-fast: false
    runs-on: ${{ matrix.os }}

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
        with:
          path: './timefold-solver'

      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-python (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-python
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: ${{ github.actor }}/timefold-solver-python
          ref: ${{ github.head_ref }}
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-python (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-python.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-python
          ref: main
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-python
        env:
          BLESSED_REPO: "timefold-solver-python"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-python
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Build and test
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: ${{ matrix.java-version }}
          distribution: 'temurin'
          cache: 'maven'
      # Need to install all Python versions in the same run for tox
      - name: Python 3.10, Python 3.11, Python 3.12 Setup
        uses: actions/setup-python@v4
        with:
          python-version: |
              3.10
              3.11
              3.12
          cache: 'pip'
          cache-dependency-path: |
            **/setup.py
      - name: Install tox
        run:
          python -m pip install --upgrade pip
          pip install tox pytest
      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        run: mvn -B -Dquickly clean install
      - name: Build with Maven to install parent poms for python build
        working-directory: ./timefold-solver-python
        run: mvn -B --fail-at-end clean install
      - name: Run tox on timefold solver python test suite
        working-directory: ./timefold-solver-python
        run: python -m tox
      - name: Run tox on jpyinterpreter test suite
        working-directory: ./timefold-solver-python/jpyinterpreter
        run: python -m tox


# Tests PRs on multiple operating systems and Python/Java versions
name: Downstream - Timefold Solver for Python Quickstarts

on:
  pull_request:
    types: [opened, synchronize, reopened, labeled]
    branches:
      - main
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'
      - '.ci/**'

defaults:
  run:
    shell: bash

jobs:
  test-build:
    concurrency:
      group: pull_request_python_quickstarts-${{ github.event_name }}-${{ github.head_ref }}-${{ matrix.os }}-${{ matrix.java-version }}-${{ matrix.python-version }}
      cancel-in-progress: true
    strategy:
      matrix:
        os: [ ubuntu-latest ]
        java-version: [ 17 ] # Only the first supported LTS; already too many jobs here.
        # TODO: Add Python 3.10 once employee scheduling and school timetabling support it
        python-version: ['3.11', '3.12']
      fail-fast: false
    runs-on: ${{ matrix.os }}

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
        with:
          path: './timefold-solver'

      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-python (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-python
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: ${{ github.actor }}/timefold-solver-python
          ref: ${{ github.head_ref }}
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-python (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-python.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-python
          ref: main
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-python
        env:
          BLESSED_REPO: "timefold-solver-python"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-python
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-quickstarts (PR) # Checkout the PR branch first, if it exists
        id: checkout-quickstarts
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: ${{ github.actor }}/timefold-quickstarts
          ref: ${{ github.head_ref }}
          path: ./timefold-quickstarts
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-quickstarts (development) # Checkout the development branch if the PR branch does not exist
        if: steps.checkout-solver-quickstarts.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-quickstarts
          ref: development
          path: ./timefold-quickstarts
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-quickstarts
        env:
          BLESSED_REPO: "timefold-quickstarts"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'development' }}
        shell: bash
        working-directory: ./timefold-quickstarts
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Build and test
      - name: "Setup Java and Maven"
        uses: actions/setup-java@v3
        with:
          java-version: ${{matrix.java-version}}
          distribution: 'temurin'
          cache: 'maven'
      - name: Python Setup
        uses: actions/setup-python@v4
        with:
          python-version: ${{matrix.python-version}}
          cache: 'pip'
          cache-dependency-path: |
            **/setup.py
      - name: Install build
        run:
          python -m pip install --upgrade pip
          pip install build
      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        run: mvn -B -Dquickly -DskipTests clean install
      - name: Build timefold-solver-python
        working-directory: ./timefold-solver-python
        run: python -m build
      - name: Build and test timefold-quickstarts
        working-directory: ./timefold-quickstarts
        env:
          TIMEFOLD_SOLVER_PYTHON_DIST: "${{ github.workspace }}/timefold-solver-python/dist"
        run: .github/scripts/run_python_tests.sh


# Tests PRs on multiple operating systems and Python/Java versions
name: Downstream - Timefold Solver Enterprise for Python

on:
  # Enables the workflow to run on PRs from forks;
  # token sharing is safe here, because enterprise is a private repo and therefore fully under our control.
  pull_request_target:
    branches: [ main, '*.x' ]
    types:
      - opened
      - reopened
      - synchronize
    paths-ignore:
      - 'LICENSE*'
      - '.gitignore'
      - '**.md'
      - '**.adoc'
      - '*.txt'

defaults:
  run:
    shell: bash

jobs:
  build:
    concurrency:
      group: downstream-enterprise-python-${{ github.event_name }}-${{ github.head_ref }}
      cancel-in-progress: true
    timeout-minutes: 120
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
        with:
          path: './timefold-solver'

      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-python (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-python
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: ${{ github.actor }}/timefold-solver-python
          ref: ${{ github.head_ref }}
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-python (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-python.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-python
          ref: main
          path: ./timefold-solver-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-python
        env:
          BLESSED_REPO: "timefold-solver-python"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-python
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Clone timefold-solver-enterprise
      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-enterprise (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-enterprise
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: ${{ github.head_ref }}
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-enterprise (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-enterprise.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-enterprise
          ref: main
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-enterprise
        env:
          BLESSED_REPO: "timefold-solver-enterprise"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-enterprise
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Clone timefold-solver-python-enterprise
      # Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
      - name: Checkout timefold-solver-enterprise-python (PR) # Checkout the PR branch first, if it exists
        id: checkout-solver-enterprise-python
        uses: actions/checkout@v4
        continue-on-error: true
        with:
          repository: TimefoldAI/timefold-solver-enterprise-python
          ref: ${{ github.head_ref }}
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Checkout timefold-solver-python-enterprise-python (main) # Checkout the main branch if the PR branch does not exist
        if: steps.checkout-solver-enterprise-python.outcome != 'success'
        uses: actions/checkout@v4
        with:
          repository: TimefoldAI/timefold-solver-enterprise-python
          ref: main
          token: ${{ secrets.JRELEASER_GITHUB_TOKEN }} # Safe; only used to clone the repo and not stored in the fork.
          path: ./timefold-solver-enterprise-python
          fetch-depth: 0 # Otherwise merge will fail on account of not having history.
      - name: Prevent stale fork of timefold-solver-enterprise-python
        env:
          BLESSED_REPO: "timefold-solver-enterprise-python"
          BLESSED_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
        shell: bash
        working-directory: ./timefold-solver-enterprise-python
        run: ../timefold-solver/.github/scripts/prevent_stale_fork.sh

      # Build and test
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'temurin'
          cache: 'maven'
      # Need to install all Python versions in the same run for tox
      - name: Python 3.10, Python 3.11, Python 3.12 Setup
        uses: actions/setup-python@v4
        with:
          python-version: |
            3.10
            3.11
            3.12
          cache: 'pip'
          cache-dependency-path: |
            **/setup.py
      - name: Install tox
        run:
          python -m pip install --upgrade pip
          pip install tox build
      - name: Quickly build timefold-solver
        working-directory: ./timefold-solver
        run: mvn -B -Dquickly clean install
      - name: Quickly Build timefold-solver-enterprise
        working-directory: ./timefold-solver-enterprise
        shell: bash
        run: mvn -B -Dquickly clean verify
      - name: Build with Maven to install parent poms for python build
        working-directory: ./timefold-solver-python
        run: mvn -B --fail-at-end clean install
      - name: Build timefold solver python
        working-directory: ./timefold-solver-python
        run: python -m build
      - name: Run tox on timefold solver enterprise python test suite
        working-directory: ./timefold-solver-enterprise-python
        env:
          PIP_FIND_LINKS: ${{ github.workspace }}/timefold-solver-python/dist
        run: tox


Verify pull requests

name: Verify pull requests

on:
  pull_request:
    branches:
      - main
      - main/**
      - feature/**

jobs:
  style_check:
    name: Style/Format check
    runs-on: ubuntu-latest
    steps:
    - name: Checkout git repo
      uses: actions/checkout@v3
      with:
        fetch-depth: 0

    - name: Commit Linter
      uses: wagoid/commitlint-github-action@v5
      with:
        configFile: .github/commitlint.config.js

  build_test:
    name: Build and Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout git repo
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Extract commit info
        id: commit_info
        run: |
          short_sha="$(git rev-parse --short $GITHUB_SHA)"
          branch_name="pr$(echo $GITHUB_REF | sed -e 's/refs\/pull\/\(.*\)\/merge/\1/')"
          echo "short_sha=$short_sha" >> $GITHUB_OUTPUT
          echo "branch_name=$branch_name" >> $GITHUB_OUTPUT

      # Install python and deps
      - name: Set up Python 3.12.1
        uses: actions/setup-python@v4
        with:
          python-version: 3.12.1
      - name: Cache PIP packages
        uses: actions/cache@v3
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('.github/requirements.txt') }}
      - name: Install python deps
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r .github/requirements.txt

      - name: Generate version
        id: versioning
        run: |
          version=$(python .github/git_utils.py genVersion --format_str="{major}.{minor}.{patch}-${{ steps.commit_info.outputs.branch_name }}+{increment}")
          echo "version=$version"
          echo "version=$version" >> $GITHUB_OUTPUT

      - name: Set up JDK 1.8
        uses: actions/setup-java@v3
        with:
          distribution: 'adopt'
          java-version: '8'

      - name: Cache Gradle packages
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle2-${{ hashFiles('**/build.gradle', '**/build.properties', '**/gradle.properties', '**/gradle-wrapper.properties') }}

      - name: Build with Gradle
        run: ./gradlew build # TODO test
        env:
          AUTO_GENERATED_VERSION: ${{ steps.versioning.outputs.version }}

      - name: Cleanup Gradle Cache
        # These files shouldn't be cached according to https://docs.github.com/en/actions/guides/building-and-testing-java-with-gradle
        run: |
          rm -f ~/.gradle/caches/modules-2/modules-2.lock
          rm -f ~/.gradle/caches/modules-2/gc.properties

      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: project-red-${{ steps.versioning.outputs.version }}
          path: '*/build/libs/ProjectRed-*.jar'