Close stale issues/PRs

# close-stale-issues (https://github.com/marketplace/actions/close-stale-issues)
name: Close stale issues/PRs

on:
  schedule:
    - cron: '0 4 * * *'
  workflow_dispatch:

permissions:
  actions: write
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          # General parameters
          operations-per-run: 100
          remove-stale-when-updated: false

          # Issue parameters
          close-issue-message: >
            This issue has been automatically closed due to lack of activity. In an
            effort to reduce noise, please do not comment any further. Note that the
            core maintainers may elect to reopen this issue at a later date if deemed
            necessary.
          days-before-issue-stale: 90
          days-before-issue-close: 30
          exempt-issue-labels: 'status: accepted,status: backlog,status: blocked'
          stale-issue-label: 'pending closure'
          stale-issue-message: >
            This issue has been automatically marked as stale because it has not had
            recent activity. It will be closed if no further activity occurs. NetBox
            is governed by a small group of core maintainers which means not all opened
            issues may receive direct feedback. **Do not** attempt to circumvent this
            process by "bumping" the issue; doing so will result in its immediate closure
            and you may be barred from participating in any future discussions. Please see
            our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md).

          # Pull request parameters
          close-pr-message: >
            This PR has been automatically closed due to lack of activity.
          days-before-pr-stale: 15
          days-before-pr-close: 15
          stale-pr-label: 'pending closure'
          stale-pr-message: >
            This PR has been automatically marked as stale because it has not had
            recent activity. It will be closed automatically if no further action is
            taken.


Lock threads

# lock-threads (https://github.com/marketplace/actions/lock-threads)
name: Lock threads

on:
  schedule:
    - cron: '0 3 * * *'
  workflow_dispatch:

permissions:
  issues: write
  pull-requests: write
  discussions: write

jobs:
  lock:
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/lock-threads@v5
        with:
          issue-inactive-days: 90
          pr-inactive-days: 30
          discussion-inactive-days: 180
          issue-lock-reason: 'resolved'


Update translation strings

name: Update translation strings

on:
  schedule:
    - cron: '0 5 * * *'
  workflow_dispatch:

permissions:
  contents: write

env:
  LOCALE: "en"

jobs:
  makemessages:
    runs-on: ubuntu-latest
    env:
      NETBOX_CONFIGURATION: netbox.configuration_testing

    steps:
    - name: Check out repo
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: 3.11

    - name: Install system dependencies
      run: sudo apt install -y gettext

    - name: Install Python dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt

    - name: Run makemessages
      run: python netbox/manage.py makemessages -l ${{ env.LOCALE }}

    - name: Commit changes
      uses: EndBug/add-and-commit@v9
      with:
        add: 'netbox/translations/'
        default_author: github_actions
        message: 'Update source translation strings'


Cherry-Pick into Release Branch

name: Cherry-Pick into Release Branch

on:
  push:
    branches: ["master"]

jobs:
  release_pull_requestk:
    name: cherry_pick_into_${{ matrix.release-branch }}
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        release-branch: [release-0.7, release-0.8]

    steps:
      - name: checkout
        uses: actions/checkout@v1

      - name: Check PR labels action step
        id: check_pr_labels
        uses: shioyang/check-pr-labels-on-push-action@v1.0.6
        with:
          github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
          labels: '["${{ matrix.release-branch }}"]'

      - name: Create PR into release branch
        if: steps.check_pr_labels.outputs.result == 'true'
        uses: gorillio/github-action-cherry-pick@master
        with:
          pr_branch: ${{ matrix.release-branch }}
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
          DRY_RUN: false


docker

name: docker

on:
  schedule:
    - cron: "0 10 * * *" # everyday at 10am
  push:
    branches: ["master", "release-*"]
    tags: ["v*.*.*"]

jobs:
  start-runner:
    name: Start self-hosted EC2 runner
    if: >
      github.event_name == 'schedule' && github.repository == 'ludwig-ai/ludwig' ||
      github.event_name == 'push' && github.repository == 'ludwig-ai/ludwig' ||
      github.event_name == 'pull_request' && github.event.pull_request.base.repo.full_name == 'ludwig-ai/ludwig' && !github.event.pull_request.head.repo.fork
    runs-on: ubuntu-latest
    outputs:
      label: ${{ steps.start-ec2-runner.outputs.label }}
      ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}

    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Start EC2 runner
        id: start-ec2-runner
        uses: machulav/ec2-github-runner@v2.3.2
        with:
          mode: start
          github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
          ec2-image-id: ami-0759580dedc953d1f
          ec2-instance-type: r5.large
          subnet-id: subnet-0983be43
          security-group-id: sg-4cba0d08
          aws-resource-tags: >
            [
              {"Key": "Name", "Value": "ludwig-github-${{ github.head_ref || github.sha }}"},
              {"Key": "GitHubRepository", "Value": "${{ github.repository }}"},
              {"Key": "GitHubHeadRef", "Value": "${{ github.head_ref }}"},
              {"Key": "GitHubSHA", "Value": "${{ github.sha }}"}
            ]

  docker:
    name: Build docker image ${{ matrix.docker-image }} (push=${{ github.event_name != 'pull_request' }})
    if: needs.start-runner.result != 'skipped'
    needs: start-runner # required to start the main job when the runner is ready
    runs-on: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runners

    # we want an ongoing run of this workflow to be canceled by a later commit
    # so that there is only one concurrent run of this workflow for each branch
    concurrency:
      group: docker-${{ matrix.docker-image }}-${{ github.head_ref || github.sha }}
      cancel-in-progress: true

    strategy:
      fail-fast: false
      matrix:
        docker-image:
          - ludwig
          - ludwig-gpu
          - ludwig-ray
          - ludwig-ray-gpu

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

      - name: Docker meta
        id: meta
        uses: crazy-max/ghaction-docker-meta@v2
        with:
          # list of Docker images to use as base name for tags
          images: |
            ludwigai/${{ matrix.docker-image }}
          # generate Docker tags based on the following events/attributes
          tags: |
            type=schedule
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha

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

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

      - name: Login to DockerHub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./docker/${{ matrix.docker-image }}/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  stop-runner:
    name: Stop self-hosted EC2 runner

    # required to stop the runner even if the error happened in the previous job
    if: always() && needs.start-runner.result != 'skipped'
    needs:
      - start-runner # required to get output from the start-runner job
      - docker # required to wait when the main job is done
    runs-on: ubuntu-latest

    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Stop EC2 runner
        uses: machulav/ec2-github-runner@v2.3.2
        with:
          mode: stop
          github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
          label: ${{ needs.start-runner.outputs.label }}
          ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}


pytest

# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: pytest

on:
  push:
    branches: ["master", "release-*"]
  pull_request:
    branches: ["master", "release-*"]

# We want an ongoing run of this workflow to be canceled by a later commit
# so that there is only one concurrent run of this workflow for each branch
concurrency:
  group: pytest-${{ github.head_ref || github.sha }}
  cancel-in-progress: true

