Build Publish and Test Docker Image

name: Build Publish and Test Docker Image

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

on:
  push:
    branches:
      - main
    tags:
      - '*'
  pull_request:
  workflow_dispatch:
    inputs:
      reason:
        description: 'Reason for manual trigger'
        required: true
        default: ''

jobs:
  ghcr_build:
    runs-on: ubuntu-latest

    outputs:
      tags: ${{ steps.capture-tags.outputs.tags }}

    permissions:
      contents: read
      packages: write

    strategy:
      matrix:
        image: ["sandbox", "opendevin"]
        platform: ["amd64", "arm64"]

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

      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          # this might remove tools that are actually needed,
          # if set to "true" but frees about 6 GB
          tool-cache: true
          # all of these default to true, but feel free to set to
          # "false" if necessary for your workflow
          android: true
          dotnet: true
          haskell: true
          large-packages: true
          docker-images: false
          swap-storage: true

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

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

      - name: Build and export image
        id: build
        run: ./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }} ${{ matrix.platform }}

      - name: Capture tags
        id: capture-tags
        run: |
          tags=$(cat tags.txt)
          echo "tags=$tags"
          echo "tags=$tags" >> $GITHUB_OUTPUT

      - name: Upload Docker image as artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.image }}-docker-image-${{ matrix.platform }}
          path: /tmp/${{ matrix.image }}_image_${{ matrix.platform }}.tar

  test-for-sandbox:
    name: Test for Sandbox
    runs-on: ubuntu-latest
    needs: ghcr_build
    env:
      PERSIST_SANDBOX: "false"
    steps:
      - uses: actions/checkout@v4

      - name: Install poetry via pipx
        run: pipx install poetry

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

      - name: Install Python dependencies using Poetry
        run: make install-python-dependencies

      - name: Download sandbox Docker image
        uses: actions/download-artifact@v4
        with:
          name: sandbox-docker-image-amd64
          path: /tmp/

      - name: Load sandbox image and run sandbox tests
        run: |
          # Load the Docker image and capture the output
          output=$(docker load -i /tmp/sandbox_image_amd64.tar)

          # Extract the image name from the output
          image_name=$(echo "$output" | grep -oP 'Loaded image: \K.*')

          # Print the full name of the image
          echo "Loaded Docker image: $image_name"

          SANDBOX_CONTAINER_IMAGE=$image_name TEST_IN_CI=true poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml -s ./tests/unit/test_sandbox.py

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  integration-tests-on-linux:
    name: Integration Tests on Linux
    runs-on: ubuntu-latest
    needs: ghcr_build
    env:
      PERSIST_SANDBOX: "false"
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        sandbox: ["ssh", "exec", "local"]
    steps:
      - uses: actions/checkout@v4

      - name: Install poetry via pipx
        run: pipx install poetry

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'poetry'

      - name: Install Python dependencies using Poetry
        run: make install-python-dependencies

      - name: Download sandbox Docker image
        uses: actions/download-artifact@v4
        with:
          name: sandbox-docker-image-amd64
          path: /tmp/

      - name: Load sandbox image and run integration tests
        env:
          SANDBOX_TYPE: ${{ matrix.sandbox }}
        run: |
          # Load the Docker image and capture the output
          output=$(docker load -i /tmp/sandbox_image_amd64.tar)

          # Extract the image name from the output
          image_name=$(echo "$output" | grep -oP 'Loaded image: \K.*')

          # Print the full name of the image
          echo "Loaded Docker image: $image_name"

          SANDBOX_CONTAINER_IMAGE=$image_name TEST_IN_CI=true TEST_ONLY=true ./tests/integration/regenerate.sh

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  ghcr_push:
    runs-on: ubuntu-latest
    # don't push if integration tests or sandbox tests fail
    needs: [ghcr_build, integration-tests-on-linux, test-for-sandbox]
    if: github.ref == 'refs/heads/main'

    env:
      tags: ${{ needs.ghcr_build.outputs.tags }}

    permissions:
      contents: read
      packages: write

    strategy:
      matrix:
        image: ["sandbox", "opendevin"]
        platform: ["amd64", "arm64"]

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

      - name: Login to GHCR
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Download Docker images
        uses: actions/download-artifact@v4
        with:
          name: ${{ matrix.image }}-docker-image-${{ matrix.platform }}
          path: /tmp/${{ matrix.platform }}

      - name: Load images and push to registry
        run: |
          mv /tmp/${{ matrix.platform }}/${{ matrix.image }}_image_${{ matrix.platform }}.tar .
          loaded_image=$(docker load -i ${{ matrix.image }}_image_${{ matrix.platform }}.tar | grep "Loaded image:" | awk '{print $3}')
          tags=$(echo ${tags} | tr ' ' '\n')
          image_name=$(echo "ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}" | tr '[:upper:]' '[:lower:]')
          echo "image name = $image_name"
          for tag in $tags; do
            echo "tag = $tag"
            docker tag $loaded_image $image_name:${tag}_${{ matrix.platform }}
            docker push $image_name:${tag}_${{ matrix.platform }}
          done

  create_manifest:
    runs-on: ubuntu-latest
    needs: [ghcr_build, ghcr_push]
    if: github.ref == 'refs/heads/main'

    env:
      tags: ${{ needs.ghcr_build.outputs.tags }}

    strategy:
      matrix:
        image: ["sandbox", "opendevin"]

    permissions:
      contents: read
      packages: write

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

      - name: Login to GHCR
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create and push multi-platform manifest
        run: |
          image_name=$(echo "ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}" | tr '[:upper:]' '[:lower:]')
          echo "image name = $image_name"
          tags=$(echo ${tags} | tr ' ' '\n')
          for tag in $tags; do
            echo 'tag = $tag'
            docker buildx imagetools create --tag $image_name:$tag \
              $image_name:${tag}_amd64 \
              $image_name:${tag}_arm64
          done

  # FIXME: an admin needs to mark this as non-mandatory, and then we can remove it
  docker_build_success:
    name: Docker Build Success
    runs-on: ubuntu-latest
    needs: ghcr_build
    steps:
    - run: echo Done!


Lint

name: Lint

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

on:
  push:
    branches:
    - main
  pull_request:

jobs:
  lint-frontend:
    name: Lint frontend
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Node.js 20
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: |
          cd frontend
          npm install --frozen-lockfile

      - name: Lint
        run: |
          cd frontend
          npm run lint

  lint-python:
    name: Lint python
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up python
        uses: actions/setup-python@v5
        with:
          python-version: 3.11
          cache: 'pip'
      - name: Install pre-commit
        run: pip install pre-commit==3.7.0
      - name: Run pre-commit hooks
        run: pre-commit run --files opendevin/**/* agenthub/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml


Use OpenDevin to Review Pull Request

name: Use OpenDevin to Review Pull Request

on:
  pull_request:
    types: [synchronize, labeled]

permissions:
  contents: write
  pull-requests: write

jobs:
  dogfood:
    if: contains(github.event.pull_request.labels.*.name, 'review-this')
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/opendevin/opendevin
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock

    steps:
    - name: install git, github cli
      run: |
        apt-get install -y git gh
        git config --global --add safe.directory $PWD

    - name: Checkout Repository
      uses: actions/checkout@v4
      with:
        ref: ${{ github.event.pull_request.base.ref }} # check out the target branch

    - name: Download Diff
      run: |
        curl -O "${{ github.event.pull_request.diff_url }}" -L

    - name: Write Task File
      run: |
        echo "Your coworker wants to apply a pull request to this project. Read and review ${{ github.event.pull_request.number }}.diff file. Create a review-${{ github.event.pull_request.number }}.txt and write your concise comments and suggestions there." > task.txt
        echo "" >> task.txt
        echo "Title" >> task.txt
        echo "${{ github.event.pull_request.title }}" >> task.txt
        echo "" >> task.txt
        echo "Description" >> task.txt
        echo "${{ github.event.pull_request.body }}" >> task.txt
        echo "" >> task.txt
        echo "Diff file is: ${{ github.event.pull_request.number }}.diff" >> task.txt

    - name: Set up environment
      run: |
        curl -sSL https://install.python-poetry.org | python3 -
        export PATH="/github/home/.local/bin:$PATH"
        poetry install --without evaluation
        poetry run playwright install --with-deps chromium

    - name: Run OpenDevin
      env:
        LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        SANDBOX_TYPE: exec
      run: |
        # Append path to launch poetry
        export PATH="/github/home/.local/bin:$PATH"
        # Append path to correctly import package, note: must set pwd at first
        export PYTHONPATH=$(pwd):$PYTHONPATH
        WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE poetry run python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
        rm task.txt

    - name: Check if review file is non-empty
      id: check_file
      run: |
        ls -la
        if [[ -s review-${{ github.event.pull_request.number }}.txt ]]; then
          echo "non_empty=true" >> $GITHUB_OUTPUT
        fi
      shell: bash

    - name: Create PR review if file is non-empty
      env:
        GH_TOKEN: ${{ github.token }}
      if: steps.check_file.outputs.non_empty == 'true'
      run: |
        gh pr review ${{ github.event.pull_request.number }} --comment --body-file "review-${{ github.event.pull_request.number }}.txt"


