CodeQL

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"

on:
  schedule:
    # run every Monday at 3am UTC
    - cron: '0 3 * * 1'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest

    strategy:
      fail-fast: true
      matrix:
        language: [ 'python', 'javascript' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

    # ℹ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏ If the Autobuild fails above, remove it and build your code if your project
    #    uses a compiled language

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3


When Opened

name: "When Opened"

on:
  issues:
    types:
    - opened
  pull_request_target:
    types:
    - opened

jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
    # NOTE: sync-labels due to https://github.com/actions/labeler/issues/112
    - name: Label PR
      uses: actions/labeler@v5
      if: github.event_name == 'pull_request_target' && github.event.pull_request.user.login != 'meeseeksmachine'
      with:
        repo-token: "${{ secrets.GITHUB_TOKEN }}"
    # Special action for a special day. Until next year!
    #- name: Special comment
    #  uses: pllim/action-special_pr_comment@main
    #  with:
    #    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


rc-testing

name: rc-testing

on:
  workflow_dispatch:
  schedule:
    # run at 9am UTC on Tuesdays
    - cron: '0 9 * * 2'

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

permissions:
  contents: read

jobs:
  # Github Actions supports ubuntu, windows, and macos virtual environments:
  # https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners
  ci_tests:
    name: ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:

          - name: RC testing on Linux with remote data
            os: ubuntu-latest
            python: '3.12'
            toxenv: py312-test-predeps
            toxposargs: --remote-data

          - name: RC testing on OSX
            os: macos-latest
            python: '3.10'
            toxenv: py310-test-predeps

          - name: RC testing on Windows
            os: windows-latest
            python: '3.11'
            toxenv: py311-test-predeps

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - name: Set up python ${{ matrix.python }} on ${{ matrix.os }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}
    - name: Install base dependencies
      run: python -m pip install --upgrade pip tox
    - name: Test/run with tox
      run: tox -e ${{ matrix.toxenv }} -- ${{ matrix.toxposargs }}


Release

name: Release

on:
  # NOTE: PR trigger is to ensure changes do not break packaging.
  pull_request:
  release:
    types: [released]

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

jobs:
  build-n-publish:
    name: Build and publish Python 🐍 distributions 📦 to PyPI
    runs-on: ubuntu-latest
    if: github.repository == 'spacetelescope/jdaviz'

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - uses: actions/setup-python@v5
      with:
        python-version: '3.10'

    - name: Install python-build and twine
      run: python -m pip install build "twine>=3.3"

    - name: Build package
      run: python -m build --sdist --wheel .

    - name: List result
      run: ls -l dist

    - name: Check dist
      run: python -m twine check --strict dist/*

    - name: Test package
      run: |
        cd ..
        python -m venv testenv
        testenv/bin/pip install pytest pytest-astropy pytest-tornasync jdaviz/dist/*.whl
        testenv/bin/python -c "import jdaviz; jdaviz.test(remote_data=True)"

    # NOTE: Do not run this part for PR testing.
    - name: Publish distribution 📦 to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      if: github.event_name != 'pull_request'
      with:
        user: __token__
        password: ${{ secrets.PYPI_TOKEN }}


Build standalone

name: Build standalone

on:
  push:
    branches:
    - main
    - 'v*'
    tags:
    - 'v*'
  workflow_dispatch:
  pull_request:

defaults:
  run:
    shell: bash {0}

jobs:
  build_binary_not_osx:
    runs-on: ${{ matrix.os }}-latest
    if: (github.repository == 'spacetelescope/jdaviz' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build standalone')))
    strategy:
      matrix:
        os: [ubuntu, windows]
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - uses: actions/setup-python@v5
      with:
        python-version: "3.11"

    - name: Install jdaviz
      run: pip install .[test]

    - name: Install pyinstaller
      run: pip install "pyinstaller<6"

    - name: Create standalone binary
      env:
        DEVELOPER_ID_APPLICATION: ${{ secrets.DEVELOPER_ID_APPLICATION }}
      run: (cd standalone; pyinstaller ./jdaviz.spec)

    - name: Run jdaviz cmd in background
      run: ./standalone/dist/jdaviz/jdaviz-cli imviz&

    - name: Install playwright
      run: (pip install playwright; playwright install chromium)

    - name: Install pytest
      run: pip install pytest-playwright

    - name: Wait for Voila to get online
      uses: ifaxity/wait-on-action@a7d13170ec542bdca4ef8ac4b15e9c6aa00a6866  # v1.2.1
      with:
        resource: tcp:8866
        timeout: 60000

    - name: Test standalone
      run: (cd standalone; touch pytest.ini; JUPYTER_PLATFORM_DIRS=1 pytest test_standalone.py --video=on)

    - name: Upload Test artifacts
      if: github.event_name != 'pull_request'
      uses: actions/upload-artifact@v3
      with:
        name: test-results-${{ matrix.os }}
        path: standalone/test-results

    - name: Upload jdaviz standalone (non-OSX)
      if: github.event_name != 'pull_request'
      uses: actions/upload-artifact@v3
      with:
        name: jdaviz-standlone-${{ matrix.os }}
        path: |
          standalone/dist/jdaviz

  # Do not want to deal with OSX certs in pull request builds.
  build_binary_osx:
    runs-on: ${{ matrix.os }}-latest
    if: (github.repository == 'spacetelescope/jdaviz' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch'))
    strategy:
      matrix:
        os: [macos]
    steps:
    # osx signing based on https://melatonin.dev/blog/how-to-code-sign-and-notarize-macos-audio-plugins-in-ci/
    - name: Import Certificates (macOS)
      uses: apple-actions/import-codesign-certs@v3
      if: ${{ matrix.os == 'macos' }}
      with:
        p12-file-base64: ${{ secrets.DEV_ID_APP_CERT }}
        p12-password: ${{ secrets.DEV_ID_APP_PASSWORD }}

    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - uses: actions/setup-python@v5
      with:
        python-version: "3.11"

    - name: Install jdaviz
      run: pip install .[test]

    - name: Install pyinstaller
      run: pip install "pyinstaller<6"

    - name: Create standalone binary
      env:
        DEVELOPER_ID_APPLICATION: ${{ secrets.DEVELOPER_ID_APPLICATION }}
      run: (cd standalone; pyinstaller ./jdaviz.spec)

    - name: Remove invalid files for OSX
      # hopefully we can improve this in the future
      # by using good hooks
      # i think the issue is that we have a . in the name, there are many
      # google hits on pyqt having the same issue
      # and we might be able to remove it after https://github.com/pyinstaller/pyinstaller/pull/7619
      # is released (pyinstaller 5.13 probably)
      if: ${{ matrix.os == 'macos' }}
      run: |
        rm -rf standalone/dist/jdaviz.app/Contents/MacOS/skimage/.dylibs
        rm -rf standalone/dist/jdaviz.app/Contents/Resources/skimage/.dylibs
        rm -rf standalone/dist/jdaviz.app/Contents/MacOS/shapely/.dylibs
        rm -rf standalone/dist/jdaviz.app/Contents/Resources/shapely/.dylibs
        rm -rf standalone/dist/jdaviz.app/Contents/MacOS/scipy/.dylibs
        rm -rf standalone/dist/jdaviz.app/Contents/Resources/scipy/.dylibs

    - name: Codesign (OSX)
      if: ${{ matrix.os == 'macos' }}
      run: |
        cd standalone/dist
        codesign --deep --force --options=runtime --entitlements ../entitlements.plist --sign ${{ secrets.DEVELOPER_ID_APPLICATION }} --timestamp jdaviz.app

    - name: Create dmg (OSX)
      # if we do not call always() GHA will && with success()
      if: ${{ always() && (matrix.os == 'macos') }}
      # it seems ditto (not zip) should be used in combination with notarization
      # see https://developer.apple.com/forums/thread/116831
      # but dmg also works
      # see https://github.com/glue-viz/glue-standalone-apps/blob/main/.github/workflows/build_stable.yml
      run: |
        rm -rf standalone/dist/jdaviz
        hdiutil create -volname "Jdaviz" -srcfolder standalone/dist -ov -format UDZO standalone/dist/jdaviz.dmg

    - name: Notary step + stapling (OSX)
      if: ${{ matrix.os == 'macos' }}
      run: |
        output=$(xcrun notarytool submit standalone/dist/jdaviz.dmg --apple-id ${{ secrets.NOTARIZATION_USERNAME }} --team-id ${{ secrets.TEAM_ID }} --wait --password ${{ secrets.NOTARIZATION_PASSWORD }}) || true
        echo "$output"
        uuid=$(echo "$output" | awk -F '[ :]+' '/id:/ {print $3; exit}')
        echo "UUID: $uuid"
        if [[ $output == *"status: Accepted"* ]]; then
          echo "Great, notarization succeeded, staple it!"
          xcrun stapler staple standalone/dist/jdaviz.dmg
        else
          echo "Log output for failed notarization: $uuid"
          xcrun notarytool log --apple-id ${{ secrets.NOTARIZATION_USERNAME }} --team-id ${{ secrets.TEAM_ID }} --password ${{ secrets.NOTARIZATION_PASSWORD }} $uuid || true
        fi

    - name: Validate app (OSX)
      if: ${{ matrix.os == 'macos' }}
      run: |
        spctl -a -vvv -t execute standalone/dist/jdaviz.app

    - name: Run jdaviz cmd in background
      if: ${{ matrix.os == 'macos' }}
      run: ./standalone/dist/jdaviz.app/Contents/MacOS/jdaviz-cli imviz&

    - name: Install playwright
      run: (pip install playwright; playwright install chromium)

    - name: Install pytest
      run: pip install pytest-playwright

    - name: Wait for Voila to get online
      uses: ifaxity/wait-on-action@a7d13170ec542bdca4ef8ac4b15e9c6aa00a6866  # v1.2.1
      with:
        resource: tcp:8866
        timeout: 60000

    - name: Test standalone
      run: (cd standalone; touch pytest.ini; JUPYTER_PLATFORM_DIRS=1 pytest test_standalone.py --video=on)

    - name: Upload Test artifacts
      if: always()
      uses: actions/upload-artifact@v3
      with:
        name: test-results-${{ matrix.os }}
        path: standalone/test-results

    - name: Upload jdaviz standalone (OSX)
      if: ${{ always() && (matrix.os == 'macos') }}
      uses: actions/upload-artifact@v3
      with:
        name: jdaviz-standlone-${{ matrix.os }}
        path: standalone/dist/jdaviz.dmg


Periodic Link Checker

name: Periodic Link Checker

on:
#  schedule:
#    - cron: "0 0 * * *"
  workflow_dispatch:

permissions:
  contents: read
  issues: write

jobs:
  link-checker:
    runs-on: ubuntu-latest
    steps:
      - name: "Checkout source code"
        uses: actions/checkout@v4
      - name: Link Checker
        id: lychee
        uses: lycheeverse/lychee-action@v1.9.3
        with:
          args: --verbose --no-progress --max-concurrency 2 --exclude-mail --exclude-loopback './**/*.md'
          output: ./lychee/out.md
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
      - name: Find existing issue
        id: find_issue
        uses: micalevisk/last-issue-action@v2
        if: ${{ steps.lychee.outputs.exit_code }} != 0
        with:
          state: open
          labels: |
            broken link
            automated issue
      - name: Create or update issue for broken links
        uses: peter-evans/create-issue-from-file@v5
        if: ${{ steps.lychee.outputs.exit_code }} != 0
        with:
          title: Link Checker Report
          # If issue number is empty a new issue gets
          issue-number: ${{ steps.find_issue.outputs.issue-number }}
          content-filepath: ./lychee/out.md
          labels: broken link, automated issue


Build Python Project and Docker Image

name: Build Python Project and Docker Image

on:
  #push:
  #  branches: ["main", "dev"]
  #pull_request:
  #  types: [opened, edited, synchronize, reopened]
  #  branches: ["main", "dev"]
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    strategy:
      matrix:
        python-version: ["3.10"]

    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install flake8 pytest
          pip install -r ./config/python/requirements.txt
      - name: Check for outdated dependencies
        run: |
          pip list --outdated
      - name: Lint with flake8
        run: |
          # stop the build if there are Python syntax errors or undefined names
          flake8 ./libs/python/ --count --select=E9,F63,F7,F82 --show-source --statistics
          # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
          flake8 ./libs/python/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

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

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v0.11.6
        #with:
        #  buildkitd-flags: --debug

      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build Docker image
        uses: docker/build-push-action@v5.1.0
        with:
          context: .
          platforms: linux/amd64
          file: ./config/Dockerfile
          build-args: |
            BTPSA_VERSION_GIT_ARG=${{ github.sha }}
          push: false
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


name: BTPSA - Integration Test - full (no trial)
# This file represents a complete version of the "BTP Setup Automator" integration tests
# All tests are executed in sequence on one BTP account to avoid entitlement limits
# This is why not matrix strategy is used
# The steps of a job must execute in any case, so we use always()

on:
  workflow_dispatch:

jobs:
  integration-test:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/sap-samples/btp-setup-automator:latest
      options: --user root
    env:
      BTPSA_PARAM_MYEMAIL: ${{ secrets.BTPSA_PARAM_MYEMAIL }}
      BTPSA_PARAM_GLOBALACCOUNT: ${{ secrets.BTPSA_PARAM_GLOBALACCOUNT }}
      BTPSA_PARAM_MYPASSWORD: ${{ secrets.BTPSA_PARAM_MYPASSWORD }}
    steps:
      - name: 'Integration test 01: Default use case'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest01.json'
      - name: "Integration test 02: Deploy full-stack CAP application running in SAP Launchpad"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest02.json'
      - name: "Integration test 04: Only app subscriptions"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest04.json'
      - name: "Integration test 05: Only one service without app subscriptions and with an admin, that is the same like the one logging into the script."
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest05.json'
      - name: "Integration test 06: Create service keys"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest06.json'
      - name: 'Integration test 07: Setup SAP Task Center'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest07.json'
      - name: "Integration test 10: Setup a Kyma environment on GCP"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest10.json'


BTPSA - Integration Test - short

name: BTPSA - Integration Test - short
# This file represents a slim version of the "BTP Setup Automator" integration tests
# All tests are executed in sequence on one BTP account to avoid entitlement limits
# This is why not matrix strategy is used
# The steps of a job must execute in any case, so we use always()

on:
  workflow_dispatch:

jobs:
    integration-test:
        runs-on: ubuntu-latest
        container:
          image: ghcr.io/sap-samples/btp-setup-automator:latest
          options: --user root
        env:
            BTPSA_PARAM_MYEMAIL: ${{ secrets.BTPSA_PARAM_MYEMAIL }}
            BTPSA_PARAM_GLOBALACCOUNT: ${{ secrets.BTPSA_PARAM_GLOBALACCOUNT }}
            BTPSA_PARAM_MYPASSWORD: ${{ secrets.BTPSA_PARAM_MYPASSWORD }}
        steps:
            - name: 'Integration test 01: Default use case'
              if: ${{ always() }}
              working-directory: /home/user
              shell: bash
              run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest01.json'
            - name: 'Integration test 07: Setup SAP Task Center'
              if: ${{ always() }}
              working-directory: /home/user
              shell: bash
              run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest07.json'
            - name: 'Integration test 04: Only app subscriptions'
              if: ${{ always() }}
              working-directory: /home/user
              shell: bash
              run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest04.json'
            - name: 'Integration test 05: Only one service without app subscriptions and with an admin, that is the same like the one logging into the script.'
              if: ${{ always() }}
              working-directory: /home/user
              shell: bash
              run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest05.json'
            - name: 'Integration test 06: create service keys'
              if: ${{ always() }}
              working-directory: /home/user
              shell: bash
              run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest06.json'


PR Integration Test (branches - slim)

name: PR Integration Test (branches - slim)
# INTEGRATION TEST FOR PULL REQUESTS ON BRANCHES != MAIN
# This file represents a slim version of the "BTP Setup Automator" integration tests
# for PRs on branches
# It executes the following steps:
# - based on the branch it creates a temporary Docker image and pushes it to the GH registry
# - it executes the integration tests on the temporary docker image
# - it deletes the temporary Docker image after execution
#
# All tests are executed in sequence on one BTP account to avoid entitlement limits
# This is why not matrix strategy is used
# The steps of a job must execute in any case, so we use always()

on:
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: tmp-${{ github.ref_name }}
  LABEL: ${{ github.ref_name }}
  ORG: sap-samples

jobs:
  build-temporary-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    outputs:
      image_path: ${{ steps.set-image-path.outputs.image_path }}
    steps:
      - uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v0.10.6
      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}
      - name: Build Docker image
        id: dockerbuild
        uses: docker/build-push-action@v5.1.0
        with:
          context: .
          file: ./config/Dockerfile
          build-args: |
            BTPSA_VERSION_GIT_ARG=${{ github.sha }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
      - id: set-image-path
        name: Set image path to output
        run: echo "image_path=${{ fromJSON(steps.dockerbuild.outputs.metadata)['image.name'] }}" >> $GITHUB_OUTPUT

  integration-test-for-pr-slim:
    needs: build-temporary-image
    runs-on: ubuntu-latest
    container:
      image: ${{ needs.build-temporary-image.outputs.image_path }}
      options: --user root
    env:
      BTPSA_PARAM_MYEMAIL: ${{ secrets.BTPSA_PARAM_MYEMAIL }}
      BTPSA_PARAM_GLOBALACCOUNT: ${{ secrets.BTPSA_PARAM_GLOBALACCOUNT }}
      BTPSA_PARAM_MYPASSWORD: ${{ secrets.BTPSA_PARAM_MYPASSWORD }}
    steps:
      - name: 'Integration test 01: Default use case'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest01.json'
      - name: 'Integration test 07: Setup SAP Task Center'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest07.json'
      - name: "Integration test 04: Only app subscriptions"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest04.json'
      - name: "Integration test 05: Only one service without app subscriptions and with an admin, that is the same like the one logging into the script."
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest05.json'
      - name: "Integration test 06: create service keys"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest06.json'

  delete-temporary-image:
    if: ${{ success() }} || ${{ failure() }}
    needs: [build-temporary-image, integration-test-for-pr-slim]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        env:
          IMAGE: ${{ needs.build-temporary-image.outputs.image_path }}
        with:
          github-token: ${{ secrets.DELETE_IMAGE_TOKEN }}
          script: |
            const imageName = process.env.IMAGE_NAME;
            const {owner, repo} = context.repo;
            const imageNameString = `${imageName}`;
            const imageNameLowerCase = imageNameString.toLowerCase();
            const packageName = `${repo}/${imageNameLowerCase}`;

            const result = await github.rest.packages.getPackageForOrganization({
              package_type: "container",
              package_name: packageName,
              org: owner
            });

            const deleteResult = await github.rest.packages.deletePackageForOrg({
              package_type: result.data.package_type,
              package_name: result.data.name,
              org: result.data.owner.login
            });


PR Integration Test (branches - full)

name: PR Integration Test (branches - full)
# INTEGRATION TEST FOR PULL REQUESTS ON BRANCHES != MAIN
# This file represents a complete version of the "BTP Setup Automator" integration tests
# for PRs on branches
# It executes the following steps:
# - based on the branch it creates a temporary Docker image and pushes it to the GH registry
# - it executes the integration tests on the temporary docker image
# - it deletes the temporary Docker image after execution
#
# All tests are executed in sequence on one BTP account to avoid entitlement limits
# This is why not matrix strategy is used
# The steps of a job must execute in any case, so we use always()

on:
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: tmp-${{ github.ref_name }}
  LABEL: ${{ github.ref_name }}
  ORG: sap-samples

jobs:
  build-temporary-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    outputs:
      image_path: ${{ steps.set-image-path.outputs.image_path }}
    steps:
      - uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v0.10.6
      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}
      - name: Build Docker image
        id: dockerbuild
        uses: docker/build-push-action@v5.1.0
        with:
          context: .
          file: ./config/Dockerfile
          build-args: |
            BTPSA_VERSION_GIT_ARG=${{ github.sha }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
      - id: set-image-path
        name: Set image path to output
        run: echo "image_path=${{ fromJSON(steps.dockerbuild.outputs.metadata)['image.name'] }}" >> $GITHUB_OUTPUT

  integration-test-for-pr-full:
    needs: build-temporary-image
    runs-on: ubuntu-latest
    container:
      image: ${{ needs.build-temporary-image.outputs.image_path }}
      options: --user root
    env:
      BTPSA_PARAM_MYEMAIL: ${{ secrets.BTPSA_PARAM_MYEMAIL }}
      BTPSA_PARAM_GLOBALACCOUNT: ${{ secrets.BTPSA_PARAM_GLOBALACCOUNT }}
      BTPSA_PARAM_MYPASSWORD: ${{ secrets.BTPSA_PARAM_MYPASSWORD }}
    steps:
      - name: 'Integration test 01: Default use case'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest01.json'
      - name: "Integration test 02: Deploy full-stack CAP application running in SAP Launchpad"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest02.json'
      - name: 'Integration test 07: Setup SAP Task Center'
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest07.json'
      - name: "Integration test 04: Only app subscriptions"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest04.json'
      - name: "Integration test 05: Only one service without app subscriptions and with an admin, that is the same like the one logging into the script."
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest05.json'
      - name: "Integration test 06: Create service keys"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest06.json'
      - name: "Integration test 10: Setup a Kyma environment on GCP"
        if: ${{ always() }}
        working-directory: /home/user
        shell: bash
        run: ./btpsa -parameterfile 'https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/tests/integrationtests/parameterfiles/integrationtest10.json'

  delete-temporary-image:
    if: ${{ success() }} || ${{ failure() }}
    needs: [build-temporary-image, integration-test-for-pr-full]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        env:
          IMAGE: ${{ needs.build-temporary-image.outputs.image_path }}
        with:
          github-token: ${{ secrets.DELETE_IMAGE_TOKEN }}
          script: |
            const imageName = process.env.IMAGE_NAME;
            const {owner, repo} = context.repo;
            const imageNameString = `${imageName}`;
            const imageNameLowerCase = imageNameString.toLowerCase();
            const packageName = `${repo}/${imageNameLowerCase}`;

            const result = await github.rest.packages.getPackageForOrganization({
              package_type: "container",
              package_name: packageName,
              org: owner
            });

            const deleteResult = await github.rest.packages.deletePackageForOrg({
              package_type: result.data.package_type,
              package_name: result.data.name,
              org: result.data.owner.login
            });


CodeQL

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: ["main", "dev"]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: ["main", "dev"]

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: ["python", "javascript"]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

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

      # Initializes the CodeQL tools for scanning.
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          # If you wish to specify custom queries, you can do so here or in a config file.
          # By default, queries listed here will override any specified in a config file.
          # Prefix the list here with "+" to use these queries and those in the config file.

          # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
          # queries: security-extended,security-and-quality

      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
      # If this step fails, then you should remove it and run the build manually (see below)
      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      # ℹ Command-line programs to run using the OS shell.
      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

      #   If the Autobuild fails above, remove it and uncomment the following three lines.
      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

      # - run: |
      #   echo "Run, Build Application using script"
      #   ./location_of_script_within_repo/buildscript.sh

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3


REUSE Compliance Check

name: REUSE Compliance Check
# To manually verify/falsify reuse checks

on:
  workflow_dispatch:

jobs:
  compliance-check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: REUSE Compliance Check
      uses: fsfe/reuse-action@v2


Build and Push Docker Image (DEV)

name: Build and Push Docker Image (DEV)

on:
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}-dev
  DEV_BRANCH: dev

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ env.DEV_BRANCH }}

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

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v0.11.6
        #with:
        #  buildkitd-flags: --debug

      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build Docker image
        uses: docker/build-push-action@v5.1.0
        with:
          context: .
          platforms: linux/amd64
          file: ./config/Dockerfile
          build-args: |
            BTPSA_VERSION_GIT_ARG=${{ github.sha }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


Build and Push Docker Image (RELEASE)

name: Build and Push Docker Image (RELEASE)

on:
#  release:
#    types: [published]
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.release.commitish }}

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

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v0.11.6

        #with:
        #  buildkitd-flags: --debug

      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build Docker image
        uses: docker/build-push-action@v5.1.0
        with:
          context: .
          platforms: linux/amd64
          file: ./config/Dockerfile
          build-args: |
            BTPSA_VERSION_GIT_ARG=${{ github.sha }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


Update metadata artifacts

name: Update metadata artifacts
# This action executes the following up actions needed after an update of the metadata happened
# 1. Generate the JSON schema for the usecase.json and the parameters.json file
# 2. Based on the JSON schemas available for the parameters.json and usecase.json generate the documentation as markdown

on:
  workflow_dispatch:
#  repository_dispatch:
#    types: [btp-metadata-update]

env:
  TARGET_BRANCH: dev
  METADATA_SOURCE_OWNER: SAP-samples
  METADATA_SOURCE_REPO: btp-service-metadata
  METADATA_VERSION: v0
  METADATA_FOLDER: developer
  METADATA_TARGET_CONFIG_FOLDER: config
  METADATA_TARGET_BASE_FOLDER: btp_base
  EXECUTED_BY_GHACTION: active

jobs:
  update-metadata-artifacts:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          ref: ${{ env.TARGET_BRANCH }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "18"

      # Fetch curated metadata from SAP-samples/btp-service-metadata
      - name: Clean up temporary metadata folder
        run: |
          mkdir -p ${{ env.METADATA_TARGET_CONFIG_FOLDER }}/${{ env.METADATA_TARGET_BASE_FOLDER }}/${{ env.METADATA_VERSION }}/${{ env.METADATA_FOLDER }}
          rm -rf ${{ env.METADATA_TARGET_CONFIG_FOLDER }}/${{ env.METADATA_TARGET_BASE_FOLDER }}/${{ env.METADATA_VERSION }}/${{ env.METADATA_FOLDER }}/*

      - name: Install dependencies for metadata fetching
        run: |
          cd .github/scripts
          npm ci

      - name: Fetch metadata
        run: |
          cd .github/scripts
          export METADATA_READ_TOKEN=${{ secrets.METADATA_READ_TOKEN }}
          export METADATA_SOURCE_OWNER=${{ env.METADATA_SOURCE_OWNER }}
          export METADATA_SOURCE_REPO=${{ env.METADATA_SOURCE_REPO }}
          export METADATA_VERSION=${{ env.METADATA_VERSION }}
          export METADATA_FOLDER=${{ env.METADATA_FOLDER }}
          export METADATA_TARGET_CONFIG_FOLDER=${{ env.METADATA_TARGET_CONFIG_FOLDER }}
          export METADATA_TARGET_BASE_FOLDER=${{ env.METADATA_TARGET_BASE_FOLDER }}
          npm run fetch:metadata

      # Generate JSON schema for usecase.json and parameters.json
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install dependencies for JSON schema generation
        run: |
          cd config/python
          pip install -r requirements.txt

      - name: Execute generation of JSON schemas
        run: |
          cd libs/python
          export METADATA_READ_TOKEN=${{ secrets.METADATA_READ_TOKEN }}
          export METADATA_SOURCE_OWNER=${{ env.METADATA_SOURCE_OWNER }}
          export METADATA_SOURCE_REPO=${{ env.METADATA_SOURCE_REPO }}
          export METADATA_VERSION=${{ env.METADATA_VERSION }}
          export METADATA_FOLDER=${{ env.METADATA_FOLDER }}
          export METADATA_TARGET_CONFIG_FOLDER=${{ env.METADATA_TARGET_CONFIG_FOLDER }}
          export METADATA_TARGET_BASE_FOLDER=${{ env.METADATA_TARGET_BASE_FOLDER }}
          export IS_EXECUTED_BY_GHACTION=${{ env.EXECUTED_BY_GHACTION }}
          python3 generator

      - name: Clean up temporary metadata folder
        run: rm -rf ${{ env.METADATA_TARGET_CONFIG_FOLDER }}/${{ env.METADATA_TARGET_BASE_FOLDER }}/*

      # Generate documentation for parameters of usecase.json and parameters.json
      - name: Install jsonschema2md
        run: npm install -g @adobe/jsonschema2md

      - name: Create/Clear output folder
        run: |
          mkdir -p docs/generated/
          rm -rf docs/generated/*

      - name: Copy base files to output folder
        run: |
          cp libs/btpsa-parameters.json docs/generated/
          cp libs/btpsa-usecase.json docs/generated/

      - name: Generate documentation
        run: jsonschema2md -d "docs/generated/" -o "docs/generated/" -e json -v 07 -h false  -x -

      - name: Remove base files from output folder
        # remove the source files as well as the generated main README.md
        run: |
          rm docs/generated/btpsa-parameters.json
          rm docs/generated/btpsa-usecase.json
          rm docs/generated/README.md

      - name: Commit and push changes
        run: |
          git add -A
          git config user.email "btpsabot@users.noreply.github.com"
          git config user.name "[btpsa bot] docu generation"
          git diff --quiet && git diff --staged --quiet || git commit -m "[btpsa bot] Generate parameter documentation"
          git push origin ${{ env.TARGET_BRANCH }}


Compatibility

name: Compatibility

on:
  create:
    tags: ['*']
  push:
    branches: ['master']
  schedule:
  - cron: 0 23 * * *

jobs:
  lint:
    name: Lint (Python ${{ matrix.python-version }})
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
    steps:
    - uses: actions/checkout@v3
    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('requirements/**') }}
        restore-keys: |
          ${{ runner.os }}-pip-
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip setuptools wheel
        pip install -r requirements/dev.txt
        python setup.py build
    - name: Lint with ruff
      run: |
        ruff coredis tests
    - name: Lint with black
      run: |
        black --check coredis tests
    - name: Check imports
      run: |
        isort -c --profile=black coredis tests
    - name: Check types
      run: |
        mypy coredis
    - name: Check auto generated sources
      run: |
        make templated-sources
        if [ ! -z "$(git diff coredis)" ];
        then
          echo "Auto-generated source are not up to date"
          git diff
          exit 1
        fi;
  test:
    name: Test (Python ${{ matrix.python-version }}, Redis ${{ matrix.redis-version }}${{ matrix.uvloop == 'True' && ', uvloop' || ''}}${{ matrix.orjson == 'True' && ', orjson' || ''}}${{ matrix.extensions == 'True' && ', compiled' || ''}}${{ matrix.label && format(', {0}', matrix.label) || '' }})
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.redis-version == 'next' }}
    strategy:
      fail-fast: false
      matrix:
        python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
        redis-version: ["6.2", "7.0", "7.2", "latest"]
        test_params: ["-m '(not (keydb or dragonfly or valkey or redict))'"]
        orjson: ["False"]
        uvloop: ["True", "False"]
        runtime_type_checks: ["True"]
        extensions: ["True"]
        label: [""]
        include:
          - python-version: "3.11"
            redis-version: next
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))'"
            runtime_type_checks: "True"
            label: ""
          - python-version: "3.11"
            redis-version: latest
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))'"
            extensions: "False"
            runtime_type_checks: "True"
            label: ""
          - python-version: "3.11"
            redis-version: latest
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))'"
            runtime_type_checks: "True"
            orjson: "True"
            label: ""
          - python-version: "3.11"
            redis-version: 7.0
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))'"
            runtime_type_checks: "False"
            label: ""
          - python-version: "3.11"
            redis-version: 7.0
            test_params: "-m keydb"
            runtime_type_checks: "False"
            label: "KeyDB"
          - python-version: "3.11"
            redis-version: 7.0
            test_params: "-m dragonfly"
            label: "Dragonfly"
          - python-version: "3.11"
            redis-version: 7.0
            test_params: "-m valkey"
            label: "Valkey"
          - python-version: "3.11"
            redis-version: 7.0
            test_params: "-m redict"
            label: "Redict"
          - python-version: "pypy-3.8"
            redis-version: 7.0
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))' tests/commands tests/test_tracking_cache.py"
            runtime_type_checks: "False"
            extensions: "False"
            uvloop: "False"
            label: ""
          - python-version: "pypy-3.9"
            redis-version: 7.0
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))' tests/commands tests/test_tracking_cache.py"
            runtime_type_checks: "False"
            extensions: "False"
            uvloop: "False"
            label: ""

    steps:
    - uses: actions/checkout@v3
    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('requirements/**') }}
        restore-keys: |
          ${{ runner.os }}-${{ matrix.python-version }}-pip-
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip setuptools wheel
        pip install -r requirements/ci.txt
        python setup.py build
    - name: Compile extensions
      if: ${{ matrix.extensions == 'True' }}
      run: python setup.py build_ext --inplace --use-mypyc
    - name: Install uvloop
      if: ${{ matrix.uvloop == 'True' }}
      run:
        pip install uvloop
    - name: Install orjson
      if: ${{ matrix.orjson == 'True' }}
      run:
        pip install orjson
    - name: Tests with coverage
      env:
        COREDIS_UVLOOP: ${{ matrix.uvloop }}
        HOST_OS: linux
        CI: "True"
        COREDIS_REDIS_VERSION: ${{matrix.redis-version}}
        COREDIS_RUNTIME_CHECKS: ${{matrix.runtime_type_checks}}
        PYTEST_SENTRY_DSN: ${{ matrix.extensions != 'True' && secrets.SENTRY_DSN || ''}}
      run: |
        echo "Runtime checks: $COREDIS_RUNTIME_CHECKS"
        echo "UVLoop: $COREDIS_UVLOOP"
        echo "CI: $CI"
        pytest -q --reverse --reruns 2 --cov=coredis --cov-report=xml ${{ matrix.test_params }}
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4.2.0
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}


CI

name: CI

on: [push, pull_request]

jobs:
  lint:
    name: Lint (Python ${{ matrix.python-version }})
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
    steps:
    - uses: actions/checkout@v3
    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('requirements/**') }}
        restore-keys: |
          ${{ runner.os }}-pip-
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip setuptools wheel
        pip install -r requirements/dev.txt
        python setup.py build
    - name: Lint with ruff
      run: |
        ruff coredis tests
    - name: Lint with black
      run: |
        black --check coredis tests
    - name: Check imports
      run: |
        isort -c --profile=black coredis tests
    - name: Check types
      run: |
        mypy coredis
    - name: Check auto generated sources
      run: |
        make templated-sources
        if [ ! -z "$(git diff coredis)" ];
        then
          echo "Auto-generated source are not up to date"
          git diff
          exit 1
        fi;
  test:
    name: Test (Python ${{ matrix.python-version }}, Redis ${{ matrix.redis-version }}${{ matrix.uvloop == 'True' && ', uvloop' || ''}}${{ matrix.orjson == 'True' && ', orjson' || ''}}${{ matrix.extensions == 'True' && ', compiled' || ''}}${{ matrix.label && format(', {0}', matrix.label) || '' }})
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.redis-version == 'next' }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
        redis-version: ["6.2", "7.0", "7.2", "latest"]
        test_params: ["-m '(not (keydb or dragonfly or valkey or redict))'"]
        uvloop: ["True", "False"]
        orjson: ["False"]
        runtime_type_checks: ["True"]
        extensions: ["True"]
        label: [""]
        include:
          - python-version: "3.11"
            redis-version: "latest"
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))'"
            runtime_type_checks: "True"
            extensions: "False"
          - python-version: "3.11"
            redis-version: "7.0"
            test_params: "-m keydb"
            label: "KeyDB"
          - python-version: "3.11"
            redis-version: "7.0"
            test_params: "-m dragonfly"
            label: "Dragonfly"
          - python-version: "3.11"
            redis-version: "7.0"
            test_params: "-m valkey"
            label: "Valkey"
          - python-version: "3.11"
            redis-version: "7.0"
            test_params: "-m redict"
            label: "Redict"
          - python-version: "pypy-3.9"
            redis-version: "7.0"
            test_params: "-m '(not (keydb or dragonfly or valkey or redict))' tests/commands tests/test_tracking_cache.py"
            runtime_type_checks: "False"
            extensions: "False"
            uvloop: "False"
            label: ""
    steps:
    - uses: actions/checkout@v3
    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('requirements/**') }}
        restore-keys: |
          ${{ runner.os }}-${{ matrix.python-version }}-pip-
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip setuptools wheel
        pip install -r requirements/ci.txt
        python setup.py build
    - name: Compile extensions
      if: ${{ matrix.extensions == 'True' }}
      run: python setup.py build_ext --inplace --use-mypyc
    - name: Install uvloop
      if: ${{ matrix.uvloop == 'True' }}
      run:
        pip install uvloop
    - name: Install orjson
      if: ${{ matrix.orjson == 'True' }}
      run:
        pip install orjson
    - name: Tests
      env:
        COREDIS_UVLOOP: ${{ matrix.uvloop }}
        HOST_OS: linux
        CI: "True"
        COREDIS_REDIS_VERSION: ${{matrix.redis-version}}
        COREDIS_RUNTIME_CHECKS: ${{matrix.runtime_type_checks}}
        PYTEST_SENTRY_DSN: ${{ matrix.extensions != 'True' && secrets.SENTRY_DSN || ''}}
      run: |
        echo "Runtime checks: $COREDIS_RUNTIME_CHECKS"
        echo "UVLoop: $COREDIS_UVLOOP"
        echo "CI: $CI"
        pytest -q --reverse --reruns 2 --cov=coredis --cov-report=xml ${{ matrix.test_params }}
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4.2.0
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  generate_wheels_matrix:
    name: Generate wheels matrix
    runs-on: ubuntu-latest
    outputs:
      include: ${{ steps.set-matrix.outputs.include }}
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Install cibuildwheel and pypyp
        run: |
          pipx install cibuildwheel==2.16.2
          pipx install pypyp==1
      - id: set-matrix
        run: |
          MATRIX=$(
            {
              cibuildwheel --print-build-identifiers --platform linux . \
              | pyp 'json.dumps({"only": x, "os": "ubuntu-latest"})' \
              && cibuildwheel --print-build-identifiers --platform macos . \
              | pyp 'json.dumps({"only": x, "os": "macos-latest"})'
            } | pyp 'json.dumps(list(map(json.loads, lines)))'
          )
          echo "include=$MATRIX" | tee -a $GITHUB_OUTPUT
        env:
          CIBW_ARCHS_LINUX: auto aarch64
          CIBW_ARCHS_MACOS: auto arm64
          CIBW_SKIP: '*musllinux* pp*'
  build_wheels:
    needs: [generate_wheels_matrix]
    name: Build wheel on ${{ matrix.only }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          ${{ fromJson(needs.generate_wheels_matrix.outputs.include) }}
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Set up QEMU
        if: runner.os == 'Linux'
        uses: docker/setup-qemu-action@v2
      - name: Build wheel
        uses: pypa/cibuildwheel@v2.16.2
        with:
          only: ${{ matrix.only }}
        env:
          CIBW_BUILD_VERBOSITY: 3
          CIBW_BUILD_FRONTEND: "build"
          CIBW_CONFIG_SETTINGS: "--build-option=--use-mypyc"
          CIBW_TEST_COMMAND: "python -c 'import coredis'"
      - uses: actions/upload-artifact@v3
        with:
          path: ./wheelhouse/*.whl
  build_pure_wheel:
    needs: [lint]
    name: Build pure python wheel
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Build wheels
        run: |
          python -m pip install build
          PURE_PYTHON=1 python -m build --wheel
      - uses: actions/upload-artifact@v3
        with:
          path: ./dist/*.whl
  build_sdist:
    name: Build source distribution
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Build sdist
        run: |
          pipx run build --sdist
      - uses: actions/upload-artifact@v3
        with:
          path: dist/*.tar.gz
  upload_pypi:
    needs: [test, build_wheels, build_pure_wheel, build_sdist]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master'
    steps:
      - uses: actions/download-artifact@v2
        with:
          name: artifact
          path: dist
      - uses: pypa/gh-action-pypi-publish@v1.4.2
        with:
          user: __token__
          password: ${{ secrets.TEST_PYPI_API_TOKEN }}
          repository_url: https://test.pypi.org/legacy/
          skip_existing: true
  upload_pypi_release:
    needs: [test, build_wheels, build_sdist]
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
    steps:
      - uses: actions/download-artifact@v2
        with:
          name: artifact
          path: dist

      - uses: pypa/gh-action-pypi-publish@v1.4.2
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}

  github_release:
    needs: [upload_pypi_release]
    name: Create Release
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Download artifacts
        uses: actions/download-artifact@v2
        with:
          name: artifact
          path: dist
      - name: Generate release notes
        run: |
          ./scripts/github_release_notes.sh > release_notes.md
      - name: Create Release
        uses: ncipollo/release-action@v1
        with:
          artifacts: "dist/*"
          bodyFile: release_notes.md
          token: ${{ secrets.GITHUB_TOKEN }}


black

# This is a workflow to format Python code with black formatter

name: black

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  pull_request:
  push:
    branches: [main]

permissions: read-all

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "black"
  black:
    # The type of runner that the job will run on
    runs-on: ubuntu-20.04

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v4
      # Set up a Python environment for use in actions
      - uses: actions/setup-python@v5

      # Run black code formatter
      - uses: psf/black@stable
        with:
          args: ". --check"
          version: "24.1.1"


Conda package

name: Conda package

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

permissions: read-all

env:
  PACKAGE_NAME: numba-dpex
  MODULE_NAME: numba_dpex
  # There is a separate action that removes defaults.
  CHANNELS: 'dppy/label/dev,conda-forge,intel,numba,nodefaults'
  VER_JSON_NAME: 'version.json'
  VER_SCRIPT1: "import json; f = open('version.json', 'r'); j = json.load(f); f.close(); "
  VER_SCRIPT2: "d = j['numba-dpex'][0]; print('='.join((d[s] for s in ('version', 'build'))))"
  PYTHONIOENCODING: 'utf-8'

jobs:
  build:
    name: Build ['${{ matrix.os }}', python='${{ matrix.python }}']

    strategy:
      fail-fast: false
      matrix:
        python: ['3.9', '3.10', '3.11']
        os: [ubuntu-latest, windows-2019]

    runs-on: ${{ matrix.os }}

    defaults:
      run:
        shell: bash -l {0}

    continue-on-error: false

    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
        with:
          access_token: ${{ github.token }}

      - name: Checkout ${{ env.PACKAGE_NAME }} repo
        uses: actions/checkout@v4
        with:
          # We need tags to build proper version
          fetch-depth: 0

      - name: Setup miniconda
        uses: ./.github/actions/setup-miniconda
        with:
          python-version: ${{ matrix.python }}
          activate-environment: "build"
          channels: ${{ env.CHANNELS }}
          channel-priority: "disabled"
          dependencies: "conda-build"
          environment-file: environment/conda-package-build.yml

      - name: Store conda paths as envs
        run: |
          echo "CONDA_BLD=$CONDA_PREFIX/conda-bld/${{ runner.os == 'Linux' && 'linux' || 'win' }}-64/" | tr "\\" '/' >> $GITHUB_ENV
          echo "WHEELS_OUTPUT_FOLDER=$GITHUB_WORKSPACE${{ runner.os == 'Linux' && '/' || '\\' }}" >> $GITHUB_ENV
          echo "WHEELS_NAME=$PACKAGE_NAME" | tr "-" "_" >> $GITHUB_ENV

      - name: Build conda package
        run: conda build --no-test --python ${{ matrix.python }} conda-recipe

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }}
          path: ${{ env.CONDA_BLD }}${{ env.PACKAGE_NAME }}-*.tar.bz2

      - name: Upload wheels artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Wheels Python ${{ matrix.python }}
          path: ${{ env.WHEELS_OUTPUT_FOLDER }}${{ env.WHEELS_NAME }}-*.whl

  test:
    name: Test ['${{ matrix.os }}', '${{ matrix.scope }}', python='${{ matrix.python }}']

    needs: build

    runs-on: ${{ matrix.os }}

    defaults:
      run:
        shell: ${{ matrix.os == 'windows-latest' && 'cmd /C CALL {0}' || 'bash -l {0}' }}

    strategy:
      fail-fast: false
      matrix:
        python: ['3.9', '3.10', '3.11']
        os: [ubuntu-20.04, ubuntu-latest, windows-latest]
        experimental: [false]
        scope: ['tests', 'examples', 'gdb']
        exclude:
          - os: windows-latest
            scope: 'gdb'

    continue-on-error: ${{ matrix.experimental }}

    steps:
      - name: Checkout setup config
        uses: actions/checkout@v4
        with:
          sparse-checkout: |
            .github/actions
            environment
            pyproject.toml
          sparse-checkout-cone-mode: false

      - name: Setup miniconda
        uses: ./.github/actions/setup-miniconda
        with:
          python-version: ${{ matrix.python }}
          activate-environment: "test"
          channels: ${{ env.CHANNELS }}
          channel-priority: "disabled"
          dependencies: "conda-index;pytest-cov;pexpect;conda-tree"
          environment-file: environment/conda-package-test.yml

      - name: Store conda paths as envs
        shell: bash -l {0}
        run: |
          echo "CHANNEL_PATH=${{ github.workspace }}/channel/" | tr "\\" "/" >> $GITHUB_ENV
          echo "EXTRACTED_PKG_PATH=${{ github.workspace }}/pkg/" | tr "\\" "/" >> $GITHUB_ENV
          echo "VER_JSON_PATH=${{ github.workspace }}/version.json" | tr "\\" "/" >> $GITHUB_ENV
          echo "PKG_PATH_IN_CHANNEL=${{ github.workspace }}/channel/${{ runner.os == 'Linux' && 'linux' || 'win' }}-64/" | tr "\\" "/" >> $GITHUB_ENV
          echo "EXAMPLES_PATH=$CONDA_PREFIX/${{ runner.os == 'Linux' && format('lib/python{0}',matrix.python) || 'Lib' }}/site-packages/numba_dpex/examples/" | tr "\\" "/" >> $GITHUB_ENV

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }}
          path: ${{ env.PKG_PATH_IN_CHANNEL }}

      # We need --force-local because on windows path looks like C:/foo/bar
      # and tar interprits semicolons as remote device.
      - name: Extract package archive
        shell: bash -l {0}
        run: |
          mkdir -p ${EXTRACTED_PKG_PATH}
          tar -xvf ${PKG_PATH_IN_CHANNEL}${PACKAGE_NAME}-*.tar.bz2 -C ${EXTRACTED_PKG_PATH} --force-local

      - name: Create conda channel
        # Note conda index (instead of python -m conda_index) may find legacy
        # conda-build index.
        run: |
          python -m conda_index ${{ env.CHANNEL_PATH }}

      - name: Test conda channel
        run: |
          conda search ${{ env.PACKAGE_NAME }} -c ${{ env.CHANNEL_PATH }} --override-channels --info --json > ${{ env.VER_JSON_PATH }}
          cat ${{ env.VER_JSON_PATH }}

      - name: Collect dependencies
        shell: bash -l {0}
        run: |
          export PACKAGE_VERSION=$(python -c "${{ env.VER_SCRIPT1 }} ${{ env.VER_SCRIPT2 }}")
          echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV

      # We want to make sure that all dependecies install automatically.
      # intel::intel-opencl-rt is needed for set-intel-ocl-icd-registry.ps1
      - name: Install built package
        run: conda install ${{ env.PACKAGE_NAME }}=${{ env.PACKAGE_VERSION }} intel::intel-opencl-rt -c ${{ env.CHANNEL_PATH }}

      - name: Setup OpenCL CPU device
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          $script_path="$env:CONDA_PREFIX\Scripts\set-intel-ocl-icd-registry.ps1"
          &$script_path

      - name: List installed packages
        run: conda list

      - name: Check dpcpp-llvm-spirv
        run: |
          python -c "import dpcpp_llvm_spirv as p; print(p.get_llvm_spirv_path())"

      - name: Check dependency tree
        run: conda-tree depends -t numba-dpex

      - name: Smoke test
        run: python -c "import dpnp, dpctl, numba_dpex; dpctl.lsplatform(verbosity=2)"

      - name: Smoke test - test import with no default device
        env:
          ONEAPI_DEVICE_SELECTOR: unknown:unknown
        run: python -c "import numba_dpex"

      - name: Run tests
        if: ${{ matrix.scope == 'tests' }}
        run: |
          pytest -q -ra --disable-warnings --pyargs ${{ env.MODULE_NAME }} -vv -k "not test_1d_strided_dpnp_array_in_kernel[2]"

      - name: Run backendless optimization tests
        # Running tests that have been found to fail on AMD CPUs with
        # -cl-opt-disable. The test failures do not happen on other platforms
        # and are possibly due to some driver/opencl compiler bug.
        if: ${{ matrix.scope == 'tests' }}
        env:
          # Disabling device driver optimization to prevent catching bugs
          # from driver compiler.
          ONEAPI_DEVICE_SELECTOR: "opencl:cpu"
          NUMBA_DPEX_BUILD_KERNEL_OPTIONS: "-cl-opt-disable"
        run: |
          pytest -q -ra --disable-warnings --pyargs ${{ env.MODULE_NAME }} -vv -k "test_1d_strided_dpnp_array_in_kernel[2]"

      - name: Run examples
        if: ${{ matrix.scope == 'examples' }}
        shell: bash -l {0}
        run: |
          cd ${{ env.EXAMPLES_PATH }}
          for script in $(find . \( -not -name "_*" -not -name "side-by-side*" -not -name "scan.py" -and -name "*.py" \))
          do
            echo "Executing ${script}"
            python ${script} || exit 1
          done

      - name: Run gdb tests
        if: ${{ matrix.scope == 'gdb' }}
        env:
          GDB_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/fc87666c-d626-47bc-a861-a1578d2ecbd3/l_dpcpp_dbg_p_2024.1.0.439_offline.sh
          GDB_INSTALLER: l_dpcpp_dbg_p_2024.1.0.439_offline.sh
          # To read gdb communication in case test fails to determine what the
          # issue is
          NUMBA_DPEX_TESTING_LOG_DEBUGGING: 1
        run: |
          wget -nc -q ${{ env.GDB_URL }}
          chmod +x ${{ env.GDB_INSTALLER }}
          mkdir /tmp/gdb
          export ONEAPI_ROOT=/tmp/gdb
          ./${{ env.GDB_INSTALLER }} -a -s --eula accept --install-dir $ONEAPI_ROOT
          source $ONEAPI_ROOT/debugger/latest/env/vars.sh
          # We match only major version because latest gdb is not often
          # available.
          gdb_version=$(echo "$GDB_INSTALLER" | grep -oP '\d+' | head -n 1)
          icpx_version=$(conda list dpcpp-cpp-rt | tail -n 1 | awk '{print $2}' | grep -oP '\d+' | head -n 1)
          if [ "$gdb_version" != "$icpx_version" ]; then
            echo "Error: GDB version ($gdb_version) does not match icpx version ($icpx_version)"
            exit 1
          fi
          pytest -q -ra --disable-warnings --pyargs ${{ env.MODULE_NAME }}.tests.debugging -vv

  upload_anaconda:
    name: Upload dppy/label/dev ['${{ matrix.os }}', python='${{ matrix.python }}']
    if: ${{github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/heads/release') == true)}}

    needs: [test]

    strategy:
      matrix:
        python: ['3.9', '3.10', '3.11']
        os: [ubuntu-latest, windows-latest]

    runs-on: ${{ matrix.os }}

    defaults:
      run:
        shell: bash -l {0}

    continue-on-error: false

    steps:
      - name: Checkout setup config
        uses: actions/checkout@v4
        with:
          sparse-checkout: |
            .github/actions
            environment

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Python ${{ matrix.python }}

      - name: Download wheels artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.PACKAGE_NAME }} ${{ runner.os }} Wheels Python ${{ matrix.python }}

      - name: Setup miniconda
        uses: ./.github/actions/setup-miniconda
        with:
          python-version: ${{ matrix.python }}
          activate-environment: "anaconda"
          channel-priority: "disabled"
          dependencies: "anaconda-client"
          environment-file: environment/conda-package-anaconda.yml

      - name: Upload
        run: anaconda --token ${{ secrets.ANACONDA_TOKEN }} upload --user dppy --label dev ${{ env.PACKAGE_NAME }}-*.tar.bz2

      - name: Package version
        run: echo "PACKAGE_VERSION=$(basename ${{ env.PACKAGE_NAME }}-*.tar.bz2 | sed 's/^${{ env.PACKAGE_NAME }}-\([^-]*\).*/\1/')" >> $GITHUB_ENV

      - name: Store wheels name
        run: |
          echo "WHEELS_NAME=$PACKAGE_NAME" | tr "-" "_" >> $GITHUB_ENV

      - name: Upload Wheels
        run: anaconda --token ${{ secrets.ANACONDA_TOKEN }} upload --user dppy --label dev ${{ env.WHEELS_NAME }}-*.whl --version ${{ env.PACKAGE_VERSION }}

  cleanup_packages:
    name: Clean up anaconda packages
    needs: [upload_anaconda]
    runs-on: 'ubuntu-latest'
    defaults:
      run:
        shell: bash -el {0}
    steps:
      - name: Checkout setup config
        uses: actions/checkout@v4
        with:
          sparse-checkout: |
            .github/actions
            environment

      - name: Setup miniconda
        uses: ./.github/actions/setup-miniconda
        with:
          python-version: '3.11'
          activate-environment: "anaconda"
          channel-priority: "disabled"
          dependencies: "anaconda-client"
          environment-file: environment/conda-package-anaconda.yml

      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          repository: IntelPython/devops-tools

      - name: Cleanup old packages
        run: |
          python scripts/cleanup-old-packages.py \
          --verbose --force --token ${{ secrets.ANACONDA_TOKEN }} \
          --package dppy/${{ env.PACKAGE_NAME }} --label dev


Coverage

name: Coverage
on:
  push:
    branches:
      - main
  pull_request:

permissions: read-all

jobs:
  main:
    name: Generate coverage and push to Coveralls.io
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    defaults:
      run:
        shell: bash -l {0}
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
        with:
          access_token: ${{ github.token }}

      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: conda-incubator/setup-miniconda@v3
        with:
          python-version: '3.10'
          miniforge-variant: Mambaforge
          miniforge-version: latest
          activate-environment: "coverage"
          channel-priority: "disabled"
          environment-file: environment/coverage.yml

      - name: Build numba-dpex
        run: |
          export PATH=$CONDA/bin-llvm:$PATH
          CC=icx CXX=icpx python setup.py develop

      - name: Test installation
        run: |
          conda list
          python -c "import numba_dpex; print(numba_dpex.__file__)"

      - name: Dump coverage test environment
        run: |
          conda env export > /tmp/env-cov.yml
          cat /tmp/env-cov.yml

      # Ignoring test due to opencl driver optimization bug
      - name: Run tests with coverage
        run: |
          pytest  -q --cov=./ --cov-report xml --pyargs numba_dpex \
            -k 'not test_1d_strided_dpnp_array_in_kernel[2]'

      - name: Coveralls
        uses: coverallsapp/github-action@v2


Coverity

name: Coverity
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
  workflow_dispatch:

permissions: read-all

jobs:
  Coverity:

    runs-on: ubuntu-latest

    env:
      CHECKERS: --concurrency --security --rule --enable-constraint-fpp --enable-fnptr --enable-virtual --webapp-security --enable-audit-checkers --enable-default

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-java@v4
      with:
        distribution: zulu
        java-version: 11

    - name: URL encode project name
      run: echo "COV_PROJECT=${{ github.repository }}" | sed -e 's:/:%2F:g' -e 's/ /%20/g' >> $GITHUB_ENV

    - name: Coverity Download
      run: |
        mkdir -p /tmp/cov-analysis
        wget https://scan.coverity.com/download/linux64 --post-data "token=${{secrets.COV_TOKEN}}&project=${{env.COV_PROJECT}}" -O cov-analysis.tgz
        tar -xzf cov-analysis.tgz --strip 1 -C /tmp/cov-analysis
        rm cov-analysis.tgz

    - name: Coverity Full Scan
      if: ${{ github.event_name != 'pull_request' }}
      run: |
        export PATH=$PATH:/tmp/cov-analysis/bin
        set -x
        cov-build --dir cov-int --fs-capture-search $GITHUB_WORKSPACE --no-command
        # Not available in package, maybe will be once approved?
        # cov-analyze --dir cov-int --ticker-mode none --strip-path $GITHUB_WORKSPACE $CHECKERS

        tar czvf numba-dpex.tgz cov-int
        rm -rf cov-int

        curl --form token=${{ secrets.COV_TOKEN }} \
            --form email=${{ secrets.COV_EMAIL }} \
            --form file=@numba-dpex.tgz \
            --form version="${{ github.sha }}" \
            --form description="Coverity Scan ${{ github.repository }} / ${{ github.ref }}" \
            https://scan.coverity.com/builds?project=${{env.COV_PROJECT}}


C++ Code Style

# This is a workflow to format C/C++ sources with clang-format

name: C++ Code Style

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  pull_request:
  push:
    branches: [master]

permissions: read-all

jobs:
  formatting-check:
    name: clang-format
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Run clang-format style check for C/C++ programs.
      uses: jidicula/clang-format-action@v4.13.0
      with:
        clang-format-version: '14'
        check-path: 'numba_dpex/dpctl_iface'


Docker build

name: Docker build

on:
  workflow_dispatch:
    inputs:
      version:
        default: '0.21.0'
      dpctl_version:
        default: '0.14.4'
      dpnp_version:
        default: '0.12.0'
      numpy_version:
        default: '1.24.3'
      numba_version:
        default: '0.58'
      # dpcpp_llvm_spirv_version:
      #   default: 'main'
      cython_version:
        default: "0.29.35"
      scikit_build_version:
        default: "0.17.6"
      oneapi_version:
        default: '2023.1.0.46401'
      python_versions:
        default: '["3.9", "3.10", "3.11"]'
      buildkit_version:
        default: '0.11.6'

permissions: read-all

jobs:
  env:
    runs-on: ubuntu-latest
    outputs:
      repository: ${{ steps.write.outputs.repository }}
      BUILD_ARGS: ${{ steps.write.outputs.BUILD_ARGS }}
      DOCKER_STAGES_IMAGE: ${{ steps.write.outputs.DOCKER_STAGES_IMAGE }}
      DOCKER_REPO_IMAGE: ${{ steps.write.outputs.DOCKER_REPO_IMAGE }}
      VERSION: ${{ steps.write.outputs.VERSION }}
    steps:
      - name: Write output env
        id: write
        run: |
          export REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')
          echo repository=$REPOSITORY >> "$GITHUB_OUTPUT"
          echo "BUILD_ARGS<<EOF" >> $GITHUB_OUTPUT

          echo "ONEAPI_VERSION=${{ inputs.oneapi_version }}" >> $GITHUB_OUTPUT
          echo "DPCTL_GIT_BRANCH=${{ inputs.dpctl_version }}" >> $GITHUB_OUTPUT
          echo "DPNP_GIT_BRANCH=${{ inputs.dpnp_version }}" >> $GITHUB_OUTPUT
          # echo "DPCPP_LLVM_SPIRV_GIT_BRANCH=${{ inputs.dpcpp_llvm_spirv_version }}" >> $GITHUB_OUTPUT
          echo "NUMBA_DPEX_GIT_BRANCH=${{ inputs.version }}" >> $GITHUB_OUTPUT
          echo "INTEL_NUMPY_VERSION===${{ inputs.numpy_version }}" >> $GITHUB_OUTPUT
          echo "INTEL_NUMBA_VERSION===${{ inputs.numba_version }}" >> $GITHUB_OUTPUT
          echo "CYTHON_VERSION===${{ inputs.cython_version }}" >> $GITHUB_OUTPUT
          echo "SCIKIT_BUILD_VERSION===${{ inputs.scikit_build_version }}" >> $GITHUB_OUTPUT

          echo "EOF" >> $GITHUB_OUTPUT

          echo "DOCKER_STAGES_IMAGE=ghcr.io/$REPOSITORY/stages" >> $GITHUB_OUTPUT
          echo "DOCKER_REPO_IMAGE=ghcr.io/$REPOSITORY" >> $GITHUB_OUTPUT
          echo "VERSION=$(echo ${{ inputs.version }} | tr '/' '-')" >> $GITHUB_OUTPUT

  build-toolkit:
    needs: env
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: toolkit
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            PYTHON_VERSION=${{ matrix.python_version }}

  build-runtime-base:
    needs: [env, build-toolkit]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: runtime-base
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            TOOLKIT_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-toolkit
            PYTHON_VERSION=${{ matrix.python_version }}

  build-builder:
    needs: [env, build-runtime-base]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: builder
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            TOOLKIT_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-toolkit
            RUNTIME_BASE_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-runtime-base
            PYTHON_VERSION=${{ matrix.python_version }}

  build-dpctl-builder:
    needs: [env, build-builder]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: dpctl-builder
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-builder
            PYTHON_VERSION=${{ matrix.python_version }}

  build-dpnp-builder:
    needs: [env, build-dpctl-builder]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: dpnp-builder
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-builder
            DPCTL_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpctl-builder
            PYTHON_VERSION=${{ matrix.python_version }}

  build-dpcpp-llvm-spirv:
    needs: [env, build-builder]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: dpcpp-llvm-spirv-builder
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
      DEST_TAG: ${{ needs.env.outputs.DOCKER_REPO_IMAGE }}/builder:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }},${{ env.DEST_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-builder

  build-numba-dpex-builder-runtime:
    needs: [env, build-dpctl-builder, build-dpnp-builder, build-dpcpp-llvm-spirv]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: numba-dpex-builder-runtime
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
      DEST_TAG: ${{ needs.env.outputs.DOCKER_REPO_IMAGE }}/builder:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }},${{ env.DEST_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-builder
            DPCTL_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpctl-builder
            DPNP_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpnp-builder
            DPCPP_LLVM_SPIRV_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpcpp-llvm-spirv-builder
            PYTHON_VERSION=${{ matrix.python_version }}

  build-runtime:
    needs: [env, build-runtime-base, build-dpctl-builder, build-dpnp-builder, build-numba-dpex-builder-runtime, build-dpcpp-llvm-spirv]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: runtime
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
      DEST_TAG: ${{ needs.env.outputs.DOCKER_REPO_IMAGE }}/runtime:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }},${{ env.DEST_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-builder
            DPCTL_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpctl-builder
            DPNP_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpnp-builder
            DPCPP_LLVM_SPIRV_BUILDER_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-dpcpp-llvm-spirv-builder
            NUMBA_DPEX_BUILDER_RUNTIME_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-numba-dpex-builder-runtime
            RUNTIME_BASE_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-runtime-base
            PYTHON_VERSION=${{ matrix.python_version }}

  build-drivers:
    needs: [env]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: drivers
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            PYTHON_VERSION=${{ matrix.python_version }}

  build-runtime-gpu:
    needs: [env, build-runtime, build-drivers]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: runtime-gpu
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
      DEST_TAG: ${{ needs.env.outputs.DOCKER_REPO_IMAGE }}/runtime:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}-gpu
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }},${{ env.DEST_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            RUNTIME_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-runtime
            PYTHON_VERSION=${{ matrix.python_version }}

  build-numba-dpex-builder-runtime-gpu:
    needs: [env, build-numba-dpex-builder-runtime, build-drivers]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
    env:
      BUILD_TARGET: numba-dpex-builder-runtime-gpu
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
      DEST_TAG: ${{ needs.env.outputs.DOCKER_REPO_IMAGE }}/builder:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}-gpu
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: |
            image=moby/buildkit:v${{ inputs.buildkit_version }}
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}
          tags: ${{ env.DOCKER_TAG }}-${{ env.BUILD_TARGET }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          target: ${{ env.BUILD_TARGET }}
          tags: ${{ steps.meta.outputs.tags }},${{ env.DEST_TAG }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            ${{ needs.env.outputs.BUILD_ARGS }}
            GITHUB_USER=${{ github.actor }}
            GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }}
            RUNTIME_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-runtime
            NUMBA_DPEX_BUILDER_RUNTIME_IMAGE=${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ env.DOCKER_TAG }}-numba-dpex-builder-runtime
            PYTHON_VERSION=${{ matrix.python_version }}

  test:
    needs: [env, build-runtime]
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python_version: ${{ fromJSON(inputs.python_versions) }}
        pyargs: ['dpctl', 'numba_dpex.tests', '/opt/dpnp/tests', '/opt/dpnp/tests/third_party']
    env:
      DOCKER_TAG: ${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}
    container:
      image: ${{ needs.env.outputs.DOCKER_STAGES_IMAGE }}:${{ needs.env.outputs.VERSION }}-py${{ matrix.python_version }}-runtime
      credentials:
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - name: List devices
        run: sycl-ls
      - name: Python version
        run: python --version
      - name: Setup pytest
        run: pip install pytest
      - name: Testing ${{ matrix.pyargs }}
        run: pytest --pyargs ${{ matrix.pyargs }}