jobs:
  pytest:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        python-version: ["3.8", "3.9", "3.10"]
        test-markers: ["not distributed", "distributed"]
        include:
          - python-version: "3.8"
            pytorch-version: 2.0.0
            torchscript-version: 1.10.2
            ray-version: 2.3.1
          - python-version: "3.9"
            pytorch-version: 2.1.1
            torchscript-version: 1.10.2
            ray-version: 2.3.1
          - python-version: "3.10"
            # pytorch-version: nightly
            pytorch-version: 2.2.1
            torchscript-version: 1.10.2
            ray-version: 2.3.1
    env:
      PYTORCH: ${{ matrix.pytorch-version }}
      MARKERS: ${{ matrix.test-markers }}
      NEUROPOD_BASE_DIR: "/usr/local/lib/neuropod"
      NEUROPOD_VERISON: "0.3.0-rc6"
      TORCHSCRIPT_VERSION: ${{ matrix.torchscript-version }}
      RAY_VERSION: ${{ matrix.ray-version }}
      AWS_ACCESS_KEY_ID: ${{ secrets.LUDWIG_TESTS_AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.LUDWIG_TESTS_AWS_SECRET_ACCESS_KEY }}
      KAGGLE_USERNAME: ${{ secrets.KAGGLE_USERNAME }}
      KAGGLE_KEY: ${{ secrets.KAGGLE_KEY }}
      IS_NOT_FORK: ${{ !(github.event.pull_request.base.repo.full_name == 'ludwig-ai/ludwig' && github.event.pull_request.head.repo.fork) }}

    name: py${{ matrix.python-version  }}, torch-${{ matrix.pytorch-version }}, ${{ matrix.test-markers }}, ${{ matrix.os }}, ray ${{ matrix.ray-version }}
    services:
      minio:
        image: fclairamb/minio-github-actions
        env:
          MINIO_ACCESS_KEY: minio
          MINIO_SECRET_KEY: minio123
        ports:
          - 9000:9000

    timeout-minutes: 150
    steps:
      - name: Setup ludwigai/ludwig-ray container for local testing with act.
        if: ${{ env.ACT }}
        run: |
          curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
          sudo apt-get install -y nodejs
          sudo mkdir -p /opt/hostedtoolcache/
          sudo chmod 777 -R /opt/hostedtoolcache/
      - uses: actions/checkout@v2
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1 wget libsox-dev

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: pip cache
        if: ${{ !env.ACT }}
        uses: actions/cache@v2
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-py${{ matrix.python-version }}-torch${{ matrix.pytorch-version }}-${{ matrix.test-markers }}-${{ hashFiles('requirements*.txt', '.github/workflows/pytest.yml') }}

      - name: Debug out of space
        run: |
          du -h -d 1 ~
          df -h

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip
          cmake --version

          # remove torch and ray from the dependencies so we can add them depending on the matrix args for the job.
          cat requirements.txt | sed '/^torch[>=<\b]/d' | sed '/^torchtext/d' | sed '/^torchvision/d' | sed '/^torchaudio/d' > requirements-temp && mv requirements-temp requirements.txt
          cat requirements_distributed.txt | sed '/^ray[\[]/d'

          if [ "$MARKERS" != "distributed" ]; then
            # Skip distributed and hyperopt requirements to test optional imports
            echo > requirements-temp && mv requirements-temp requirements_distributed.txt
            echo > requirements-temp && mv requirements-temp requirements_hyperopt.txt

            # Skip distributed tree requirement (lightgbm-ray)
            cat requirements_tree.txt | sed '/^lightgbm-ray/d' > requirements-temp && mv requirements-temp requirements_tree.txt
          else
            if [ "$RAY_VERSION" == "nightly" ]; then
              # NOTE: hardcoded for python 3.10 on Linux
              echo "ray[default,data,serve,tune] @ https://s3-us-west-2.amazonaws.com/ray-wheels/latest/ray-3.0.0.dev0-cp310-cp310-manylinux2014_x86_64.whl" >> requirements_distributed.txt
            else
              echo "ray[default,data,serve,tune]==$RAY_VERSION" >> requirements_distributed.txt
            fi
          fi

          if [ "$PYTORCH" == "nightly" ]; then
            extra_index_url=https://download.pytorch.org/whl/nightly/cpu
            pip install --pre torch torchtext torchvision torchaudio --extra-index-url $extra_index_url

          else
            extra_index_url=https://download.pytorch.org/whl/cpu
            pip install torch==$PYTORCH torchtext torchvision torchaudio --extra-index-url $extra_index_url
          fi

          pip install '.[test]' --extra-index-url $extra_index_url
          pip list

          if [ "$PYTORCH" == "nightly" ]; then
            python -c "from packaging import version; import torch; assert version.parse(torch.__version__).release >= version.parse(\"2.0.0\").release, f\"torch {version.parse(torch.__version__).release} < version.parse(\'2.0.0\').release\""
          else
            python -c "from packaging import version; import torch; assert version.parse(torch.__version__).release == version.parse(\"$PYTORCH\").release, f\"torch {version.parse(torch.__version__).release} != version.parse(\'$PYTORCH\').release\""
          fi

          if [ "$MARKERS" == "distributed" ]; then
            python -c "from packaging import version; import ray; assert version.parse(ray.__version__).release == version.parse(\"$RAY_VERSION\").release, f\"ray {version.parse(ray.__version__).release} != version.parse(\'$RAY_VERSION\').release\""
          else
            python -c "import importlib.util; assert importlib.util.find_spec('ray') is None, \"found ray but expected it to not be installed\""
          fi
        shell: bash

      - name: Install Neuropod backend
        run: |
          sudo mkdir -p "$NEUROPOD_BASE_DIR"
          curl -L https://github.com/uber/neuropod/releases/download/v${{ env.NEUROPOD_VERISON }}/libneuropod-cpu-linux-v${{ env.NEUROPOD_VERISON }}-torchscript-${{ env.TORCHSCRIPT_VERSION }}-backend.tar.gz | sudo tar -xz -C "$NEUROPOD_BASE_DIR"
        shell: bash

      - name: Unit Tests
        run: |
          RUN_PRIVATE=$IS_NOT_FORK LUDWIG_TEST_SUITE_TIMEOUT_S=5400 pytest -v --timeout 300 --durations 100 -m "$MARKERS and not slow and not combinatorial and not horovod and not llm" --junitxml pytest.xml tests/ludwig

      - name: Regression Tests
        run: |
          RUN_PRIVATE=$IS_NOT_FORK LUDWIG_TEST_SUITE_TIMEOUT_S=5400 pytest -v --timeout 300 --durations 100 -m "$MARKERS and not slow and not combinatorial and not horovod or benchmark and not llm" --junitxml pytest.xml tests/regression_tests

      # Skip Horovod and replace with DDP.
      # https://github.com/ludwig-ai/ludwig/issues/3468
      # - name: Install Horovod if necessary
      #   if: matrix.test-markers == 'distributed' && matrix.pytorch-version != 'nightly'
      #   env:
      #     HOROVOD_WITH_PYTORCH: 1
      #     HOROVOD_WITHOUT_MPI: 1
      #     HOROVOD_WITHOUT_TENSORFLOW: 1
      #     HOROVOD_WITHOUT_MXNET: 1
      #   run: |
      #     pip install -r requirements_extra.txt
      #     HOROVOD_BUILT=$(python -c "import horovod.torch; horovod.torch.nccl_built(); print('SUCCESS')" || true)
      #     if [[ $HOROVOD_BUILT != "SUCCESS" ]]; then
      #       pip uninstall -y horovod
      #       pip install --no-cache-dir git+https://github.com/horovod/horovod.git@master
      #     fi
      #     horovodrun --check-build
      #   shell: bash

      # Skip Horovod tests and replace with DDP.
      # https://github.com/ludwig-ai/ludwig/issues/3468
      # - name: Horovod Tests
      #   if: matrix.test-markers == 'distributed' && matrix.pytorch-version != 'nightly'
      #   run: |
      #     RUN_PRIVATE=$IS_NOT_FORK LUDWIG_TEST_SUITE_TIMEOUT_S=5400 pytest -v --timeout 300 --durations 100 -m "$MARKERS and horovod and not slow and not combinatorial and not llm" --junitxml pytest.xml tests/

      - name: Upload Unit Test Results
        if: ${{ always() && !env.ACT }}
        uses: actions/upload-artifact@v2
        with:
          name: Unit Test Results (Python ${{ matrix.python-version }} ${{ matrix.test-markers }})
          path: pytest.xml

  integration-tests:
    name: ${{ matrix.test-markers }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        test-markers:
          - "integration_tests_a"
          - "integration_tests_b"
          - "integration_tests_c"
          - "integration_tests_d"
          - "integration_tests_e"
          - "integration_tests_f"

    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.LUDWIG_TESTS_AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.LUDWIG_TESTS_AWS_SECRET_ACCESS_KEY }}
      KAGGLE_USERNAME: ${{ secrets.KAGGLE_USERNAME }}
      KAGGLE_KEY: ${{ secrets.KAGGLE_KEY }}
      IS_NOT_FORK: ${{ !(github.event.pull_request.base.repo.full_name == 'ludwig-ai/ludwig' && github.event.pull_request.head.repo.fork) }}
      MARKERS: ${{ matrix.test-markers }}

    services:
      minio:
        image: fclairamb/minio-github-actions
        env:
          MINIO_ACCESS_KEY: minio
          MINIO_SECRET_KEY: minio123
        ports:
          - 9000:9000

    timeout-minutes: 90
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.10
        uses: actions/setup-python@v2
        with:
          python-version: "3.10"

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip

          # remove torch and ray from the dependencies so we can add them depending on the matrix args for the job.
          cat requirements.txt | sed '/^torch[>=<\b]/d' | sed '/^torchtext/d' | sed '/^torchvision/d' | sed '/^torchaudio/d' > requirements-temp && mv requirements-temp requirements.txt
          cat requirements_distributed.txt | sed '/^ray[\[]/d'
          pip install torch==2.0.0 torchtext torchvision torchaudio
          pip install ray==2.3.0
          pip install '.[test]'
          pip list
        shell: bash

      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          android: true
          dotnet: true
          haskell: true
          large-packages: false
          docker-images: true
          swap-storage: true

      - name: Clean out /tmp directory
        run: |
          sudo rm -rf /tmp/*

      - name: Integration Tests
        run: |
          RUN_PRIVATE=$IS_NOT_FORK LUDWIG_TEST_SUITE_TIMEOUT_S=7200 pytest -v --timeout 300 --durations 100 -m "not slow and not combinatorial and not horovod and not llm and $MARKERS" --junitxml pytest.xml tests/integration_tests

  llm-tests:
    name: LLM Tests
    runs-on: ubuntu-latest

    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.9
        uses: actions/setup-python@v2
        with:
          python-version: 3.9

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip

          # remove torch and ray from the dependencies so we can add them depending on the matrix args for the job.
          cat requirements.txt | sed '/^torch[>=<\b]/d' | sed '/^torchtext/d' | sed '/^torchvision/d' | sed '/^torchaudio/d' > requirements-temp && mv requirements-temp requirements.txt
          cat requirements_distributed.txt | sed '/^ray[\[]/d'
          pip install torch==2.0.0 torchtext torchvision torchaudio
          pip install ray==2.3.0
          pip install '.[test]'
          pip list
        shell: bash

      - name: LLM Tests
        run: |
          pytest -vs --durations 100 -m "llm" --junitxml pytest.xml tests

  combinatorial-tests:
    name: Combinatorial Tests
    runs-on: ubuntu-latest

    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip
          pip install '.[test]'
          pip list
        shell: bash

      - name: Testing combinatorial config generation code
        run: |
          pytest -vs --durations 100 -m "combinatorial" --junitxml pytest.xml tests/ludwig/config_sampling

      - name: Combinatorial Tests
        run: |
          pytest -rx --durations 100 -m "combinatorial" --junitxml pytest.xml tests/training_success

  test-minimal-install:
    name: Test Minimal Install
    runs-on: ubuntu-latest

    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip
          pip install torch==2.0.0 torchtext
          pip install ray==2.3.0
          pip install '.'
          pip list
        shell: bash
      - name: Check Install
        run: |
          ludwig check_install
        shell: bash

      - name: Test Getting Started
        run: |
          cd examples/getting_started && sh ./run.sh
        shell: bash

  # start-runner:
  #   name: Start self-hosted EC2 runner
  #   if: >
  #     always() && needs.pytest.result != 'failure' && (
  #     github.event_name == 'schedule' && github.repository == 'ludwig-ai/ludwig' ||
  #     github.event_name == 'push' && github.repository == 'ludwig-ai/ludwig' ||
  #     github.event_name == 'pull_request' && github.event.pull_request.base.repo.full_name == 'ludwig-ai/ludwig' && !github.event.pull_request.head.repo.fork)
  #   needs: pytest
  #   runs-on: ubuntu-latest
  #   outputs:
  #     label: ${{ steps.start-ec2-runner.outputs.label }}
  #     ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}

  #   steps:
  #     - name: Configure AWS credentials
  #       uses: aws-actions/configure-aws-credentials@v1
  #       with:
  #         aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
  #         aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  #         aws-region: ${{ secrets.AWS_REGION }}

  #     - name: Start EC2 runner
  #       id: start-ec2-runner
  #       uses: machulav/ec2-github-runner@v2.3.2
  #       with:
  #         mode: start
  #         github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
  #         ec2-image-id: ami-0759580dedc953d1f
  #         ec2-instance-type: g4dn.xlarge
  #         subnet-id: subnet-0983be43
  #         security-group-id: sg-4cba0d08
  #         aws-resource-tags: >
  #           [
  #             {"Key": "Name", "Value": "ludwig-github-${{ github.head_ref || github.sha }}"},
  #             {"Key": "GitHubRepository", "Value": "${{ github.repository }}"},
  #             {"Key": "GitHubHeadRef", "Value": "${{ github.head_ref }}"},
  #             {"Key": "GitHubSHA", "Value": "${{ github.sha }}"}
  #           ]

  # pytest-gpu:
  #   if: needs.start-runner.result != 'skipped'
  #   needs: start-runner # required to start the main job when the runner is ready
  #   runs-on: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runners
  #   strategy:
  #     fail-fast: false
  #     matrix:
  #       python-version: [3.7]
  #       include:
  #         - python-version: 3.7
  #           pytorch-version: 1.10.0
  #           torchscript-version: 1.10.2
  #   env:
  #     PYTORCH: ${{ matrix.pytorch-version }}
  #     NEUROPOD_BASE_DIR: "/usr/local/lib/neuropod"
  #     NEUROPOD_VERISON: "0.3.0-rc6"
  #     TORCHSCRIPT_VERSION: ${{ matrix.torchscript-version }}

  #   name: py${{ matrix.python-version  }}, torch-${{ matrix.pytorch-version }}, gpu

  #   timeout-minutes: 70
  #   steps:
  #     - uses: actions/checkout@v2
  #     - name: Set up Python ${{ matrix.python-version }}
  #       uses: actions/setup-python@v2
  #       with:
  #         python-version: ${{ matrix.python-version }}

  #     - name: Setup Linux
  #       if: runner.os == 'linux'
  #       run: |
  #         sudo apt-get update && sudo apt-get install -y libsndfile1 cmake ccache build-essential g++-8 gcc-8
  #         cmake --version

  #     - name: Install CUDA drivers
  #       run: |
  #         wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
  #         sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
  #         wget https://developer.download.nvidia.com/compute/cuda/11.5.1/local_installers/cuda-repo-ubuntu2004-11-5-local_11.5.1-495.29.05-1_amd64.deb
  #         sudo dpkg -i cuda-repo-ubuntu2004-11-5-local_11.5.1-495.29.05-1_amd64.deb
  #         sudo apt-key add /var/cuda-repo-ubuntu2004-11-5-local/7fa2af80.pub
  #         sudo apt-get update
  #         sudo apt-get -y install cuda
  #       shell: bash

  #     - name: pip cache
  #       uses: actions/cache@v2
  #       with:
  #         path: ~/.cache/pip
  #         key: ${{ runner.os }}-pip-py${{ matrix.python-version }}-torch${{ matrix.pytorch-version }}-${{ hashFiles('requirements*.txt') }}
  #         restore-keys: |
  #           ${{ runner.os }}-pip-py${{ matrix.python-version }}-torch${{ matrix.pytorch-version }}-

  #     - name: Install dependencies
  #       env:
  #         HOROVOD_WITH_PYTORCH: 1
  #         HOROVOD_WITHOUT_MPI: 1
  #         HOROVOD_WITHOUT_TENSORFLOW: 1
  #         HOROVOD_WITHOUT_MXNET: 1
  #       run: |
  #         python --version
  #         pip --version
  #         python -m pip install -U pip
  #         if [ $PYTORCH == "nightly" ]; then
  #           cat requirements.txt | sed '/^torch[>=<]/d' > requirements-temp && mv requirements-temp requirements.txt
  #           pip install --pre torch torchvision -f https://download.pytorch.org/whl/torch_stable.html
  #         else
  #           pip install torch==${PYTORCH}+cu111 -f https://download.pytorch.org/whl/torch_stable.html
  #         fi
  #         # pip install --no-cache-dir git+https://github.com/horovod/horovod.git@master
  #         pip install dulwich==0.20.26 # workaround for `/usr/bin/ld: cannot find -lpython3.7m`
  #         pip install '.[test]'
  #         pip list
  #       shell: bash

  #     - name: Install Neuropod backend
  #       run: |
  #         sudo mkdir -p "$NEUROPOD_BASE_DIR"
  #         curl -L https://github.com/uber/neuropod/releases/download/v${{ env.NEUROPOD_VERISON }}/libneuropod-cpu-linux-v${{ env.NEUROPOD_VERISON }}-torchscript-${{ env.TORCHSCRIPT_VERSION }}-backend.tar.gz | sudo tar -xz -C "$NEUROPOD_BASE_DIR"
  #       shell: bash

  #     - name: Reinstall Horovod if necessary
  #       env:
  #         HOROVOD_WITH_PYTORCH: 1
  #         HOROVOD_WITHOUT_MPI: 1
  #         HOROVOD_WITHOUT_TENSORFLOW: 1
  #         HOROVOD_WITHOUT_MXNET: 1
  #       run: |
  #         HOROVOD_BUILT=$(python -c "import horovod.torch; horovod.torch.nccl_built(); print('SUCCESS')" || true)
  #         if [[ $HOROVOD_BUILT != "SUCCESS" ]]; then
  #           pip uninstall -y horovod
  #           pip install --no-cache-dir git+https://github.com/horovod/horovod.git@master
  #         fi
  #         horovodrun --check-build
  #       shell: bash

  #     - name: Check CUDA is available
  #       run: |
  #         python -c "import torch; assert torch.cuda.is_available()"

  #     - name: Tests
  #       run: |
  #         pytest -v --timeout 300 --durations 10 --junitxml pytest.xml tests

  #     - name: Upload Unit Test Results
  #       if: always()
  #       uses: actions/upload-artifact@v2
  #       with:
  #         name: Unit Test Results (Python ${{ matrix.python-version }} gpu
  #         path: pytest.xml

  event_file:
    name: "Event File"
    runs-on: ubuntu-latest

    steps:
      - name: Upload
        if: ${{ !env.ACT }}
        uses: actions/upload-artifact@v2
        with:
          name: Event File
          path: ${{ github.event_path }}

  # stop-runner:
  #   name: Stop self-hosted EC2 runner

  #   # required to stop the runner even if the error happened in the previous job
  #   if: always() && needs.start-runner.result != 'skipped'
  #   needs:
  #     - start-runner # required to get output from the start-runner job
  #     - pytest-gpu # required to wait when the main job is done
  #   runs-on: ubuntu-latest

  #   steps:
  #     - name: Configure AWS credentials
  #       uses: aws-actions/configure-aws-credentials@v1
  #       with:
  #         aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
  #         aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  #         aws-region: ${{ secrets.AWS_REGION }}

  #     - name: Stop EC2 runner
  #       uses: machulav/ec2-github-runner@v2.3.1
  #       with:
  #         mode: stop
  #         github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
  #         label: ${{ needs.start-runner.outputs.label }}
  #         ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}


pytest (slow)

# This workflow will install Python dependencies and run all tests marked as `slow` on a single Python version.
# The tests will run on a high-memory AWS compute instance to accommodate memory-intensive workloads.

name: pytest (slow)

on:
  push:
    branches: ["master", "release-*"]

jobs:
  slow-pytest:
    name: py-slow${{ matrix.python-version  }}
    runs-on: ubuntu-latest
    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.LUDWIG_TESTS_AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.LUDWIG_TESTS_AWS_SECRET_ACCESS_KEY }}
      KAGGLE_USERNAME: ${{ secrets.KAGGLE_USERNAME }}
      KAGGLE_KEY: ${{ secrets.KAGGLE_KEY }}
      IS_NOT_FORK: ${{ !(github.event.pull_request.base.repo.full_name == 'ludwig-ai/ludwig' && github.event.pull_request.head.repo.fork) }}

    services:
      minio:
        image: fclairamb/minio-github-actions
        env:
          MINIO_ACCESS_KEY: minio
          MINIO_SECRET_KEY: minio123
        ports:
          - 9000:9000

    timeout-minutes: 150
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.9
        uses: actions/setup-python@v2
        with:
          python-version: 3.9

      - name: Setup Linux
        if: runner.os == 'linux'
        run: |
          sudo apt-get update && sudo apt-get install -y cmake libsndfile1 libsox-dev

      - name: Setup macOS
        if: runner.os == 'macOS'
        run: |
          brew install libuv

      - name: Install dependencies
        run: |
          python --version
          pip --version
          python -m pip install -U pip
          pip install torch==2.1.0 torchtext torchvision torchaudio
          pip install ray==2.3.1
          pip install '.[test]'

          pip list

        shell: bash

      - name: Tests
        run: |
          RUN_PRIVATE=$IS_NOT_FORK LUDWIG_TEST_SUITE_TIMEOUT_S=7200 pytest -v --timeout 600 --durations 100 -m "slow" --junitxml pytest.xml tests/integration_tests/


test results

name: test results

on:
  workflow_run:
    workflows: ["pytest"]
    types:
      - completed

jobs:
  test-results:
    name: Test Results
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion != 'skipped'

    steps:
      - name: Download and Extract Artifacts
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
        run: |
           mkdir -p artifacts && cd artifacts

           artifacts_url=${{ github.event.workflow_run.artifacts_url }}

           gh api "$artifacts_url" -q '.artifacts[] | [.name, .archive_download_url] | @tsv' | while read artifact
           do
             IFS=$'\t' read name url <<< "$artifact"
             gh api $url > "$name.zip"
             unzip -d "$name" "$name.zip"
           done

      - name: Publish Unit Test Results
        uses: EnricoMi/publish-unit-test-result-action@v1
        with:
          commit: ${{ github.event.workflow_run.head_sha }}
          event_file: artifacts/Event File/event.json
          event_name: ${{ github.event.workflow_run.event }}
          files: "artifacts/**/*.xml"