Run Unit Tests

name: Run Unit Tests

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

on:
  push:
    branches:
      - main
    paths-ignore:
      - '**/*.md'
      - 'frontend/**'
      - 'docs/**'
      - 'evaluation/**'
  pull_request:

env:
  PERSIST_SANDBOX : "false"

jobs:
  test-on-macos:
    name: Test on macOS
    runs-on: macos-12
    env:
      INSTALL_DOCKER: "1" # Set to '0' to skip Docker installation
    strategy:
      matrix:
        python-version: ["3.11"]

    steps:
      - uses: actions/checkout@v4

      - name: Install poetry via pipx
        run: pipx install poetry

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: "poetry"

      - name: Install Python dependencies using Poetry
        run: poetry install

      - name: Install & Start Docker
        if: env.INSTALL_DOCKER == '1'
        run: |
          # Uninstall colima to upgrade to the latest version
          if brew list colima &>/dev/null; then
              brew uninstall colima
              # unlinking colima dependency: go
              brew uninstall go@1.21
          fi
          brew install --HEAD colima
          brew services start colima
          brew install docker
          colima start  --network-address --arch x86_64

          # For testcontainers to find the Colima socket
          # https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
          sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock

      - name: Build Environment
        run: make build

      - name: Run Tests
        run: poetry run pytest --forked --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit -k "not test_sandbox"

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  test-on-linux:
    name: Test on Linux
    runs-on: ubuntu-latest
    env:
      INSTALL_DOCKER: "0" # Set to '0' to skip Docker installation
    strategy:
      matrix:
        python-version: ["3.11"]

    steps:
      - uses: actions/checkout@v4

      - name: Install poetry via pipx
        run: pipx install poetry

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: "poetry"

      - name: Install Python dependencies using Poetry
        run: poetry install --without evaluation

      - name: Build Environment
        run: make build

      - name: Run Tests
        run: poetry run pytest --forked --cov=agenthub --cov=opendevin --cov-report=xml ./tests/unit -k "not test_sandbox"

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}


Use OpenDevin to Resolve GitHub Issue

name: Use OpenDevin to Resolve GitHub Issue

on:
  issues:
    types: [labeled]

permissions:
  contents: write
  pull-requests: write
  issues: write

jobs:
  dogfood:
    if: github.event.label.name == 'solve-this'
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/opendevin/opendevin
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock

    steps:
    - name: install git, github cli
      run: apt-get install -y git gh

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

    - name: Write Task File
      env:
        ISSUE_TITLE: ${{ github.event.issue.title }}
        ISSUE_BODY: ${{ github.event.issue.body }}
      run: |
        echo "TITLE:" > task.txt
        echo "${ISSUE_TITLE}" >> task.txt
        echo "" >> task.txt
        echo "BODY:" >> task.txt
        echo "${ISSUE_BODY}" >> task.txt

    - name: Set up environment
      run: |
        curl -sSL https://install.python-poetry.org | python3 -
        export PATH="/github/home/.local/bin:$PATH"
        poetry install --without evaluation
        poetry run playwright install --with-deps chromium

    - name: Run OpenDevin
      env:
        ISSUE_TITLE: ${{ github.event.issue.title }}
        ISSUE_BODY: ${{ github.event.issue.body }}
        LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        SANDBOX_TYPE: exec
      run: |
        # Append path to launch poetry
        export PATH="/github/home/.local/bin:$PATH"
        # Append path to correctly import package, note: must set pwd at first
        export PYTHONPATH=$(pwd):$PYTHONPATH
        WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE poetry run python ./opendevin/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
        rm task.txt

    - name: Setup Git, Create Branch, and Commit Changes
      run: |
        # Setup Git configuration
        git config --global --add safe.directory $PWD
        git config --global user.name 'OpenDevin'
        git config --global user.email 'OpenDevin@users.noreply.github.com'

        # Create a unique branch name with a timestamp
        BRANCH_NAME="fix/${{ github.event.issue.number }}-$(date +%Y%m%d%H%M%S)"

        # Checkout new branch
        git checkout -b $BRANCH_NAME

        # Add all changes to staging, except task.txt
        git add --all -- ':!task.txt'

        # Commit the changes, if any
        git commit -m "OpenDevin: Resolve Issue #${{ github.event.issue.number }}"
        if [ $? -ne 0 ]; then
          echo "No changes to commit."
          exit 0
        fi

        # Push changes
        git push --set-upstream origin $BRANCH_NAME

    - name: Fetch Default Branch
      env:
        GH_TOKEN: ${{ github.token }}
      run: |
        # Fetch the default branch using gh cli
        DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
        echo "Default branch is $DEFAULT_BRANCH"
        echo "DEFAULT_BRANCH=$DEFAULT_BRANCH" >> $GITHUB_ENV

    - name: Generate PR
      env:
        GH_TOKEN: ${{ github.token }}
      run: |
        # Create PR and capture URL
        PR_URL=$(gh pr create \
          --title "OpenDevin: Resolve Issue #2" \
          --body "This PR was generated by OpenDevin to resolve issue #2" \
          --repo "foragerr/OpenDevin" \
          --head "${{ github.head_ref }}" \
          --base "${{ env.DEFAULT_BRANCH }}" \
          | grep -o 'https://github.com/[^ ]*')

        # Extract PR number from URL
        PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]\+$')

        # Set environment vars
        echo "PR_URL=$PR_URL" >> $GITHUB_ENV
        echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV

    - name: Post Comment
      env:
        GH_TOKEN: ${{ github.token }}
      run: |
        gh issue comment ${{ github.event.issue.number }} \
          -b "OpenDevin raised [PR #${{ env.PR_NUMBER }}](${{ env.PR_URL }}) to resolve this issue."


Close stale issues

name: 'Close stale issues'
on:
  schedule:
    - cron: '30 1 * * *'

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          # Aggressively close issues that have been explicitly labeled `age-out`
          any-of-labels: age-out
          stale-issue-message: 'This issue is stale because it has been open for 7 days with no activity. Remove stale label or comment or this will be closed in 1 day.'
          close-issue-message: 'This issue was closed because it has been stalled for over 7 days with no activity.'
          stale-pr-message: 'This PR is stale because it has been open for 7 days with no activity. Remove stale label or comment or this will be closed in 1 days.'
          close-pr-message: 'This PR was closed because it has been stalled for over 7 days with no activity.'
          days-before-stale: 7
          days-before-close: 1

      - uses: actions/stale@v9
        with:
          # Be more lenient with other issues
          stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
          close-issue-message: 'This issue was closed because it has been stalled for over 30 days with no activity.'
          stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
          close-pr-message: 'This PR was closed because it has been stalled for over 30 days with no activity.'
          days-before-stale: 30
          days-before-close: 7


CodeSee

# SPDX-License-Identifier: MIT

# This workflow was added by CodeSee. Learn more at https://codesee.io/
# This is v2.0 of this workflow file

on:
  push:
    branches:
      - main
  pull_request_target:
    types: [opened, synchronize, reopened]

name: CodeSee

permissions: read-all

jobs:
  codesee:
    runs-on: ubuntu-latest
    continue-on-error: true
    name: Analyze the repo with CodeSee
    steps:
      - uses: Codesee-io/codesee-action@v2
        with:
          codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}


Create, Publish & Deploy Docker Image

# SPDX-License-Identifier: MIT

name: Create, Publish & Deploy Docker Image

on:
  release:
    types: [published]

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

jobs:
  build:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      packages: write

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

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

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

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


Format

# SPDX-License-Identifier: MIT

name: Format

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

permissions:
  contents: write
  pull-requests: write

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

jobs:
  ruff:
    name: Ruff
    runs-on: ubuntu-latest

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

      - name: Check
        id: formatcheck
        uses: chartboost/ruff-action@v1
        continue-on-error: true
        with:
          args: format --check .

      - name: Format
        uses: chartboost/ruff-action@v1
        with:
          args: format .

      - name: Create pull request
        if: steps.formatcheck.outcome != 'success'
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          title: "🎨 Automated formatting errors fix"
          commit-message: "fixed formatting errors (ruff)"
          body: |
            Formatter errors found in: ${{ github.sha }}.
          base: ${{ github.head_ref }}
          branch: actions/ruff
          delete-branch: true
          reviewers: hitblast


Lint

# SPDX-License-Identifier: MIT

name: Lint

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

permissions:
  contents: write
  pull-requests: write

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

jobs:
  ruff:
    name: Ruff
    runs-on: ubuntu-latest

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

      - name: Check
        id: lintcheck
        uses: chartboost/ruff-action@v1
        continue-on-error: true
        with:
          args: check .

      - name: Lint
        uses: chartboost/ruff-action@v1
        with:
          args: check . --fix

      - name: Create pull request
        if: steps.lintcheck.outcome != 'success'
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          title: "🎨 Automated linting errors fix"
          commit-message: "fixed linting errors (ruff)"
          body: |
            Linter errors found in: ${{ github.sha }}.
          base: ${{ github.head_ref }}
          branch: actions/ruff
          reviewers: hitblast
          delete-branch: true


Publish collection

---
name: Publish collection

"on":
  push:
    tags:
      - 'v*'

jobs:
  publish-collection:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup python
        uses: actions/setup-python@v5
        with:
          python-version: 3.9
      - name: Install dependencies
        run: |
          python3 -m pip install ansible-core~=2.12.3
      - name: Get the version
        id: get_version
        run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
      - name: Publish collection
        run: ansible-playbook -i localhost, -e tag='${{ steps.get_version.outputs.VERSION }}' .github/publish-collection.yml
        env:
          ANSIBLE_GALAXY_TOKEN: ${{ secrets.ANSIBLE_GALAXY_TOKEN }}


Upload Python Package

# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
  release:
    types: [published]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4.1.1
      with:
        submodules: true
        token: ${{ secrets.OXOTITAN_GITHUB_KEY }}
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18.0.0'
    - name: Install Node dependencies
      run: npm install
      working-directory: src/oxotitan
    - name: Generate Nuxt
      run: npm run generate
      working-directory: src/oxotitan
    - name: Create ostorlab ui static folder
      run: mkdir -p ostorlab/ui/static
      working-directory: src
    - name: Copy Nuxt output
      run: cp -r src/oxotitan/.output/public src/ostorlab/ui/static
    - name: Set up Python
      uses: actions/setup-python@v5.0.0
      with:
        python-version: '3.x'
    - name: Install SSH key
      uses: shimataro/ssh-key-action@v2
      with:
        key: ${{ secrets.WORKFLOW_SSH_KEY }}
        known_hosts: ${{ secrets.KNOWN_HOSTS }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build
    - name: Build package
      run: python -m build
    - name: Publish package
      uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        user: __token__
        password: ${{ secrets.PYPI_API_TOKEN }}
    - name: Set up Docker Buildx
      id: buildx
      uses: docker/setup-buildx-action@v1
    - name: Login to ostorlab store
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.store_user }}
        password: ${{ secrets.store_password }}
    - name: truncate current tag
      run: echo "TAG=$(echo $GITHUB_REF | cut -c12-17)" >> $GITHUB_ENV
    - name: Build image and push to ostorlab Container Registry
      uses: docker/build-push-action@v2
      with:
        # relative path to the place where source code with Dockerfile is located
        context: .
        tags: |
          ostorlab/oxo:${{env.TAG}}
          ostorlab/oxo:latest
        push: true


Pytest-Windows

# This workflow use pytest:
#  - Install Python dependencies.
#  - Run pytest for each of the supported Python versions ["3.8", "3.9", "3.10"].
# Running pytest with -m "no docker" to disable test that require a docker installation.

name: Pytest-Windows

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ "*" ]

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

    runs-on:  ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9", "3.10", "3.11"]
        os: [windows-latest]

    steps:
    - uses: actions/checkout@v4.1.1
      with:
        submodules: true
        token: ${{ secrets.OXOTITAN_GITHUB_KEY }}
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5.0.0
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies.
      run: |
        python -m pip install --upgrade pip
        pip install .[testing]
        pip install .[scanner]
        pip install .[agent]
        pip install .[serve]
        python -m pip install -e .
    - name: Running tests with pytest.
      env:
        RE_OXO_API_KEY: ${{ secrets.RE_OXO_API_KEY }}
      run: |
        pytest -m "not docker and not nats"


Pytest-Linux

# This workflow use pytest:
#  - Install Python dependencies.
#  - Run pytest for each of the supported Python versions ["3.8", "3.9", "3.10"].
# Running pytest with -m "no docker" to disable test that require a docker installation.

name: Pytest-Linux

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ "*" ]

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

    runs-on:  ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9", "3.10", "3.11"]
        os: [ubuntu-latest]

    steps:
    - uses: actions/checkout@v4.1.1
      with:
        submodules: true
        token: ${{ secrets.OXOTITAN_GITHUB_KEY }}
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5.0.0
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies.
      run: |
        python -m pip install --upgrade pip
        pip install .[testing]
        pip install .[scanner]
        pip install .[agent]
        pip install .[serve]
        python -m pip install -e .
    - name: Running tests with pytest.
      env:
        RE_OXO_API_KEY: ${{ secrets.RE_OXO_API_KEY }}
      run: |
        set -o pipefail
        pytest -m "not docker and not nats"


Lint And Format Checker.

name: Lint And Format Checker.

on:
  pull_request:
    branches: [ "*" ]
  push:
    branches: [ "main" ]
jobs:
  ruff:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: [ "3.9", "3.10" ]
    steps:
      - uses: actions/checkout@v4.1.1
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5.0.0
        with:
          python-version: ${{ matrix.python-version }}
      - name: Checking code formatting.
        uses: chartboost/ruff-action@v1
        with:
          args: format --check
      - name: Running linter.
        uses: chartboost/ruff-action@v1


Static Types Checker.

name: Static Types Checker.

on:
  pull_request:
    branches: [ "*" ]
  push:
    branches: [ "main" ]
jobs:
  mypy:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: [ "3.9", "3.10" ]
    steps:
      - uses: actions/checkout@v4.1.1
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5.0.0
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'
          cache-dependency-path: |
            **/typing_requirements.txt
      - name: Install dependencies.
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r typing_requirements.txt
          python -m pip install -e .
      - name: Running static types checker.
        run: |
          mypy src/ostorlab/agent/schema
          mypy src/ostorlab/agent/kb
          mypy src/ostorlab/agent/message
          mypy src/ostorlab/utils
          mypy src/ostorlab/apis/runners
          mypy src/ostorlab/agent/mixins/agent_report_vulnerability_mixin.py
          mypy src/ostorlab/assets