Upload to PyPI

name: Upload to PyPI

on:
  # Triggers the workflow when a release or draft of a release is published,
  # or a pre-release is changed to a release
  release:
    types: [released]
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  pypi-publish:
    name: upload release to PyPI
    runs-on: ubuntu-latest
    # Specifying a GitHub environment is optional, but strongly encouraged
    environment: release
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write
    steps:
      # retrieve your distributions here
      - name: Checkout
        uses: actions/checkout@v2
        with:
          submodules: "recursive"

      - uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - name: Build and upload to PyPI
        run: |
          python setup.py sdist

      - name: Publish package distributions to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1


test

name: test

on:
  push: # any branch
  pull_request:
    branches: [master]

env:
  FORCE_COLOR: 1

jobs:
  test-ubuntu:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4
      - name: Setup Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true
      - name: Install Dependencies
        run: |
          sudo apt remove python3-pip
          python -m pip install --upgrade pip
          python -m pip install . ruff typos coverage codecov mypy pytest readme_renderer types-contextvars asyncssh
          pip list
      - name: Ruff
        run: |
          ruff .
          ruff format --check .
          typos .
      - name: Tests
        run: |
          coverage run -m pytest
      - if: "matrix.python-version != '3.7'"
        name: Mypy
        # Check whether the imports were sorted correctly.
        # When this fails, please run ./tools/sort-imports.sh
        run: |
          mypy --strict src/prompt_toolkit --platform win32
          mypy --strict src/prompt_toolkit --platform linux
          mypy --strict src/prompt_toolkit --platform darwin
      - name: Validate README.md
        # Ensure that the README renders correctly (required for uploading to PyPI).
        run: |
          python -m readme_renderer README.rst > /dev/null
      - name: Run codecov
        run: |
          codecov