on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: ['ubuntu-latest', 'macos-latest']

    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
        token: ${{ secrets.OXOTITAN_GITHUB_KEY }}
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18.0.0'
    - name: Install Node dependencies
      run: npm install
      working-directory: src/oxotitan
    - name: Generate Nuxt
      run: npm run generate
      working-directory: src/oxotitan
    - name: Create ostorlab ui static folder
      run: mkdir -p ostorlab/ui/static
      working-directory: src
    - name: Copy Nuxt output
      run: cp -r src/oxotitan/.output/public src/ostorlab/ui/static
    - 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 .
        pip install .[scanner]
        pip install .[agent]
        pip install .[serve]
        pip install pyinstaller

    - run: |
        pyinstaller --name oxo_${{ matrix.os }} --onefile --add-data="./*:." --hidden-import alembic --hidden-import docker --hidden-import click src/ostorlab/main.py
    - name: Release
      uses: softprops/action-gh-release@v2
      with:
        files: dist/oxo_${{ matrix.os }}


on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: ['windows-latest']

    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true
        token: ${{ secrets.OXOTITAN_GITHUB_KEY }}
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18.0.0'
    - name: Install Node dependencies
      run: npm install
      working-directory: src/oxotitan
    - name: Generate Nuxt
      run: npm run generate
      working-directory: src/oxotitan
    - name: Create ostorlab ui static folder
      run: mkdir -p ostorlab/ui/static
      working-directory: src
    - name: Copy Nuxt output
      run: cp -r src/oxotitan/.output/public src/ostorlab/ui/static
    - 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 .
        pip install .[scanner]
        pip install .[agent]
        pip install .[serve]
        pip install pyinstaller

    - run: |
        pyinstaller --name oxo_${{ matrix.os }} --onefile --add-data="./*:." --hidden-import alembic --hidden-import docker --hidden-import click src/ostorlab/main.py
    - name: Release
      uses: softprops/action-gh-release@v2
      with:
        files: dist/oxo_${{ matrix.os }}.exe


Build workflow

name: "Build workflow"
on: [pull_request]
jobs:
  build-test:
    runs-on: ubuntu-20.04
    container: python:3.9-slim
    steps:
      - name: Check out code
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Install pip dependencies
        run: pip install -r requirements.txt
      - name: Build
        run: mkdocs build


PR Generator

name: "PR Generator"
on:
  workflow_dispatch:
    inputs:
      go_algorand_version:
        description: "Go-Algorand Full Version (Example: v1.2.3-stable)"
        type: string
        required: true
      indexer_version:
        description: "Indexer Version (Example: X.X.X)"
        type: string
        required: true
  workflow_call:
    inputs:
      go_algorand_version:
        description: "Go-Algorand Full Version (Example: v1.2.3-stable)"
        type: string
        required: true
      indexer_version:
        description: "Indexer Version (Example: X.X.X)"
        type: string
        required: true

jobs:
  pr-generator:
    runs-on: ubuntu-latest
    steps:
      - name: Clone docs repo
        uses: actions/checkout@v3
        with:
          path: docs

      - name: Clone go-algorand repo
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        uses: actions/checkout@v3
        with:
          repository: algorand/go-algorand
          ref: ${{ inputs.go_algorand_version }}
          path: go-algorand

      - name: Get short/clean go-algorand version
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        run: |
          TEMP_VERSION=${{ inputs.go_algorand_version }}
          TEMP_VERSION=$(echo $TEMP_VERSION | sed 's/v//' | sed 's/-stable//')
          echo "GO_ALGORAND_VERSION set to $TEMP_VERSION"
          echo "GO_ALGORAND_VERSION=$TEMP_VERSION" >> $GITHUB_ENV

      - name: Clone Indexer repo
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        uses: actions/checkout@v3
        with:
          repository: algorand/indexer
          ref: v${{ inputs.indexer_version }}
          path: indexer
          submodules: true

      - name: Install golang
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        uses: actions/setup-go@v3
        with:
          go-version: 1.20.7

      - name: Generate conduit docs
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        run: |
          sudo apt update
          sudo apt -y install python3 python3-pip python3-setuptools python3-wheel libboost-math-dev libffi-dev
          cd ./indexer
          make
          rm -r ../get-details/conduit/* && cp ./conduit-docs/* ../get-details/conduit/
          cd ../

      - name: Download binaries
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        run: |
          wget https://github.com/algorand/go-algorand/releases/download/v${{ env.GO_ALGORAND_VERSION }}-stable/node_stable_linux-amd64_${{ env.GO_ALGORAND_VERSION }}.tar.gz
          wget https://github.com/algorand/indexer/releases/download/v${{ inputs.indexer_version }}/indexer_Linux_x86_64_${{ inputs.indexer_version }}.tar.gz
          ls -la

      - name: Install binaries
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        run: |
          mkdir -p ~/go/bin
          mkdir -p node_stable_linux-amd64_${{ env.GO_ALGORAND_VERSION }}
          tar xzf node_stable_linux-amd64_${{ env.GO_ALGORAND_VERSION }}.tar.gz -C node_stable_linux-amd64_${{ env.GO_ALGORAND_VERSION }}/
          cd node_stable_linux-amd64_${{ env.GO_ALGORAND_VERSION }}/bin/
          cp goal algokey kmd diagcfg tealdbg  ~/go/bin/
          cd ../../
          mkdir -p indexer_Linux_x86_64_${{ inputs.indexer_version }}
          tar xzf indexer_Linux_x86_64_${{ inputs.indexer_version }}.tar.gz -C indexer_Linux_x86_64_${{ inputs.indexer_version }}
          cd indexer_Linux_x86_64_${{ inputs.indexer_version }}
          cp algorand-indexer ~/go/bin/
          cd ../
          ls -la
          ls -la ~/go/bin

      - name: Run doc generator
        if: (contains(inputs.go_algorand_version, '-stable')) && (!contains(inputs.indexer_version, '-'))
        run: |
          cd docs/scripts/
          ./reformat-all-commands.sh ../../go-algorand/ ../../indexer/
          go run config_json_gen/main.go -path ../../go-algorand/ > ../docs/run-a-node/reference/config.md
          git diff

      - name: Change version for Beta release
        if: contains(inputs.go_algorand_version, '-beta')
        run: |
          cd docs/docs/get-details/algorand-networks
          sed -i.bak 's/v.*-beta/${{ inputs.go_algorand_version }}/' betanet.md
          rm betanet.md.bak
          cd ../../../
          echo "${{ inputs.go_algorand_version }}" > .go-algorand-beta.version

      - name: Change go-algorand version for Stable release
        if: contains(inputs.go_algorand_version, '-stable')
        run: |
          cd docs/docs/get-details/algorand-networks
          sed -i.bak 's/v.*-stable/${{ inputs.go_algorand_version }}/' testnet.md
          sed -i.bak 's/v.*-stable/${{ inputs.go_algorand_version }}/' mainnet.md
          rm testnet.md.bak mainnet.md.bak
          cd ../../../
          echo "${{ inputs.go_algorand_version }}" > .go-algorand-stable.version

      - name: Change Indexer version
        if: inputs.indexer_version != '' && !contains(inputs.indexer_version, '-')
        run: |
          echo "${{ inputs.indexer_version }}" > docs/.indexer.version

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v4
        with:
          path: docs
          branch: "automatic-pr-go-algorand-${{ inputs.go_algorand_version }}-indexer-${{ inputs.indexer_version }}"
          title: "Automatic update generated for go-algorand: ${{ inputs.go_algorand_version }} and indexer: ${{ inputs.indexer_version }}"
          body: "Changes generated automatically by github action docs-generator."
          reviewers: "nullun"
          delete-branch: true
          base: staging


Check for releases

name: "Check for releases"
on:
  schedule:
    - cron: '*/30 * * * *'

jobs:
  release-checker:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout docs repo
        uses: actions/checkout@v3
        with:
          path: docs

      - name: Clone go-algorand repo
        uses: actions/checkout@v3
        with:
          repository: algorand/go-algorand
          ref: rel/stable
          path: go-algorand

      - name: Clone Indexer repo
        uses: actions/checkout@v3
        with:
          repository: algorand/indexer
          ref: main
          path: indexer

      - name: Get current documentation versions
        id: current_versions
        run: |
          cd docs
          TEMP_STABLE=$(cat .go-algorand-stable.version)
          echo "Stable version: $TEMP_STABLE"
          echo "::set-output name=stable::$TEMP_STABLE"
          TEMP_BETA=$(cat .go-algorand-beta.version)
          echo "Beta version: $TEMP_BETA"
          echo "::set-output name=beta::$TEMP_BETA"
          TEMP_INDEXER=$(cat .indexer.version)
          echo "Indexer version: $TEMP_INDEXER"
          echo "::set-output name=indexer::$TEMP_INDEXER"

      - name: Get latest stable and beta go-algorand releases
        id: latest_go_algorand
        run: |
          cd go-algorand
          git fetch --tags
          echo "::set-output name=stable::$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*-stable' | sort -V | tail -1)"
          git tag --list 'v[0-9]*.[0-9]*.[0-9]*-stable' | sort -V | tail -1
          echo "::set-output name=beta::$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*-beta' | sort -V | tail -1)"
          git tag --list 'v[0-9]*.[0-9]*.[0-9]*-beta' | sort -V | tail -1

      - name: Get latest indexer release without dashes
        id: latest_indexer
        run: |
          cd indexer
          git fetch --tags
          echo "::set-output name=indexer::$(git tag --list | grep '[0-9]*\.[0-9]*\.[0-9]*' | grep -v '-' | sed 's/^v//' | sort -V | tail -1)"
          git tag --list | grep '[0-9]*\.[0-9]*\.[0-9]*' | grep -v '-' | sed 's/^v//' | sort -V | tail -1

      - name: Check versions # Just for easier troubleshooting
        run: |
          echo "Checking if current stable: ${{ steps.current_versions.outputs.stable }} equals latest stable: ${{ steps.latest_go_algorand.outputs.stable }}"
          [[ "${{ steps.current_versions.outputs.stable }}" == "${{ steps.latest_go_algorand.outputs.stable }}" ]] && echo "Matches" || echo "NO MATCH!!"
          echo "Checking if current beta: ${{ steps.current_versions.outputs.beta }} equals latest beta: ${{ steps.latest_go_algorand.outputs.beta }}"
          [[ "${{ steps.current_versions.outputs.beta }}" == "${{ steps.latest_go_algorand.outputs.beta }}" ]] && echo "Matches" || echo "NO MATCH!!"
          echo "Checking if current indexer: ${{ steps.current_versions.outputs.indexer }} equals latest indexer: ${{ steps.latest_indexer.outputs.indexer }}"
          [[ "${{ steps.current_versions.outputs.indexer }}" == "${{ steps.latest_indexer.outputs.indexer }}" ]] && echo "Matches" || echo "NO MATCH!!"

    outputs: # Translate step outputs into job outputs
      current_stable: ${{ steps.current_versions.outputs.stable }}
      current_beta: ${{ steps.current_versions.outputs.beta }}
      current_indexer: ${{ steps.current_versions.outputs.indexer }}
      latest_stable: ${{ steps.latest_go_algorand.outputs.stable }}
      latest_beta: ${{ steps.latest_go_algorand.outputs.beta }}
      latest_indexer: ${{ steps.latest_indexer.outputs.indexer }}

  trigger-pr-generator-stable:
    name: Trigger docs and version updates if STABLE versions don't match
    needs: release-checker
    if: |
      needs.release-checker.outputs.current_stable != needs.release-checker.outputs.latest_stable ||
      needs.release-checker.outputs.current_indexer != needs.release-checker.outputs.latest_indexer
    uses: ./.github/workflows/pr-generator.yml
    with:
      go_algorand_version: ${{ needs.release-checker.outputs.latest_stable }}
      indexer_version: ${{ needs.release-checker.outputs.latest_indexer }}

  trigger-pr-generator-beta:
    name: Trigger docs and version updates if BETA versions don't match
    needs: release-checker
    if: needs.release-checker.outputs.current_beta != needs.release-checker.outputs.latest_beta
    uses: ./.github/workflows/pr-generator.yml
    with:
      go_algorand_version: ${{ needs.release-checker.outputs.latest_beta }}
      indexer_version: ${{ needs.release-checker.outputs.latest_indexer }}


Probot

name: Probot

on:
  issues:
    types: [labeled]
  # should use `pull_request_target` but it's blocked by
  # https://github.com/probot/probot/issues/1635
  # so this job will not run on forks until the above is fixed
  pull_request:
    types: [labeled, ready_for_review]

jobs:
  auto-cc:
    runs-on: ubuntu-latest
    if: github.event_name == 'issue' || github.event.pull_request.draft == false
    timeout-minutes: 5
    steps:
      - uses: Lightning-AI/probot@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          job: auto-cc


General checks

name: General checks

on:
  push:
    branches: [main]
  pull_request: {}

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }}
  cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }}

jobs:
  precommit-run:
    uses: Lightning-AI/utilities/.github/workflows/check-precommit.yml@v0.11.2
    with:
      python-version: "3.10"

  check-schema:
    uses: Lightning-AI/utilities/.github/workflows/check-schema.yml@v0.11.2
    with:
      azure-dir: ".azure"

  check-package:
    uses: Lightning-AI/utilities/.github/workflows/check-package.yml@v0.11.2
    with:
      actions-ref: v0.11.2
      import-name: "thunder"
      artifact-name: dist-packages-${{ github.sha }}
      testing-matrix: |
        {
          "os": ["ubuntu-latest", "macOS-latest", "windows-latest"],
          "python-version": ["3.10", "3.11"]
        }


CI testing

name: CI testing

# see: https://help.github.com/en/actions/reference/events-that-trigger-workflows
on: # Trigger the workflow on push or pull request, but only for the main branch
  push:
    branches: [main]
  pull_request: {}

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

defaults:
  run:
    shell: bash

env:
  #CI: "true"
  TORCH_URL_RC: "https://download.pytorch.org/whl/test/cpu/torch_test.html"
  TORCH_URL_NIGHTLY: "https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html"
  TORCH_URL_STABLE: "https://download.pytorch.org/whl/cpu"

jobs:
  pytester:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: ["ubuntu-22.04", "macOS-12", "windows-2022"]
        python-version: ["3.10"]
        requires: ["latest", "nightly"] # , 'oldest'
        include:
          - { os: "ubuntu-22.04", python-version: "3.11", requires: "latest" }

    # Timeout: https://stackoverflow.com/a/59076067/4521646
    timeout-minutes: 35

    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Setup Ubuntu
        if: runner.os == 'ubuntu'
        run: |
          sudo apt-get install -y graphviz

      - name: Set min. dependencies
        if: matrix.requires == 'oldest'
        run: |
          for fpath in ('requirements/base.txt', 'requirements/test.txt'):
              req = open(fpath).read().replace('>=', '==')
              open(fpath, 'w').write(req)
        shell: python

      - name: Get pip cache dir
        id: pip-cache
        run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT

      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ${{ steps.pip-cache.outputs.dir }}
          key: ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.requires }}-pip-${{ hashFiles('requirements/*.txt') }}
          restore-keys: |
            ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.requires }}-pip-
      - name: switch Torch source
        run: |
          if [[ "${{ matrix.requires }}" == "nightly" ]]; then
            echo "TORCH_URL=$TORCH_URL_NIGHTLY" >> $GITHUB_ENV
            echo "PIP_EXTRA_FLAG=--pre" >> $GITHUB_ENV
          else
            echo "TORCH_URL=$TORCH_URL_STABLE" >> $GITHUB_ENV
          fi

      - name: Install package & dependencies
        run: |
          pip --version
          pip install -e . -U \
            -r requirements/test.txt \
            --find-links=${TORCH_URL} ${PIP_EXTRA_FLAG}
          pip list
        shell: bash

      - name: Testing Local
        if: matrix.python-version == '3.10'
        run: |
          coverage run --source thunder -m \
            pytest thunder/tests/ \
              --ignore=thunder/tests/distributed \
              -v --datefmt="%Y%m%d-%H:%M:%S.%f" \
              --random-order-seed=$GITHUB_RUN_ID \
              -n 2 --durations=250

      - name: Testing Distributed
        # run all found tests in given past as standalone
        if: matrix.python-version == '3.10' && runner.os == 'Linux'
        run: |
          pytest thunder/tests/distributed/ \
            -v --datefmt="%Y%m%d-%H:%M:%S.%f" \
            --random-order-seed=$GITHUB_RUN_ID \
            --durations=250

      - name: Testing just a few
        if: matrix.python-version == '3.11'
        #continue-on-error: true
        run: |
          python -m pytest \
            thunder/tests/test_interpreter.py \
            -v --durations=50 --cov=thunder
          python -m pytest thunder/tests/test_jit_general.py -v --durations=50 --cov=thunder

      - name: Statistics
        run: |
          coverage report
          coverage xml

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./coverage.xml
          flags: unittests
          env_vars: OS,PYTHON
          name: codecov-umbrella
          fail_ci_if_error: false

  testing-guardian:
    runs-on: ubuntu-latest
    needs: pytester
    if: always()
    steps:
      - run: echo "${{ needs.pytester.result }}"
      - name: failing...
        if: needs.pytester.result == 'failure'
        run: exit 1
      - name: cancelled or skipped...
        if: contains(fromJSON('["cancelled", "skipped"]'), needs.pytester.result)
        timeout-minutes: 1
        run: sleep 90