No Response

name: No Response

# Both `issue_comment` and `scheduled` event types are required for this Action
# to work properly.
on:
  issue_comment:
    types: [created]
  schedule:
    # Schedule for an arbitrary time (5am) once every day
    - cron: '* 5 * * *'

jobs:
  noResponse:
    # Don't run if in a fork
    if: github.repository_owner == 'kivy'
    runs-on: ubuntu-latest
    steps:
      - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb
        # This commit hash targets release v0.5.0 of lee-dohm/no-response.
        # Targeting a commit hash instead of a tag has been done for security reasons.
        # Please be aware that the commit hash specifically targets the "Automatic compilation"
        # done by `github-actions[bot]` as the `no-response` Action needs to be compiled.
        with:
          token: ${{ github.token }}
          daysUntilClose: 42
          responseRequiredLabel: 'awaiting-reply'
          closeComment: >
                This issue has been automatically closed because there has been no response
                to our request for more information from the original author. With only the
                information that is currently in the issue, we don't have the means
                to take action. Please reach out if you have or find the answers we need so
                that we can investigate further.


Unit tests & build apps

name: Unit tests & build apps

on: ['push', 'pull_request']

env:
  APK_ARTIFACT_FILENAME: bdist_unit_tests_app-debug-1.1.apk
  AAB_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1.aab
  AAR_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1.aar
  PYTHONFORANDROID_PREREQUISITES_INSTALL_INTERACTIVE: 0