Build (& deploy) Docs

name: "Build (& deploy) Docs"
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
    types: [opened, reopened, ready_for_review, synchronize]
  workflow_dispatch: {}

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

defaults:
  run:
    shell: bash

jobs:
  docs-make:
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        target: ["html", "doctest", "linkcheck"]
    env:
      ARTIFACT_DAYS: 0
      PYPI_LOCAL_DIR: "pypi_pkgs/"
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Pull sphinx template
        run: make get-sphinx-theme
      - name: Install pandoc
        timeout-minutes: 5
        run: sudo apt-get install -y pandoc
      - name: Install package & dependencies
        timeout-minutes: 20
        run: pip install . -U -r requirements/docs.txt

      - name: Make ${{ matrix.target }}
        working-directory: docs/
        # allow failing link check and doctest if you run with dispatch
        continue-on-error: ${{ matrix.target == 'doctest' || matrix.target == 'linkcheck' }}
        run: make ${{ matrix.target }} --debug --jobs $(nproc) SPHINXOPTS="-W --keep-going"

      - name: Keep artifact
        if: github.event_name == 'pull_request'
        run: echo "ARTIFACT_DAYS=7" >> $GITHUB_ENV
      - name: Upload built docs
        if: ${{ matrix.target == 'html' }}
        uses: actions/upload-artifact@v4
        with:
          name: docs-html-${{ github.sha }}
          path: docs/build/html/
          retention-days: ${{ env.ARTIFACT_DAYS }}

  deploy-docs:
    needs: docs-make
    if: github.repository_owner == 'Lightning-AI' && github.event_name == 'push'
    runs-on: ubuntu-latest
    env:
      GCP_TARGET: "gs://lightning-docs-thunder"
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: docs-html-${{ github.sha }}
          path: docs/build/html/

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCS_SA_KEY }}

      - name: Setup gcloud
        uses: google-github-actions/setup-gcloud@v2
        with:
          project_id: ${{ secrets.GCS_PROJECT }}

      # Uploading docs to GCS, so they can be served on lightning.ai
      #- name: Upload docs/thunder/stable to GCS 🪣
      #  if: startsWith(github.ref, 'refs/heads/release/')
      #  run: gsutil -m rsync -d -R docs/build/html/ ${GCP_TARGET}/stable

      # Uploading docs to GCS, so they can be served on lightning.ai
      - name: Upload docs/thunder/latest to GCS 🪣
        if: github.ref == 'refs/heads/main'
        run: gsutil -m rsync -d -R docs/build/html/ ${GCP_TARGET}/latest

      # Uploading docs to GCS, so they can be served on lightning.ai
      #- name: Upload docs/thunder/release to GCS 🪣
      #  if: startsWith(github.ref, 'refs/tags/')
      #  run: gsutil -m rsync -d -R docs/build/html/ ${GCP_TARGET}/${{ github.ref_name }}

      # Uploading docs as archive to GCS, so they can be as backup
      #- name: Upload docs as archive to GCS 🪣
      #  if: startsWith(github.ref, 'refs/tags/')
      #  working-directory: docs/build
      #  run: |
      #    zip ${{ github.ref_name }}.zip -r html/
      #    gsutil cp ${{ github.ref_name }}.zip ${GCP_TARGET}


Label merge conflicts

name: Label merge conflicts

on:
  push:
    branches: ["main"]
  pull_request_target:
    types: ["synchronize", "reopened", "opened"]

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

jobs:
  triage-conflicts:
    runs-on: ubuntu-latest
    steps:
      - uses: mschilde/auto-label-merge-conflicts@591722e97f3c4142df3eca156ed0dcf2bcd362bd
        with:
          CONFLICT_LABEL_NAME: "has conflicts"
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          MAX_RETRIES: 3
          WAIT_MS: 5000


Pull Request Labeler

name: "Pull Request Labeler"
on: [pull_request_target]

jobs:
  triage-prs:
    permissions:
      contents: read
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      # Uploads repository content to the runner
      - uses: actions/checkout@v4
      - uses: actions/labeler@v5
        with:
          # The path to the label configuration file.
          configuration-path: .github/labeling-config.yml
          # Whether removing labels when matching files are reverted or no longer changed by the PR
          sync-labels: true


Nightly packages

name: Nightly packages

on:
  pull_request: # this shall test only the part of workflow before publishing
    branches: [main, "release/*"]
    types: [opened, reopened, ready_for_review, synchronize]
    paths:
      - ".github/workflows/release-nightly.yml"
  schedule:
    - cron: "0 0 * * 0" # on Sundays
  workflow_dispatch: {}

defaults:
  run:
    shell: bash

jobs:
  releasing-nightly:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install dependencies
        run: python -m pip install --user --upgrade setuptools wheel
      - name: Build package
        env:
          CONVERT_VERSION2NIGHTLY: "1"
        run: python setup.py sdist bdist_wheel

      # We do this, since failures on test.pypi aren't that bad
      - name: Publish to Test PyPI
        if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
        uses: pypa/gh-action-pypi-publish@v1.8.14
        with:
          user: __token__
          password: ${{ secrets.test_pypi_password }}
          repository_url: https://test.pypi.org/legacy/

      - name: Publish distribution 📦 to PyPI
        if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
        uses: pypa/gh-action-pypi-publish@v1.8.14
        with:
          user: __token__
          password: ${{ secrets.pypi_password }}


PyPI Release

name: PyPI Release

# https://help.github.com/en/actions/reference/events-that-trigger-workflows
on: # Trigger the workflow on push or pull request, but only for the main branch
  push:
    branches: [main]
  release:
    types: [published]

# based on https://github.com/pypa/gh-action-pypi-publish

jobs:
  releasing-pypi:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install dependencies
        run: python -m pip install --user --upgrade setuptools wheel
      - name: Build
        run: python setup.py sdist bdist_wheel

      # We do this, since failures on test.pypi aren't that bad
      - name: Publish to Test PyPI
        if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release'
        uses: pypa/gh-action-pypi-publish@v1.8.14
        with:
          user: __token__
          password: ${{ secrets.test_pypi_password }}
          repository_url: https://test.pypi.org/legacy/

      - name: Publish distribution 📦 to PyPI
        if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release'
        uses: pypa/gh-action-pypi-publish@v1.8.14
        with:
          user: __token__
          password: ${{ secrets.pypi_password }}


Delete abandoned branches

name: Delete abandoned branches

on:
  pull_request:
    branches: ["main"]
    paths:
      - ".github/workflows/stale-branches.yaml"
  # Run daily at midnight
  schedule:
    - cron: "0 0 * * *"
  # Allow workflow to be manually run from the GitHub UI
  workflow_dispatch:

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