jobs:

  flake8:
    name: Flake8 tests
    runs-on: ubuntu-latest
    steps:
    - name: Checkout python-for-android
      uses: actions/checkout@v4
    - name: Set up Python 3.x
      uses: actions/setup-python@v4
      with:
        python-version: 3.x
    - name: Run flake8
      run: |
        python -m pip install --upgrade pip
        pip install tox>=2.0
        tox -e pep8

  test:
    name: Pytest [Python ${{ matrix.python-version }} | ${{ matrix.os }}]
    needs: flake8
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        python-version: ['3.8', '3.9', '3.10', '3.11']
        os: [ubuntu-latest, macOs-latest]
    steps:
    - name: Checkout python-for-android
      uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Tox tests
      run: |
        python -m pip install --upgrade pip
        pip install tox>=2.0
        make test
    - name: Coveralls
      uses: AndreMiras/coveralls-python-action@develop
      if: ${{ matrix.os == 'ubuntu-latest' }}
      with:
        parallel: true
        flag-name: run-${{ matrix.os }}-${{ matrix.python-version }}

  ubuntu_build:
    name: Build test APP [ ${{ matrix.runs_on }} | ${{ matrix.bootstrap.name }} ]
    needs: [flake8]
    runs-on: ${{ matrix.runs_on }}
    continue-on-error: true
    strategy:
      matrix:
        runs_on: [ubuntu-latest]
        bootstrap:
          - name: sdl2
            target: testapps-with-numpy
          - name: sdl2_scipy
            target: testapps-with-scipy
          - name: webview
            target: testapps-webview
          - name: service_library
            target: testapps-service_library-aar
          - name: qt
            target: testapps-qt
    steps:
    - name: Checkout python-for-android
      uses: actions/checkout@v4
    - name: Build python-for-android docker image
      run: |
        docker build --tag=kivy/python-for-android .
    - name: Build multi-arch ${{ matrix.bootstrap.target }} artifact with docker
      run: |
        docker run --name p4a-latest kivy/python-for-android make ${{ matrix.bootstrap.target }}
    - name: Copy produced artifacts from docker container (*.apk, *.aab)
      if: matrix.bootstrap.name != 'service_library'
      run: |
        mkdir -p dist
        docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/${{ env.APK_ARTIFACT_FILENAME }} dist/
        docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/${{ env.AAB_ARTIFACT_FILENAME }} dist/
    - name: Copy produced artifacts from docker container (*.aar)
      if: matrix.bootstrap.name == 'service_library'
      run: |
        mkdir -p dist
        docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/${{ env.AAR_ARTIFACT_FILENAME }} dist/
    - name: Rename artifacts to include the build platform name (*.apk, *.aab, *.aar)
      run: |
        if [ -f dist/${{ env.APK_ARTIFACT_FILENAME }} ]; then mv dist/${{ env.APK_ARTIFACT_FILENAME }} dist/${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-${{ env.APK_ARTIFACT_FILENAME }}; fi
        if [ -f dist/${{ env.AAB_ARTIFACT_FILENAME }} ]; then mv dist/${{ env.AAB_ARTIFACT_FILENAME }} dist/${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-${{ env.AAB_ARTIFACT_FILENAME }}; fi
        if [ -f dist/${{ env.AAR_ARTIFACT_FILENAME }} ]; then mv dist/${{ env.AAR_ARTIFACT_FILENAME }} dist/${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-${{ env.AAR_ARTIFACT_FILENAME }}; fi
    - name: Upload artifacts
      uses: actions/upload-artifact@v3
      with:
        name: ${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-artifacts
        path: dist

  macos_build:
    name: Build test APP [ ${{ matrix.runs_on }} | ${{ matrix.bootstrap.name }} ]
    needs: [flake8]
    runs-on: ${{ matrix.runs_on }}
    continue-on-error: true
    strategy:
      matrix:
        # macos-latest (ATM macos-14) runs on Apple Silicon,
        # macos-13 runs on Intel
        runs_on: ['macos-latest', 'macos-13']
        bootstrap:
          - name: sdl2
            target: testapps-with-numpy
          - name: webview
            target: testapps-webview
    env:
      ANDROID_HOME: ${HOME}/.android
      ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
      ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
      ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
    steps:
      - name: Checkout python-for-android
        uses: actions/checkout@v4
      - name: Set up Python 3.x
        uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - name: Install python-for-android
        run: |
          python3 -m pip install -e .
      - name: Install prerequisites via pythonforandroid/prerequisites.py (Experimental)
        run: |
          python3 pythonforandroid/prerequisites.py
      - name: Install dependencies (Legacy)
        run: |
          make --file ci/makefiles/osx.mk
      - name: Build multi-arch apk Python 3 (armeabi-v7a, arm64-v8a, x86_64, x86)
        run: |
          make ${{ matrix.bootstrap.target }}
      - name: Copy produced artifacts into dist/ (*.apk, *.aab)
        run: |
          mkdir -p dist
          cp testapps/on_device_unit_tests/*.apk dist/
          cp testapps/on_device_unit_tests/*.aab dist/
          ls -l dist/
      - name: Rename artifacts to include the build platform name (*.apk, *.aab)
        run: |
          if [ -f dist/${{ env.APK_ARTIFACT_FILENAME }} ]; then mv dist/${{ env.APK_ARTIFACT_FILENAME }} dist/${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-${{ env.APK_ARTIFACT_FILENAME }}; fi
          if [ -f dist/${{ env.AAB_ARTIFACT_FILENAME }} ]; then mv dist/${{ env.AAB_ARTIFACT_FILENAME }} dist/${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-${{ env.AAB_ARTIFACT_FILENAME }}; fi
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.runs_on }}-${{ matrix.bootstrap.name }}-artifacts
          path: dist

  ubuntu_rebuild_updated_recipes:
    name: Test updated recipes for arch ${{ matrix.android_arch }} [ ubuntu-latest ]
    needs: [flake8]
    runs-on: ubuntu-latest
    continue-on-error: true
    strategy:
      matrix:
        android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
    env:
      REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }}
    steps:
    - name: Checkout python-for-android (all-history)
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    # helps with GitHub runner getting out of space
    - name: Free disk space
      run: |
        df -h
        sudo swapoff -a
        sudo rm -f /swapfile
        sudo apt -y clean
        docker rmi $(docker image ls -aq)
        df -h
    - name: Pull docker image
      run: |
        make docker/pull
    - name: Rebuild updated recipes
      run: |
        make docker/run/make/rebuild_updated_recipes

  macos_rebuild_updated_recipes:
    name: Test updated recipes for arch ${{ matrix.android_arch }} [ ${{ matrix.runs_on }} ]
    needs: [flake8]
    runs-on: ${{ matrix.runs_on }}
    continue-on-error: true
    strategy:
      matrix:
        android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
        # macos-latest (ATM macos-14) runs on Apple Silicon,
        # macos-13 runs on Intel
        runs_on: ['macos-latest', 'macos-13']
    env:
      ANDROID_HOME: ${HOME}/.android
      ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk
      ANDROID_SDK_HOME: ${HOME}/.android/android-sdk
      ANDROID_NDK_HOME: ${HOME}/.android/android-ndk
      REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }}
    steps:
      - name: Checkout python-for-android (all-history)
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Python 3.x
        uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - name: Install python-for-android
        run: |
          python3 -m pip install -e .
      - name: Install prerequisites via pythonforandroid/prerequisites.py (Experimental)
        run: |
          python3 pythonforandroid/prerequisites.py
      - name: Install dependencies (Legacy)
        run: |
          make --file ci/makefiles/osx.mk
      - name: Rebuild updated recipes
        run: |
          make rebuild_updated_recipes

  coveralls_finish:
    needs: test
    runs-on: ubuntu-latest
    steps:
    - name: Coveralls Finished
      uses: AndreMiras/coveralls-python-action@develop
      with:
        parallel-finished: true

  documentation:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Requirements
      run: |
        python -m pip install --upgrade pip
        pip install -r doc/requirements.txt
    - name: Check links
      run: sphinx-build -b linkcheck doc/source doc/build
    - name: Generate documentation
      run: sphinx-build doc/source doc/build


PyPI release

name: PyPI release
on: [push]

jobs:
  pypi_release:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python 3.x
      uses: actions/setup-python@v4
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade setuptools wheel twine
    - name: Build
      run: |
        python setup.py sdist bdist_wheel
        twine check dist/*
    - name: Publish package
      if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
      uses: pypa/gh-action-pypi-publish@v1.4.2
      with:
        user: __token__
        password: ${{ secrets.pypi_password }}


Support Requests

# When a user creates an issue that is actually a support request, it should
# be closed with a friendly comment.
#
# This triggers on an issue being labelled with the `support` tag.

name: 'Support Requests'

on:
  issues:
    types: [labeled, unlabeled, reopened]

permissions:
  issues: write

jobs:
  action:
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/support-requests@v4
        with:
          github-token: ${{ github.token }}
          support-label: 'support'
          issue-comment: >
            👋 @{issue-author},

            Sorry to hear you are having difficulties with Kivy's python-for-android; Kivy unites a number of different technologies, so building apps can be temperamental.

            We try to use GitHub issues only to track work for developers to do to fix bugs and add new features to python-for-android.

            However, this issue appears to be a support request. Please use our
            [support channels](https://github.com/kivy/python-for-android/blob/master/CONTACT.md)
            to get help with the project.

            If you're having trouble installing python-for-android,
            please see our [quickstart](https://python-for-android.readthedocs.io/en/latest/quickstart) guide.

            If you're having trouble using python-for-android,
            please see our [troubleshooting guide](https://python-for-android.readthedocs.io/en/latest/troubleshooting)
            and [FAQ](https://github.com/kivy/python-for-android/blob/master/FAQ.md).

            Let us know if this comment was made in error, and we'll be happy
            to reopen the issue.

          close-issue: true
          lock-issue: false


Build

name: Build
on: [push, pull_request]
env:
  GCS_BUCKET: autobuilds.grr-response.com
  GCS_BUCKET_OPENAPI: autobuilds-grr-openapi
  DOCKER_REPOSITORY: ghcr.io/google/grr
jobs:
  test-devenv:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - name: check_deps
        run: |
          sudo apt-get update && sudo apt-get install -y podman curl jq
          sudo usermod --add-subuids 500000-565535 --add-subgids 500000-565535 "${USER}"
          devenv/devenv.sh check_deps
      - name: start
        run: |
          devenv/devenv.sh start
          OK=false
          for attempt in $(seq 20); do
            curl -su admin:admin http://localhost:4280/api/clients |
              sed 1d |
              jq -r ".items[].value.client_id.value" |
              egrep -e "^C[.][0-9a-f]{16}\$" && { OK=true; break; }
            echo "attempt ${attempt}: devenv not ready"
            sleep 10
          done
          [[ $OK = true ]]

  test-ubuntu:
    runs-on: ubuntu-22.04
    env:
      CHROME_DEB: google-chrome-stable_current_amd64.deb
    steps:
      - uses: actions/checkout@v4
      - name: Install
        run: |
          free -hmw
          lscpu
          sudo apt install -y python3-dev python3-pip python3-venv python3-mysqldb
          if [[ -z "$(type google-chrome 2>/dev/null)" ]]; then
            wget "https://dl.google.com/linux/direct/${CHROME_DEB}" && sudo apt install -y "./${CHROME_DEB}";
          fi
          python3 -m venv --system-site-packages "${HOME}/INSTALL"
          travis/install.sh
      - name: Test
        run: |
          source "${HOME}/INSTALL/bin/activate"
          pip install pytest-xdist==2.2.1 pytest==6.2.5
          # We have 4 vCPUs available, but only use 3 here to avoid timeouts like
          # https://ci.appveyor.com/project/grr/grr-ia94e/builds/20483467/messages ,
          # which happen when tests stall.
          pytest --verbose -n 3 grr/ --ignore grr/server/grr_response_server/gui/selenium_tests/ --ignore grr/client/grr_response_client/client_actions/windows/
          # jsTree tests seem to fail on Chrome 71 headless due to https://github.com/GoogleChrome/puppeteer/issues/3463
          if [ $(google-chrome --version | grep -Eo " [0-9]{1,3}") != "71" ]; then (cd grr/server/grr_response_server/gui/static/ && npm run gulp test); fi

  build-openapi:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - name: Install
        run: |
          sudo apt install -y python3-dev python3-pip python3-venv python3-mysqldb
          python3 -m venv --system-site-packages "${HOME}/INSTALL"
          travis/install.sh
      - name: Build
        run: |
          source "${HOME}/INSTALL/bin/activate"
          mkdir -p _openapi_artifacts/openapi_description
          mkdir -p _openapi_artifacts/documentation
          travis/build_api_documentation.sh "_openapi_artifacts/openapi_description/openapi_description.json" "_openapi_artifacts/documentation/openapi_documentation.html"
          ls -la _openapi_artifacts/*
      - name: Upload OpenAPI to GitHub artifacts
        uses: actions/upload-artifact@v4
        with:
          name: openapi
          path: _openapi_artifacts/
          retention-days: 1

  build-ubuntu:
    runs-on: ubuntu-22.04
    env:
      GCS_TAG: ubuntu_64bit
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.9'
      - name: Set up
        run: |
          sudo apt install fakeroot debhelper libffi-dev libssl-dev
          pip install virtualenv
          virtualenv "${HOME}/INSTALL"
      - name: Build
        run: |
          travis/install_client_builder.sh
          travis/build_templates.sh
          ls -la gcs_upload_dir
      - name: Upload installers to GitHub artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ubuntu-installers
          path: gcs_upload_dir/
          retention-days: 1

  build-osx:
    runs-on: macos-13
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.9'
      - name: Set up
        run: |
          pip install --upgrade setuptools virtualenv
          virtualenv "${HOME}/INSTALL"
      - name: Build installers
        run: |
          travis/install_client_builder.sh
          travis/build_templates.sh
          ls -la gcs_upload_dir
      - name: Upload installers to GitHub artifacts
        uses: actions/upload-artifact@v4
        with:
          name: osx-installers
          path: gcs_upload_dir/
          retention-days: 1

  build-centos:
    runs-on: ubuntu-22.04
    env:
      GCS_TAG: centos_64bit
      DOCKER_IMG: grrdocker/centos7-python39
      DOCKER_CONTAINER: centos_64bit_container
      DOCKER_USER: grrbot
    steps:
      - uses: actions/checkout@v4
      - name: Build installers
        run: |
          docker run -dit \
            --volume "${PWD}:/mnt/grr" \
            --workdir /mnt/grr \
            --env DOCKER_USER="${DOCKER_USER}" \
            --env TRAVIS_OS_NAME="linux" \
            --name "${DOCKER_CONTAINER}" \
            "${DOCKER_IMG}"
          # Using `bash -l` here and below to make sure devtools
          # (including the C++ 14 compatible compiler) are properly
          # registered in the environment variables.
          docker exec "${DOCKER_CONTAINER}" bash -l travis/set_up_test_user.sh
          docker exec --user "${DOCKER_USER}" "${DOCKER_CONTAINER}" bash -l -c '/usr/local/bin/python3.9 -m venv "/home/${DOCKER_USER}/INSTALL"'
          docker exec --user "${DOCKER_USER}" "${DOCKER_CONTAINER}" bash -l travis/install_client_builder.sh
          docker exec --user "${DOCKER_USER}" "${DOCKER_CONTAINER}" bash -l travis/build_templates.sh
          docker exec "${DOCKER_CONTAINER}" rpm -vih gcs_upload_dir/*.rpm
          ls -la gcs_upload_dir
      - name: Upload installers to GitHub artifacts
        uses: actions/upload-artifact@v4
        with:
          name: centos-installers
          path: gcs_upload_dir/
          retention-days: 1

  build-windows:
    runs-on: windows-2022
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.9'
      - name: Build installers
        shell: bash
        run: |
          set -ex
          pip install virtualenv wheel
          python -u appveyor/windows_templates/build_windows_templates.py --grr_src=$GITHUB_WORKSPACE --output_dir=$GITHUB_WORKSPACE/output --test_repack_install
          mkdir -p gcs_upload_dir
          mv -v output*/* gcs_upload_dir
          ls -la gcs_upload_dir
      - name: Upload installers to GitHub artifacts
        uses: actions/upload-artifact@v4
        with:
          name: windows-installers
          path: gcs_upload_dir/
          retention-days: 1

  build-docker-image:
    runs-on: ubuntu-22.04
    permissions:
      contents: 'read'
      id-token: 'write'
    needs:
      - build-centos
      - build-ubuntu
      - build-osx
      - build-windows
    steps:
      - uses: actions/checkout@v4
      - name: Download installers from GitHub artifacts
        id: download
        uses: actions/download-artifact@v4
        with:
          pattern: '*installer*'
          path: _installers
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.DOCKER_REPOSITORY }}
      - name: Build and export
        uses: docker/build-push-action@v5
        with:
          context: .
          file: Dockerfile
          # Temporarily add a `testing`-tag to identify this image
          # for testing, tag is removed again before uploading to
          # github container registry.
          tags: |
            ${{ env.DOCKER_REPOSITORY }}:testing
            ${{ steps.meta.outputs.tags }}
          outputs: type=docker,dest=/tmp/grr_base_image.tar
      - name: Upload docker image
        uses: actions/upload-artifact@v4
        with:
          name: grr_base_image
          path: /tmp/grr_base_image.tar
          retention-days: 3

  docker-compose-e2e-test:
    permissions:
      contents: 'read'
      id-token: 'write'
    runs-on: ubuntu-22.04
    needs:
      - build-docker-image
    steps:
      - uses: actions/checkout@v4
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: grr_base_image
          path: /tmp
      - name: Load image
        run: |
          docker load --input /tmp/grr_base_image.tar
      - name: Start docker compose stack
        shell: bash
        run: |
          docker_config_files/init_certs.sh
          docker compose \
            -f compose.yaml \
            -f compose.testing.yaml \
            up -d --wait
      - name: Test
        shell: bash
        run: |
          docker compose exec grr-client bash /configs/client/create_fake_user.sh
          docker run \
            --add-host=host.docker.internal:host-gateway \
            -v $(pwd):/github_workspace \
            -v ./docker_config_files:/configs \
            -w /github_workspace \
            --entrypoint appveyor/e2e_tests/run_docker_compose_e2e_test.sh \
            ${{ env.DOCKER_REPOSITORY }}:testing \
            $(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' grr-client)
      - name: Dump docker compose log output
        if: always()
        shell: bash
        run: |
          docker compose logs > /tmp/docker_compose_test.log
      - name: Upload docker compose logs
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: docker_commpose_test_logs
          path: /tmp/docker_compose_test.log
          retention-days: 3
      - name: Stop the docker compose stack
        if: always()
        shell: bash
        run: |
          docker compose down --volumes

  push-docker-image:
    if: ${{ github.event_name == 'push' }}
    env:
      REGISTRY: ghcr.io
    permissions: write-all
    runs-on: ubuntu-22.04
    needs:
      - test-ubuntu
      - docker-compose-e2e-test
    steps:
      - uses: actions/checkout@v4
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: grr_base_image
          path: /tmp
      - name: Load image
        run: |
          docker load --input /tmp/grr_base_image.tar
      - name: Login to GitHub Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Push Docker image
        run: |
          # Remove the tag used to identify the image for testing again.
          docker rmi ${{ env.DOCKER_REPOSITORY }}:testing
          docker push --all-tags ${{ env.DOCKER_REPOSITORY }}

  upload-artifacts:
    if: ${{ github.event_name == 'push' }}
    permissions:
      contents: 'read'
      id-token: 'write'
    runs-on: ubuntu-22.04
    needs:
      - docker-compose-e2e-test
      - test-ubuntu
      - build-centos
      - build-ubuntu
      - build-osx
      - build-windows
      - build-openapi
    steps:
      - uses: actions/checkout@v4
      - name: Download installers from GitHub artifacts
        id: download
        uses: actions/download-artifact@v4
        with:
          path: _artifacts
      - run: |
          ls -la _artifacts/*/
          COMMIT_TIME=$(git show -s --date='format-local:%Y-%m-%dT%H:%M:%SZ' --format="%cd" $GITHUB_SHA)
          OUTPUT_DIR=gcs_upload_dir/${COMMIT_TIME}_${GITHUB_SHA}/
          echo "OUTPUT_DIR=$OUTPUT_DIR" >> $GITHUB_ENV
          mkdir -p $OUTPUT_DIR/centos/
          mv -v _artifacts/centos-installers/* $OUTPUT_DIR/centos
          mkdir -p $OUTPUT_DIR/ubuntu/
          mv -v _artifacts/ubuntu-installers/* $OUTPUT_DIR/ubuntu
          mkdir -p $OUTPUT_DIR/osx/
          mv -v _artifacts/osx-installers/* $OUTPUT_DIR/osx
          mkdir -p $OUTPUT_DIR/windows/
          mv -v _artifacts/windows-installers/* $OUTPUT_DIR/windows
      - name: Authenticate
        uses: 'google-github-actions/auth@v1'
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}
          export_environment_variables: true
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v1.1.0
      - name: Upload installers to GCS
        uses: google-github-actions/upload-cloud-storage@v1.0.0
        with:
          path: gcs_upload_dir/
          destination: ${{ env.GCS_BUCKET }}
          # Omit `path` (e.g. /home/runner/deploy/) in final GCS path.
          parent: false
      - name: Upload OpenAPI to GCS
        uses: google-github-actions/upload-cloud-storage@v1.0.0
        with:
          path: _artifacts/openapi/
          destination: ${{ env.GCS_BUCKET_OPENAPI }}
          # Omit `path` (e.g. /home/runner/deploy/) in final GCS path.
          parent: false


Publish to PyPi

name: Publish to PyPi
on:
  release:
    types: [published]

jobs:
  build-pypi-packages:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.9'
      - uses: actions/setup-node@v4
        with:
          node-version: 16.13.0
      - name: Build
        run: |
          set -ex
          mkdir /tmp/sdists
          python grr/proto/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python grr/core/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python grr/client/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python grr/client_builder/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python grr/server/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python grr/test/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python colab/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
          python api_client/python/setup.py --quiet sdist --formats=zip --dist-dir="/tmp/sdists"
      - name: Upload grr-response-proto
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-proto
          path: /tmp/sdists/grr-response-proto-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-response-core
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-core
          path: /tmp/sdists/grr-response-core-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-response-client
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-client
          path: /tmp/sdists/grr-response-client-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-response-client-builder
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-client-builder
          path: /tmp/sdists/grr-response-client-builder-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-response-server
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-server
          path: /tmp/sdists/grr-response-server-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-response-test
        uses: actions/upload-artifact@v4
        with:
          name: grr-response-test
          path: /tmp/sdists/grr-response-test-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-colab
        uses: actions/upload-artifact@v4
        with:
          name: grr-colab
          path: /tmp/sdists/grr-colab-[0-9]*.zip
          retention-days: 3
      - name: Upload grr-api-client
        uses: actions/upload-artifact@v4
        with:
          name: grr-api-client
          path: /tmp/sdists/grr-api-client-[0-9]*.zip
          retention-days: 3

  publish-to-pypi-grr-response-proto:
    name: Publish grr-response-proto to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-proto
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-proto
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-response-core:
    name: Publish grr-response-core to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-core
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-core
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-response-client:
    name: Publish grr-response-client to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-client
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-client
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-response-client-builder:
    name: Publish grr-response-client-builder to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-client-builder
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-client-builder
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-response-server:
    name: Publish grr-response-server to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-server
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-server
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-response-test:
    name: Publish grr-response-test to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-response-test
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-response-test
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-colab:
    name: Publish grr-colab to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-colab
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-colab
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-pypi-grr-api-client:
    name: Publish grr-api-client to PyPI
    needs:
    - build-pypi-packages
    runs-on: ubuntu-22.04
    environment:
      name: pypi
      url: https://pypi.org/p/grr-api-client
    permissions:
      id-token: write  # IMPORTANT: mandatory for trusted publishing
    steps:
    - name: Download the artifact
      uses: actions/download-artifact@v4
      with:
        name: grr-api-client
        path: dist/
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1


Deploy Wiki

name: Deploy Wiki

on:
  push:
    paths:
      - 'kubernetes/docs/**'
    branches:
      - master

jobs:
  deploy-wiki:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Install rsync
      run: |
        sudo apt install rsync grsync
    - name: Clone Wiki
      run: |
        git config --global --add safe.directory "/github/workspace"
        git config --global --add safe.directory "/github/workspace/wiki"
        git clone https://github.com/kubernetes-client/python.wiki.git wiki
        message=$(git log -1 --format=%B)
    - name: Copy to wiki repository
      run: |
        rsync -av --delete kubernetes/docs/ wiki/ --exclude .git
    - name: Push wiki
      run: |
        cd wiki
        git config user.name github-actions
        git config user.email github-actions@github.com
        git add .
        git commit -m "$message"
        git push


End to End Tests - master

name: End to End Tests - master

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.7, 3.8, 3.9]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-master-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.17.0
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.25.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


End to End Tests - release-11.0

name: End to End Tests - release-11.0

on:
  push:
    branches:
      - release-11.0
  pull_request:
    branches:
      - release-11.0

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-release-11.0-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.11.1
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.15.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


End to End Tests - release-12.0

name: End to End Tests - release-12.0

on:
  push:
    branches:
      - release-12.0
  pull_request:
    branches:
      - release-12.0

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-release-12.0-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.11.1
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.16.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


End to End Tests - release-17.0

name: End to End Tests - release-17.0

on:
  push:
    branches:
      - release-17.0
  pull_request:
    branches:
      - release-17.0

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-release-17.0-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.11.1
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.17.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


End to End Tests - release-18.0

name: End to End Tests - release-18.0

on:
  push:
    branches:
      - release-18.0
  pull_request:
    branches:
      - release-18.0

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.6, 3.7, 3.8, 3.9]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-release-18.0-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.11.1
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.18.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


End to End Tests - release-26.0

name: End to End Tests - release-26.0

on:
  push:
    branches:
      - release-26.0
  pull_request:
    branches:
      - release-26.0

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.7, 3.8, 3.9]
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - name: Create Kind Cluster
      uses: helm/kind-action@v1.10.0
      with:
        cluster_name: kubernetes-python-e2e-release-26.0-${{ matrix.python-version }}
        # The kind version to be used to spin the cluster up
        # this needs to be updated whenever a new Kind version is released
        version: v0.17.0
        # Update the config here whenever a new client snapshot is performed
        # This would eventually point to cluster with the latest Kubernetes version
        # as we sync with Kubernetes upstream
        config: .github/workflows/kind-configs/cluster-1.26.yaml
    - 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
        python -m pip install -r requirements.txt
        python -m pip install -r test-requirements.txt
    - name: Install package
      run: python -m pip install -e .
    - name: Run End to End tests
      run: pytest -vvv -s kubernetes/e2e_test


Kubernetes Python Client - Validation

name: Kubernetes Python Client - Validation

on: [ push, pull_request ]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.7", "3.8", "3.10", "3.11"]
        include:
          - python-version: "3.9"
            use_coverage: 'coverage'

    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
    - 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 -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Install Tox and any other packages
      run: pip install tox

    - name: Test without coverage
      if: "! matrix.use_coverage"
      run: tox -e py  # Run tox using the version of Python in `PATH`

    - name: Test with coverage
      if: "matrix.use_coverage"
      run: tox -e py-coverage

    - name: Upload coverage to Codecov
      if: "matrix.use_coverage"
      uses: codecov/codecov-action@v4
      with:
        fail_ci_if_error: false
        verbose: true


Build docset

---
name: Build docset

on:
  push:
    tags: ["*"]
  workflow_dispatch:

env:
  PIP_DISABLE_PIP_VERSION_CHECK: "1"
  PIP_NO_PYTHON_VERSION_WARNING: "1"

permissions: {}

jobs:
  docset:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        with:
          python-version: "3.x"

      - run: python -Im pip install tox

      - run: python -Im tox run -e docset

      - uses: actions/upload-artifact@v4
        with:
          name: docset
          path: attrs.tgz


CI

---
name: CI

on:
  merge_group:
  push:
    branches: [main]
    tags: ["*"]
  pull_request:
  workflow_dispatch:

env:
  FORCE_COLOR: "1" # Make tools pretty.
  PIP_DISABLE_PIP_VERSION_CHECK: "1"
  PIP_NO_PYTHON_VERSION_WARNING: "1"
  # Use oldest version used in doctests / examples.
  SETUPTOOLS_SCM_PRETEND_VERSION: "19.2.0"

permissions: {}

jobs:
  tests:
    name: Tests & Mypy on ${{ matrix.python-version }}
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.7"
          - "3.8"
          - "3.9"
          - "3.10"
          - "3.11"
          - "3.12"
          - "3.13"
          - "pypy-3.8"
          - "pypy-3.9"
          - "pypy-3.10"

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true
          cache: pip

      - name: Prepare tox
        run: |
          V=${{ matrix.python-version }}

          if [[ "$V" = pypy-* ]]; then
            V=pypy3
            IS_PYPY=1
          else
            V=py$(echo $V | tr -d .)
            IS_PYPY=0
          fi

          echo IS_PYPY=$IS_PYPY >>$GITHUB_ENV
          echo TOX_PYTHON=$V >>$GITHUB_ENV

          python -Im pip install tox

      - run: python -Im pip install tox-uv
        if: matrix.python-version != '3.7'

      - run: python -Im tox run -e ${{ env.TOX_PYTHON }}-tests
      - run: python -Im tox run -e ${{ env.TOX_PYTHON }}-mypy
        if: env.IS_PYPY == '0' && matrix.python-version != '3.7'

      - name: Upload coverage data
        uses: actions/upload-artifact@v4
        with:
          name: coverage-data-${{ matrix.python-version }}
          path: .coverage.*
          if-no-files-found: ignore

  coverage:
    name: Combine & check coverage.
    runs-on: ubuntu-latest
    needs: tests

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version-file: .python-version-default
          cache: pip

      - name: Download coverage data
        uses: actions/download-artifact@v4
        with:
          pattern: coverage-data-*
          merge-multiple: true

      - name: Combine coverage & fail if it's <100%.
        run: |
          python -Im pip install coverage[toml]

          python -Im coverage combine
          python -Im coverage html --skip-covered --skip-empty

          # Report and write to summary.
          python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY

          # Report again and fail if under 100%.
          python -Im coverage report --fail-under=100

      - name: Upload HTML report if check failed.
        uses: actions/upload-artifact@v4
        with:
          name: html-report
          path: htmlcov
        if: ${{ failure() }}

  docs:
    name: Build docs & run doctests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          # Keep in sync with tox/docs and .readthedocs.yaml.
          python-version: "3.12"
          cache: pip

      - run: python -Im pip install tox-uv
      - run: python -Im tox run -e docs,changelog

  pyright:
    name: Check types using pyright
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version-file: .python-version-default
          cache: pip

      - run: python -Im pip install tox-uv
      - run: python -Im tox run -e pyright

  install-dev:
    name: Verify dev env
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version-file: .python-version-default
          cache: pip

      - name: Install in dev mode & import
        run: |
          python -Im pip install -e .[dev]
          python -Ic 'import attr; print(attr.__version__)'
          python -Ic 'import attrs; print(attrs.__version__)'

  # Ensure everything required is passing for branch protection.
  required-checks-pass:
    if: always()

    needs:
      - coverage
      - docs
      - install-dev
      - pyright

    runs-on: ubuntu-latest

    steps:
      - name: Decide whether the needed jobs succeeded or failed
        uses: re-actors/alls-green@release/v1
        with:
          jobs: ${{ toJSON(needs) }}


CodeQL

---
name: CodeQL

on:
  schedule:
    - cron: "30 22 * * 4"

permissions:
  contents: read

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

    strategy:
      fail-fast: false
      matrix:
        language: [python]

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

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}

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


Build & maybe upload PyPI package

---
name: Build & maybe upload PyPI package

on:
  push:
    branches: [main]
    tags: ["*"]
  pull_request:
    branches: [main]
  release:
    types:
      - published
  workflow_dispatch:

permissions:
  contents: read
  id-token: write

jobs:
  # Always build & lint package.
  build-package:
    name: Build & verify package
    runs-on: ubuntu-latest

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

      - uses: hynek/build-and-inspect-python-package@v2

  # Upload to Test PyPI on every commit on main.
  release-test-pypi:
    name: Publish in-dev package to test.pypi.org
    environment: release-test-pypi
    if: github.repository_owner == 'python-attrs' && github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    needs: build-package

    steps:
      - name: Download packages built by build-and-inspect-python-package
        uses: actions/download-artifact@v4
        with:
          name: Packages
          path: dist

      - name: Upload package to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/

  # Upload to real PyPI on GitHub Releases.
  release-pypi:
    name: Publish released package to pypi.org
    environment: release-pypi
    if: github.repository_owner == 'python-attrs' && github.event.action == 'published'
    runs-on: ubuntu-latest
    needs: build-package

    steps:
      - name: Download packages built by build-and-inspect-python-package
        uses: actions/download-artifact@v4
        with:
          name: Packages
          path: dist

      - name: Upload package to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1


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: [ "master" ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ "master" ]
  schedule:
    - cron: '20 4 * * 4'

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

    strategy:
      fail-fast: false
      matrix:
        language: [ 'python' ]
        # 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


Publish

name: Publish

on:
  release:
    types:
      - created

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: 3.10.x
      - name: Upgrade pip
        run: pip install --upgrade --user pip
      - name: Install Poetry
        run: pip install poetry
      - name: Cache virtual environment
        uses: actions/cache@v4
        with:
          path: .venv
          key: ${{ runner.os }}-python-${{ matrix.python-version }}-venv-${{ hashFiles('**/poetry.lock') }}
      - name: Install dependencies
        run: poetry install --no-interaction
      - name: Build and validate
        run: |
          poetry run task minify
          poetry build
          poetry run pip install --quiet --disable-pip-version-check "twine>=1.12"
          poetry run twine check --strict dist/*
      - name: Publish
        env:
          # https://python-poetry.org/docs/repositories/#configuring-credentials
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.POETRY_PYPI_TOKEN_PYPI }}
        run: |
          poetry publish


Test

name: Test

# https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows
on:
  push:
    branches: [master]
  pull_request:
    types: [opened, synchronize]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        python-version: ['3.10', '3.11', '3.12']
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Upgrade pip
        run: pip install --upgrade --user pip
      - name: Install Poetry
        run: pip install poetry
      - name: Cache virtual environment
        uses: actions/cache@v4
        with:
          path: .venv
          key: ${{ runner.os }}-python-${{ matrix.python-version }}-venv-${{ hashFiles('**/poetry.lock') }}
      - name: Install dependencies
        run: poetry install --no-interaction
      - name: mypy, pytest, codecov
        run: make test
      - name: Check code style
        run: make lint
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        if: "github.repository == 'lk-geimfari/mimesis' && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'"
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./coverage.xml
          env_vars: OS,PYTHON
          fail_ci_if_error: true

  quick-test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ['pypy3.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: Upgrade pip
        run: pip install --upgrade --user pip
      - name: Install Poetry
        run: pip install poetry
      - name: Install dependencies
        run: poetry install --no-interaction
      - name: pytest and codecov
        run: poetry run pytest


Publish packages to PyPI

name: Publish packages to PyPI

on:
  push:
    tags:
      - "[0-9]+.[0-9]+.[0-9]+"
      - "[0-9]+.[0-9]+.[0-9]+.post[0-9]+"
      - "[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+"
      - "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"

jobs:
  build:
    name: Build the source tarball and the wheel
    runs-on: ubuntu-latest
    environment: release
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: 3.x
    - name: Install dependencies
      run: pip install build
    - name: Create packages
      run: python -m build
    - name: Archive packages
      uses: actions/upload-artifact@v4
      with:
        name: dist
        path: dist

  publish:
    name: Publish build artifacts to the PyPI
    needs: build
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write
    steps:
    - name: Retrieve packages
      uses: actions/download-artifact@v4
    - name: Upload packages
      uses: pypa/gh-action-pypi-publish@release/v1

  release:
    name: Create a GitHub release
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
    - uses: actions/checkout@v4
    - id: changelog
      uses: agronholm/release-notes@v1
      with:
        path: docs/versionhistory.rst
    - uses: ncipollo/release-action@v1
      with:
        body: ${{ steps.changelog.outputs.changelog }}