jobs:
  cleanup-old-branches:
    runs-on: ubuntu-latest
    name: Satisfy my repo CDO
    env:
      DRY_RUN: no
    steps:
      - name: Set dry run
        if: ${{ github.event_name == 'pull_request' }}
        run: echo "DRY_RUN=yes" >> $GITHUB_ENV
      - name: Delete those pesky dead branches
        uses: phpdocker-io/github-actions-delete-abandoned-branches@v2
        id: delete_stuff
        with:
          github_token: ${{ github.token }}
          last_commit_age_days: 90
          ignore_branches: main
          # Disable dry run and actually get stuff deleted
          # For a PR, always perform dry run
          dry_run: ${{ env.DRY_RUN }}

      - name: Get output
        run: "echo 'Deleted branches: ${{ steps.delete_stuff.outputs.deleted_branches }}'"


Python Style Check

name: Python Style Check

# Cancel previous runs in the PR when you push new commits
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
  cancel-in-progress: true

permissions:
  contents: read

# Controls when the action will run.
on:
  push:
    branches: [ main ]
    paths:
      - 'python/**'
      - '.github/workflows/python-style-check.yml'
  pull_request:
    branches: [ main ]
    paths:
      - 'python/**'
      - '.github/workflows/python-style-check.yml'
  # schedule:
  #   - cron: '0 16 * * *'

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  style-check:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.7]

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # actions/checkout@v2

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies
        run: pip install pycodestyle
             pip install pydocstyle
             pip install mypy==0.982
             pip install wheel

      - name: LLM style checking
        run: bash python/llm/dev/test/lint-python


Manually Build

name: Manually Build

on:
  workflow_dispatch:
    inputs:
      artifact:
        description: 'select which job to run("all" will make all jobs run)'
        required: true
        default: 'all'
        type: choice
        options:
        - all
        - ipex-llm-cpu
        - ipex-llm-xpu
        - ipex-llm-inference-cpp-xpu
        - ipex-llm-serving-cpu
        - ipex-llm-serving-xpu
        - ipex-llm-finetune-lora-cpu
        - ipex-llm-finetune-qlora-cpu
        - ipex-llm-finetune-qlora-cpu-k8s
        - ipex-llm-finetune-xpu
      tag:
        description: 'docker image tag (e.g. 2.1.0-SNAPSHOT)'
        required: true
        default: '2.1.0-SNAPSHOT'
        type: string
  workflow_call:
    inputs:
      artifact:
        description: 'select which job to run("all" will make all jobs run)'
        required: true
        default: 'all'
        type: string
      tag:
        description: 'docker image tag (e.g. 2.1.0-SNAPSHOT)'
        required: true
        default: '2.1.0-SNAPSHOT'
        type: string

env:
  TAG: ${{ inputs.tag }}

permissions:
  contents: read

jobs:
  ipex-llm-finetune-lora-cpu:
    if: ${{ inputs.artifact == 'ipex-llm-finetune-lora-cpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-finetune-lora-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-finetune-lora-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-finetune-lora-cpu
        cd docker/llm/finetune/lora/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-finetune-qlora-cpu:
    if: ${{ inputs.artifact == 'ipex-llm-finetune-qlora-cpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      - name: docker login
        run: |
          docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
      - name: ipex-llm-finetune-qlora-cpu
        run: |
          echo "##############################################################"
          echo "####### ipex-llm-finetune-qlora-cpu ########"
          echo "##############################################################"
          export image=intelanalytics/ipex-llm-finetune-qlora-cpu
          cd docker/llm/finetune/qlora/cpu/docker
          sudo docker build \
            --no-cache=true \
            --build-arg http_proxy=${HTTP_PROXY} \
            --build-arg https_proxy=${HTTPS_PROXY} \
            --build-arg no_proxy=${NO_PROXY} \
            -t ${image}:${TAG} -f ./Dockerfile .
          sudo docker push ${image}:${TAG}
          sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
          sudo docker push 10.239.45.10/arda/${image}:${TAG}
          # tag 'latest'
          sudo docker tag ${image}:${TAG} ${image}:latest
          sudo docker push ${image}:latest
          sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-finetune-qlora-cpu-k8s:
    if: ${{ inputs.artifact == 'ipex-llm-finetune-qlora-cpu-k8s' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      - name: docker login
        run: |
          docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
      - name: ipex-llm-finetune-qlora-cpu-k8s
        run: |
          echo "##############################################################"
          echo "####### ipex-llm-finetune-qlora-cpu-k8s ########"
          echo "##############################################################"
          export image=intelanalytics/ipex-llm-finetune-qlora-cpu-k8s
          cd docker/llm/finetune/qlora/cpu/docker
          sudo docker build \
            --no-cache=true \
            --build-arg http_proxy=${HTTP_PROXY} \
            --build-arg https_proxy=${HTTPS_PROXY} \
            --build-arg no_proxy=${NO_PROXY} \
            -t ${image}:${TAG} -f ./Dockerfile.k8s .
          sudo docker push ${image}:${TAG}
          sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
          sudo docker push 10.239.45.10/arda/${image}:${TAG}
          # tag 'latest'
          sudo docker tag ${image}:${TAG} ${image}:latest
          sudo docker push ${image}:latest
          sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-finetune-xpu:
    if: ${{ inputs.artifact == 'ipex-llm-finetune-xpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-finetune-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-finetune-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-finetune-xpu
        cd docker/llm/finetune/xpu
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-xpu:
    if: ${{ inputs.artifact == 'ipex-llm-xpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-xpu
        cd docker/llm/inference/xpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-inference-cpp-xpu:
    if: ${{ inputs.artifact == 'ipex-llm-inference-cpp-xpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-inference-cpp-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-inference-cpp-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-inference-cpp-xpu
        cd docker/llm/inference-cpp/
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-cpu:
    if: ${{ inputs.artifact == 'ipex-llm-cpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-cpu
        cd docker/llm/inference/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-serving-xpu:
    if: ${{ inputs.artifact == 'ipex-llm-serving-xpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-serving-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-serving-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-serving-xpu
        cd docker/llm/serving/xpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest

  ipex-llm-serving-cpu:
    if: ${{ inputs.artifact == 'ipex-llm-serving-cpu' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire, AVX512]
    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-serving-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-serving-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-serving-cpu
        cd docker/llm/serving/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker push ${image}:${TAG}
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        # tag 'latest'
        sudo docker tag ${image}:${TAG} ${image}:latest
        sudo docker push ${image}:latest
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG} ${image}:latest


Manually Build For Testing

name: Manually Build For Testing

on:
  workflow_dispatch:
    inputs:
      sha:
        description: 'commit id (SHA-1 hash)'
        required: true
        type: string
      artifact:
        description: 'select which job to run("all" will make all jobs run)'
        required: true
        default: 'all'
        type: choice
        options:
        - all
        - ipex-llm-cpu
        - ipex-llm-xpu
        - ipex-llm-inference-cpp-xpu
        - ipex-llm-serving-cpu
        - ipex-llm-serving-xpu
        - ipex-llm-finetune-lora-cpu
        - ipex-llm-finetune-qlora-cpu
        - ipex-llm-finetune-qlora-cpu-k8s
        - ipex-llm-finetune-xpu
      tag:
        description: 'docker image tag (e.g. test)'
        required: true
        default: 'test'
        type: string

env:
  TAG: ${{ github.event.inputs.tag }}

permissions:
  contents: read

jobs:
  ipex-llm-finetune-lora-cpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-finetune-lora-cpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-finetune-lora-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-finetune-lora-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-finetune-lora-cpu
        cd docker/llm/finetune/lora/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-finetune-qlora-cpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-finetune-qlora-cpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
        with:
          ref: ${{ github.event.inputs.sha }}
      - name: docker login
        run: |
          docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
      - name: ipex-llm-finetune-qlora-cpu
        run: |
          echo "##############################################################"
          echo "####### ipex-llm-finetune-qlora-cpu ########"
          echo "##############################################################"
          export image=intelanalytics/ipex-llm-finetune-qlora-cpu
          cd docker/llm/finetune/qlora/cpu/docker
          sudo docker build \
            --no-cache=true \
            --build-arg http_proxy=${HTTP_PROXY} \
            --build-arg https_proxy=${HTTPS_PROXY} \
            --build-arg no_proxy=${NO_PROXY} \
            -t ${image}:${TAG} -f ./Dockerfile .
          sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
          sudo docker push 10.239.45.10/arda/${image}:${TAG}
          sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-finetune-qlora-cpu-k8s:
    if: ${{ inputs.artifact == 'ipex-llm-finetune-qlora-cpu-k8s' || inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      - name: docker login
        run: |
          docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
      - name: ipex-llm-finetune-qlora-cpu-k8s
        run: |
          echo "##############################################################"
          echo "####### ipex-llm-finetune-qlora-cpu-k8s ########"
          echo "##############################################################"
          export image=intelanalytics/ipex-llm-finetune-qlora-cpu-k8s
          cd docker/llm/finetune/qlora/cpu/docker
          sudo docker build \
            --no-cache=true \
            --build-arg http_proxy=${HTTP_PROXY} \
            --build-arg https_proxy=${HTTPS_PROXY} \
            --build-arg no_proxy=${NO_PROXY} \
            -t ${image}:${TAG} -f ./Dockerfile.k8s .
          sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
          sudo docker push 10.239.45.10/arda/${image}:${TAG}
          sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-finetune-xpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-finetune-xpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-finetune-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-finetune-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-finetune-xpu
        cd docker/llm/finetune/xpu
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-xpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-xpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-xpu
        cd docker/llm/inference/xpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-inference-cpp-xpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-inference-cpp-xpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-inference-cpp-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-inference-cpp-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-inference-cpp-xpu
        cd docker/llm/inference-cpp/
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-cpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-cpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-cpu
        cd docker/llm/inference/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-serving-xpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-serving-xpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-serving-xpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-serving-xpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-serving-xpu
        cd docker/llm/serving/xpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}

  ipex-llm-serving-cpu:
    if: ${{ github.event.inputs.artifact == 'ipex-llm-serving-cpu' || github.event.inputs.artifact == 'all' }}
    runs-on: [self-hosted, Shire, AVX512]

    steps:
    - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      with:
        ref: ${{ github.event.inputs.sha }}
    - name: docker login
      run: |
        docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}
    - name: ipex-llm-serving-cpu
      run: |
        echo "##############################################################"
        echo "####### ipex-llm-serving-cpu ########"
        echo "##############################################################"
        export image=intelanalytics/ipex-llm-serving-cpu
        cd docker/llm/serving/cpu/docker
        sudo docker build \
          --no-cache=true \
          --build-arg http_proxy=${HTTP_PROXY} \
          --build-arg https_proxy=${HTTPS_PROXY} \
          --build-arg no_proxy=${NO_PROXY} \
          -t ${image}:${TAG} -f ./Dockerfile .
        sudo docker tag ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}
        sudo docker push 10.239.45.10/arda/${image}:${TAG}
        sudo docker rmi -f ${image}:${TAG} 10.239.45.10/arda/${image}:${TAG}


LLM Nightly Tests

name: LLM Nightly Tests

# Cancel previous runs in the PR when you push new commits
concurrency:
  group: ${{ github.workflow }}-llm-nightly-test-${{ github.event.pull_request.number || github.run_id }}
  cancel-in-progress: true

permissions:
  contents: read

# Controls when the action will run.
on:
  schedule:
    - cron: "00 13 * * *" # GMT time, 13:00 GMT == 21:00 China
  pull_request:
    branches: [main]
    paths:
      - ".github/workflows/llm-nightly-test.yml"
      - ".github/actions/llm/setup-llm-env/action.yml"
      - ".github/actions/llm/remove-llm-env/action.yml"
      - ".github/actions/llm/convert-test/action.yml"
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  llm-cpp-build:
    uses: ./.github/workflows/llm-binary-build.yml
  llm-nightly-convert-test:
    needs: llm-cpp-build
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows
            instruction: AVX-VNNI-UT
            python-version: "3.11"
          - os: ubuntu-20.04-lts
            instruction: avx512
            python-version: "3.11"
    runs-on: [self-hosted, llm, "${{matrix.instruction}}", "${{matrix.os}}"]
    env:
      ANALYTICS_ZOO_ROOT: ${{ github.workspace }}
    steps:
      - name: Set model directories
        shell: bash
        run: |
          echo "ORIGIN_DIR=$(pwd)/../llm/origin-models" >> "$GITHUB_ENV"
          echo "INT4_CKPT_DIR=$(pwd)/../llm/nightly-converted-models" >> "$GITHUB_ENV"
      - name: Create model directories
        shell: bash
        run: |
          if [ ! -d $ORIGIN_DIR ]; then
            mkdir -p $ORIGIN_DIR
          fi
          if [ ! -d $INT4_CKPT_DIR ]; then
            mkdir -p $INT4_CKPT_DIR
          fi
      - name: Set environment variables
        shell: bash
        run: |
          echo "LLAMA_ORIGIN_PATH=${ORIGIN_DIR}/llama-7b-hf" >> "$GITHUB_ENV"
          echo "GPTNEOX_ORIGIN_PATH=${ORIGIN_DIR}/gptneox-7b-redpajama-bf16" >> "$GITHUB_ENV"
          echo "BLOOM_ORIGIN_PATH=${ORIGIN_DIR}/bloomz-7b1" >> "$GITHUB_ENV"
          echo "STARCODER_ORIGIN_PATH=${ORIGIN_DIR}/gpt_bigcode-santacoder" >> "$GITHUB_ENV"

          echo "LLAMA_INT4_CKPT_PATH=${INT4_CKPT_DIR}/bigdl_llm_llama_q4_0.bin" >> "$GITHUB_ENV"
          echo "GPTNEOX_INT4_CKPT_PATH=${INT4_CKPT_DIR}/bigdl_llm_gptneox_q4_0.bin" >> "$GITHUB_ENV"
          echo "BLOOM_INT4_CKPT_PATH=${INT4_CKPT_DIR}/bigdl_llm_bloom_q4_0.bin" >> "$GITHUB_ENV"
          echo "STARCODER_INT4_CKPT_PATH=${INT4_CKPT_DIR}/bigdl_llm_starcoder_q4_0.bin" >> "$GITHUB_ENV"
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        shell: bash
        run: |
          python -m pip install --upgrade pip
          python -m pip install --upgrade setuptools==58.0.4
          python -m pip install --upgrade wheel

      - name: Download llm binary
        uses: ./.github/actions/llm/download-llm-binary

      - name: Install IPEX-LLM
        uses: ./.github/actions/llm/setup-llm-env

      - name: Download original models & convert
        uses: ./.github/actions/llm/convert-test

      - name: Upload ckpt to ftp
        shell: bash
        if: runner.os == 'Linux' && github.event_name == 'schedule'
        run: |
          curl -T $LLAMA_INT4_CKPT_PATH ${LLM_FTP_URL}/llm/ggml-actions/nightly/bigdl_llm_llama_7b_q4_0.bin
          curl -T $GPTNEOX_INT4_CKPT_PATH ${LLM_FTP_URL}/llm/ggml-actions/nightly/bigdl_llm_redpajama_7b_q4_0.bin
          curl -T $BLOOM_INT4_CKPT_PATH ${LLM_FTP_URL}/llm/ggml-actions/nightly/bigdl_llm_bloom_7b_q4_0.bin
          curl -T $STARCODER_INT4_CKPT_PATH ${LLM_FTP_URL}/llm/ggml-actions/nightly/bigdl_llm_santacoder_1b_q4_0.bin
      - name: Delete ckpt
        shell: bash
        run: |
          rm -rf $LLAMA_INT4_CKPT_PATH
          rm -rf $GPTNEOX_INT4_CKPT_PATH
          rm -rf $BLOOM_INT4_CKPT_PATH
          rm -rf $STARCODER_INT4_CKPT_PATH

  llm-unit-tests:
    needs: llm-cpp-build
    uses: ./.github/workflows/llm_unit_tests.yml
  llm-example-test:
    needs: llm-cpp-build
    uses: ./.github/workflows/llm_example_tests.yml