microsoft/semantic-kernel

21 workflows · maturity 83% · 7 patterns · GitHub ↗

Security 23.21/100

Practices

✓ Matrix✓ Permissions✓ Security scan○ AI review✓ Cache✓ Concurrency○ Reusable workflows

Detected patterns

Security dimensions

permissions
10.7
security scan
12.5
supply chain
0
secret handling
0
harden runner
0

Tools: github/codeql-action/analyze, github/codeql-action/autobuild, github/codeql-action/init

Workflows (21)

close-inactive-issues .github/workflows/close-inactive-issues.yml
Triggers
schedule
Runs on
ubuntu-latest
Jobs
close-issues
Actions
actions/stale
View raw YAML
name: Close inactive issues
on:
  schedule:
    - cron: "30 1 * * *"

jobs:
  close-issues:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - uses: actions/stale@v5
        with:
          days-before-issue-stale: 90
          days-before-issue-close: 14
          stale-issue-label: "stale"
          stale-issue-message: "This issue is stale because it has been open for 90 days with no activity."
          close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
          days-before-pr-stale: -1
          days-before-pr-close: -1
          repo-token: ${{ secrets.GITHUB_TOKEN }}
codeql-analysis matrix security .github/workflows/codeql-analysis.yml
Triggers
workflow_dispatch, push, schedule
Runs on
ubuntu-latest
Jobs
analyze
Matrix
language→ csharp, python
Actions
github/codeql-action/init, github/codeql-action/autobuild, github/codeql-action/analyze
View raw YAML
# CodeQL is the code analysis engine developed by GitHub to automate security checks.
# The results are shown as code scanning alerts in GitHub. For more details, visit:
# https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql

name: "CodeQL"

on:
  workflow_dispatch:
  push:
    # TODO: Add "feature*" back in again, once we determine the cause of the ongoing CodeQL failures.
    branches: ["main", "experimental*", "*-development"]
  schedule:
    - cron: "17 11 * * 2"

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

    strategy:
      fail-fast: false
      matrix:
        language: ["csharp", "python"]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Use only 'java' to analyze code written in Java, Kotlin or both
        # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

    steps:
      - name: Checkout repository
        uses: actions/checkout@v5
        with:
          persist-credentials: false

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

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

      # Autobuild attempts to build any compiled languages  (C/C++, C#, Go, or Java).
      # If this step fails, then you should remove it and run the build manually (see below)
      - name: Autobuild
        if: ${{ matrix.language != 'java' }}
        uses: github/codeql-action/autobuild@v3

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

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

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

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{matrix.language}}"
devflow-pr-review perms .github/workflows/devflow-pr-review.yml
Triggers
pull_request_target, workflow_dispatch
Runs on
ubuntu-latest
Jobs
review
Actions
astral-sh/setup-uv
Commands
  • set -euo pipefail if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then pr_number="${PR_NUMBER_EVENT}" pr_url="${PR_HTML_URL}" else pr_number="${PR_NUMBER_INPUT}" pr_url="https://github.com/${GITHUB_REPOSITORY}/pull/${pr_number}" fi if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then echo "Could not determine PR number; for workflow_dispatch runs, the 'pr_number' input is required when not running on pull_request_target." >&2 exit 1 fi echo "pr_url=${pr_url}" >> "$GITHUB_OUTPUT" echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT" echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
  • uv sync --frozen
  • uv run python scripts/classify_pr_spam.py \ --repo "$PR_REPO" \ --pr-number "$PR_NUMBER" \ --repo-path "${TARGET_REPO_PATH}" \ --apply-labels
  • echo "Skipping review because spam gate decided: ${SPAM_DECISION}"
  • uv run python scripts/trigger_pr_review.py \ --pr-url "$PR_URL" \ --github-username "$GITHUB_ACTOR" \ --no-require-comment-selection
View raw YAML
name: DevFlow PR Review

on:
  pull_request_target:
    types:
      - opened
      - reopened
      - ready_for_review
  workflow_dispatch:
    inputs:
      pr_number:
        description: Pull request number to review
        required: true
        type: string

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

concurrency:
  group: devflow-pr-review-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}
  cancel-in-progress: true

env:
  DEVFLOW_REPOSITORY: ${{ vars.DF_REPO }}
  DEVFLOW_REF: main
  TARGET_REPO_PATH: ${{ github.workspace }}/target-repo
  DEVFLOW_PATH: ${{ github.workspace }}/devflow

jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    if: ${{ github.event_name != 'pull_request_target' || !github.event.pull_request.draft }}

    steps:
      - name: Resolve PR metadata
        id: pr
        shell: bash
        env:
          PR_HTML_URL: ${{ github.event.pull_request.html_url }}
          PR_NUMBER_EVENT: ${{ github.event.pull_request.number }}
          PR_NUMBER_INPUT: ${{ inputs.pr_number }}
        run: |
          set -euo pipefail

          if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
            pr_number="${PR_NUMBER_EVENT}"
            pr_url="${PR_HTML_URL}"
          else
            pr_number="${PR_NUMBER_INPUT}"
            pr_url="https://github.com/${GITHUB_REPOSITORY}/pull/${pr_number}"
          fi

          if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
            echo "Could not determine PR number; for workflow_dispatch runs, the 'pr_number' input is required when not running on pull_request_target." >&2
            exit 1
          fi

          echo "pr_url=${pr_url}" >> "$GITHUB_OUTPUT"
          echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT"
          echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"

      # Safe checkout: base repo only, not the untrusted PR head.
      - name: Checkout target repo base
        uses: actions/checkout@v5
        with:
          ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
          fetch-depth: 0
          persist-credentials: false
          path: target-repo

      # Private DevFlow checkout: the PAT/token grants access to this repo's code.
      - name: Checkout DevFlow
        uses: actions/checkout@v5
        with:
          repository: ${{ env.DEVFLOW_REPOSITORY }}
          ref: ${{ env.DEVFLOW_REF }}
          token: ${{ secrets.DEVFLOW_TOKEN }}
          fetch-depth: 1
          persist-credentials: false
          path: devflow

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

      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true

      - name: Install DevFlow dependencies
        working-directory: ${{ env.DEVFLOW_PATH }}
        run: uv sync --frozen

      - name: Classify PR relevance
        id: spam
        working-directory: ${{ env.DEVFLOW_PATH }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
          SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
          AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
          PR_REPO: ${{ steps.pr.outputs.repo }}
          PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
        run: |
          uv run python scripts/classify_pr_spam.py \
            --repo "$PR_REPO" \
            --pr-number "$PR_NUMBER" \
            --repo-path "${TARGET_REPO_PATH}" \
            --apply-labels

      - name: Stop after spam gate
        if: ${{ steps.spam.outputs.decision != 'allow' }}
        shell: bash
        env:
          SPAM_DECISION: ${{ steps.spam.outputs.decision }}
        run: |
          echo "Skipping review because spam gate decided: ${SPAM_DECISION}"

      - name: Run PR review
        if: ${{ steps.spam.outputs.decision == 'allow' }}
        id: review
        working-directory: ${{ env.DEVFLOW_PATH }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
          SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
          AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
          PR_URL: ${{ steps.pr.outputs.pr_url }}
        run: |
          uv run python scripts/trigger_pr_review.py \
            --pr-url "$PR_URL" \
            --github-username "$GITHUB_ACTOR" \
            --no-require-comment-selection
dotnet-build-and-test matrix perms .github/workflows/dotnet-build-and-test.yml
Triggers
workflow_dispatch, pull_request, merge_group
Runs on
ubuntu-latest, ${{ matrix.os }}, ubuntu-latest
Jobs
paths-filter, dotnet-build-and-test, dotnet-build-and-test-check
Matrix
include, include.configuration, include.dotnet, include.environment, include.integration-tests, include.os→ 10.0, 9.0, Debug, Release, True, integration, ubuntu-latest, windows-latest
Actions
dorny/paths-filter, azure/login, danielpalme/ReportGenerator-GitHub-Action
Commands
  • echo "Dotnet file"
  • echo "NOT dotnet file"
  • export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ') for solution in $SOLUTIONS; do dotnet build $solution -c ${{ matrix.configuration }} --warnaserror done
  • export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ') for solution in $SOLUTIONS; do dotnet pack $solution -c ${{ matrix.configuration }} --no-build --no-restore --output ./artifacts done dotnet new console --name packcheck --output consoleapp # Create minimal nuget.config and use only dotnet nuget commands echo '<?xml version="1.0" encoding="utf-8"?><configuration><packageSources><clear /></packageSources></configuration>' > consoleapp/nuget.config # Add sources with local first using dotnet nuget commands dotnet nuget add source ../artifacts --name local --configfile consoleapp/nuget.config dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org --configfile consoleapp/nuget.config # Change to project directory to ensure local nuget.config is used cd consoleapp dotnet add packcheck.csproj package Microsoft.SemanticKernel dotnet build -c ${{ matrix.configuration }} packcheck.csproj cd .. # Clean up rm -rf ./artifacts rm -rf ./consoleapp
  • export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | tr '\n' ' ') for project in $UT_PROJECTS; do dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute done
  • .github/workflows/test-aot-compatibility.ps1 ${{ matrix.dotnet }}
  • export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | grep -v "Experimental.Orchestration.Flow.IntegrationTests.csproj" | grep -v "VectorDataIntegrationTests.csproj" | tr '\n' ' ') for project in $INTEGRATION_TEST_PROJECTS; do dotnet test -f net10.0 -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx done
  • .github/workflows/check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD
View raw YAML
#
# This workflow will build and run all unit tests using dotnet docker containers,
# each targeting a single version of the dotnet SDK.
#

name: dotnet-build-and-test

on:
  workflow_dispatch:
  pull_request:
    branches: ["main", "feature*"]
  merge_group:
    branches: ["main"]

env:
  COVERAGE_THRESHOLD: 80

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: read
  id-token: "write"

jobs:
  paths-filter:
    runs-on: ubuntu-latest
    outputs:
      dotnetChanges: ${{ steps.filter.outputs.dotnet }}
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            dotnet:
              - 'dotnet/**'
              - '**/dotnet/**'
              - '.github/workflows/check-coverage.ps1'
              - '.github/workflows/dotnet-build-and-test.yml'
      # run only if 'dotnet' files were changed
      - name: dotnet tests
        if: steps.filter.outputs.dotnet == 'true'
        run: echo "Dotnet file"
      # run only if not 'dotnet' files were changed
      - name: not dotnet tests
        if: steps.filter.outputs.dotnet != 'true'
        run: echo "NOT dotnet file"
  dotnet-build-and-test:
    needs: paths-filter
    if: needs.paths-filter.outputs.dotnetChanges == 'true'
    strategy:
      fail-fast: false
      matrix:
        include:
          - {
              dotnet: "10.0",
              os: "ubuntu-latest",
              configuration: Release,
              integration-tests: true,
              environment: "integration",
            }
          - { dotnet: "10.0", os: "windows-latest", configuration: Debug }
          - { dotnet: "9.0", os: "windows-latest", configuration: Release }

    runs-on: ${{ matrix.os }}
    environment: ${{ matrix.environment }}
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false

      - name: Setup dotnet
        uses: actions/setup-dotnet@v5
        with:
          global-json-file: ${{ github.workspace }}/dotnet/global.json

      - name: Build dotnet solutions
        shell: bash
        run: |
          export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ')
          for solution in $SOLUTIONS; do
            dotnet build $solution -c ${{ matrix.configuration }} --warnaserror
          done

      - name: Package install check
        shell: bash
        run: |
          export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ')
          for solution in $SOLUTIONS; do
            dotnet pack $solution -c ${{ matrix.configuration }} --no-build --no-restore --output ./artifacts
          done
          dotnet new console --name packcheck --output consoleapp

          # Create minimal nuget.config and use only dotnet nuget commands
          echo '<?xml version="1.0" encoding="utf-8"?><configuration><packageSources><clear /></packageSources></configuration>' > consoleapp/nuget.config

          # Add sources with local first using dotnet nuget commands
          dotnet nuget add source ../artifacts --name local --configfile consoleapp/nuget.config
          dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org --configfile consoleapp/nuget.config

          # Change to project directory to ensure local nuget.config is used
          cd consoleapp
          dotnet add packcheck.csproj package Microsoft.SemanticKernel
          dotnet build -c ${{ matrix.configuration }} packcheck.csproj
          cd ..

          # Clean up
          rm -rf ./artifacts
          rm -rf ./consoleapp

      - name: Run Unit Tests
        shell: bash
        run: |
          export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | tr '\n' ' ')
          for project in $UT_PROJECTS; do
            dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute
          done

      - name: Run AOT Unit Tests
        shell: pwsh
        run: .github/workflows/test-aot-compatibility.ps1 ${{ matrix.dotnet }}

      - name: Azure CLI Login
        if: github.event_name != 'pull_request' && matrix.integration-tests
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Run Integration Tests
        shell: bash
        if: github.event_name != 'pull_request' && matrix.integration-tests
        run: |
          export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | grep -v "Experimental.Orchestration.Flow.IntegrationTests.csproj" | grep -v "VectorDataIntegrationTests.csproj" | tr '\n' ' ')
          for project in $INTEGRATION_TEST_PROJECTS; do
            dotnet test -f net10.0 -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
          done
        env:
          # Azure OpenAI Deployments
          AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
          AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
          AzureOpenAI__ChatDeploymentName: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
          AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
          AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }}
          AzureOpenAITextToAudio__Endpoint: ${{ secrets.AZUREOPENAITEXTTOAUDIO__ENDPOINT }}
          AzureOpenAITextToAudio__DeploymentName: ${{ vars.AZUREOPENAITEXTTOAUDIO__DEPLOYMENTNAME }}
          AzureOpenAIAudioToText__Endpoint: ${{ secrets.AZUREOPENAIAUDIOTOTEXT__ENDPOINT }}
          AzureOpenAIAudioToText__DeploymentName: ${{ vars.AZUREOPENAIAUDIOTOTEXT__DEPLOYMENTNAME }}
          AzureOpenAITextToImage__Endpoint: ${{ secrets.AZUREOPENAITEXTTOIMAGE__ENDPOINT }}
          AzureOpenAITextToImage__DeploymentName: ${{ vars.AZUREOPENAITEXTTOIMAGE__DEPLOYMENTNAME }}
          Planners__AzureOpenAI__Endpoint: ${{ secrets.PLANNERS__AZUREOPENAI__ENDPOINT }}
          Planners__AzureOpenAI__DeploymentName: ${{ vars.PLANNERS__AZUREOPENAI__DEPLOYMENTNAME }}
          # OpenAI Models
          OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
          OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }}
          OpenAIEmbeddings__ApiKey: ${{ secrets.OPENAIEMBEDDINGS__APIKEY }}
          OpenAIEmbeddings__ModelId: ${{ vars.OPENAIEMBEDDINGS__MODELID }}
          OpenAITextToAudio__ApiKey: ${{ secrets.OPENAITEXTTOAUDIO__APIKEY }}
          OpenAITextToAudio__ModelId: ${{ vars.OPENAITEXTTOAUDIO__MODELID }}
          OpenAIAudioToText__ApiKey: ${{ secrets.OPENAIAUDIOTOTEXT__APIKEY }}
          OpenAIAudioToText__ModelId: ${{ vars.OPENAIAUDIOTOTEXT__MODELID }}
          OpenAITextToImage__ApiKey: ${{ secrets.OPENAITEXTTOIMAGE__APIKEY }}
          OpenAITextToImage__ModelId: ${{ vars.OPENAITEXTTOIMAGE__MODELID }}
          Planners__OpenAI__ApiKey: ${{ secrets.PLANNERS__OPENAI__APIKEY }}
          Planners__OpenAI__ModelId: ${{ vars.PLANNERS__OPENAI__MODELID }}
          # Bing Web Search
          Bing__ApiKey: ${{ secrets.BING__APIKEY }}
          # Google Web Search
          Google__SearchEngineId: ${{ secrets.GOOGLE__SEARCHENGINEID }}
          Google__ApiKey: ${{ secrets.GOOGLE__APIKEY }}
          # Azure AI Inference Endpoint
          AzureAIInference__ApiKey: ${{ secrets.AZUREAIINFERENCE__APIKEY }}
          AzureAIInference__Endpoint: ${{ secrets.AZUREAIINFERENCE__ENDPOINT }}
          AzureAIInference__ChatModelId: ${{ vars.AZUREAIINFERENCE__CHATMODELID }}
          # Azure AI Foundry
          AzureAI__Endpoint: ${{ secrets.AZUREAI__ENDPOINT }}
          AzureAI__ConnectionString: ${{ secrets.AZUREAI__CONNECTIONSTRING }}
          AzureAI__ChatModelId: ${{ vars.AZUREAI__CHATMODELID }}

      # Generate test reports and check coverage
      - name: Generate test reports
        uses: danielpalme/ReportGenerator-GitHub-Action@5.4.12
        with:
          reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
          targetdir: "./TestResults/Reports"
          reporttypes: "HtmlInline;JsonSummary"

      - name: Upload coverage report artifact
        uses: actions/upload-artifact@v4
        with:
          name: CoverageReport-${{ matrix.os }}-${{ matrix.dotnet }}-${{ matrix.configuration }} # Artifact name
          path: ./TestResults/Reports # Directory containing files to upload

      - name: Check coverage
        shell: pwsh
        run: .github/workflows/check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD

  # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed
  dotnet-build-and-test-check:
    if: always()
    runs-on: ubuntu-latest
    needs: [dotnet-build-and-test]
    steps:
      - name: Get Date
        shell: bash
        run: |
          echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV"

      - name: Run Type is Daily
        if: ${{ github.event_name == 'schedule' }}
        shell: bash
        run: |
          echo "run_type=Daily" >> "$GITHUB_ENV"

      - name: Run Type is Manual
        if: ${{ github.event_name == 'workflow_dispatch' }}
        shell: bash
        run: |
          echo "run_type=Manual" >> "$GITHUB_ENV"

      - name: Run Type is ${{ github.event_name }}
        if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}}
        shell: bash
        run: |
          echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV"

      - name: Fail workflow if tests failed
        id: check_tests_failed
        if: contains(join(needs.*.result, ','), 'failure')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Failed!')

      - name: Fail workflow if tests cancelled
        id: check_tests_cancelled
        if: contains(join(needs.*.result, ','), 'cancelled')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Cancelled!')
dotnet-ci matrix perms .github/workflows/dotnet-ci.yml
Triggers
workflow_dispatch, schedule
Runs on
${{ matrix.os }}, ${{ matrix.os }}
Jobs
build-and-test-docker, build-and-test-windows
Matrix
configuration, dotnet-version, include, include.configuration, include.dotnet, include.os, os→ 10.0, 10.0.x, Debug, Release, ubuntu-latest, windows-latest
Commands
  • echo "solutions=$(find ./ -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV
  • docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}
  • for solution in ${{ env.solutions }}; do docker run --rm -v $(pwd):/app -w /app -e GITHUB_ACTIONS='true' mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet build -c ${{ matrix.configuration }} /app/$solution" done
  • echo "testprojects=$(find ./dotnet -type f -name "*UnitTests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV
  • for project in ${{ env.testprojects }}; do docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet test -c ${{ matrix.configuration }} /app/$project --no-build -v Normal --logger trx" done
  • echo "solutions=$(find ./dotnet -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV
  • for solution in ${{ env.solutions }}; do dotnet restore $solution done
  • for solution in ${{ env.solutions }}; do dotnet build $solution --no-restore --configuration ${{ matrix.configuration }} done
View raw YAML
#
# This workflow will build and run all tests using dotnet docker containers,
# each targeting a single version of the dotnet SDK.
#

name: dotnet-ci

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

permissions:
  contents: read

jobs:
  build-and-test-docker:
    strategy:
        fail-fast: false
        matrix:
          include:
          - { os: ubuntu-latest, dotnet: '10.0', configuration: Debug }
          - { os: ubuntu-latest, dotnet: '10.0', configuration: Release }

    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v5
      with:
        clean: true
        persist-credentials: false

    - name: Find solutions
      shell: bash
      run: echo "solutions=$(find ./ -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV

    - name: Pull container dotnet/sdk:${{ matrix.dotnet }}
      run: |
        docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}

    - name: Build
      run: |
        for solution in ${{ env.solutions }}; do
          docker run --rm -v $(pwd):/app -w /app -e GITHUB_ACTIONS='true' mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet build -c ${{ matrix.configuration }} /app/$solution"
        done

    - name: Find test projects
      shell: bash
      run: echo "testprojects=$(find ./dotnet -type f -name "*UnitTests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV

    - name: Run Tests
      shell: bash
      run: |
        for project in ${{ env.testprojects }}; do
          docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet test -c ${{ matrix.configuration }} /app/$project --no-build -v Normal --logger trx"
        done

    - name: Upload dotnet test results
      uses: actions/upload-artifact@v4
      with:
        name: dotnet-testresults-${{ matrix.configuration }}
        path: ./TestResults
      if: ${{ always() }}

  build-and-test-windows:
    strategy:
        fail-fast: false
        matrix:
          os: [windows-latest]
          configuration: [Release, Debug]
          dotnet-version: ['10.0.x']
    runs-on: ${{ matrix.os }}
    env:
      NUGET_CERT_REVOCATION_MODE: offline
    steps:
    - uses: actions/checkout@v5
      with:
        clean: true
        persist-credentials: false

    - name: Setup .NET SDK ${{ matrix.dotnet-version }}
      uses: actions/setup-dotnet@v5
      with:
        dotnet-version: ${{ matrix.dotnet-version }}
      env:
        NUGET_AUTH_TOKEN: ${{ secrets.GPR_READ_TOKEN }}

    - uses: actions/cache@v4
      with:
        path: ~/.nuget/packages
        # Look to see if there is a cache hit for the corresponding requirements file
        key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
        restore-keys: |
          ${{ runner.os }}-nuget

    - name: Find solutions
      shell: bash
      run: echo "solutions=$(find ./dotnet -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV

    - name: Restore dependencies
      shell: bash
      run: |
        for solution in ${{ env.solutions }}; do
          dotnet restore $solution
        done

    - name: Build
      shell: bash
      run: |
        for solution in ${{ env.solutions }}; do
          dotnet build $solution --no-restore --configuration ${{ matrix.configuration }}
        done

    - name: Find test projects
      shell: bash
      run: echo "testprojects=$(find ./dotnet -type f -name "*Tests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV

    - name: Run Tests
      shell: bash
      run: |
        for project in ${{ env.testprojects }}; do
          dotnet test $project --verbosity normal --logger trx --results-directory ./TestResults --configuration ${{ matrix.configuration }}
        done

    - name: Upload dotnet test results
      uses: actions/upload-artifact@v4
      with:
        name: dotnet-testresults-${{ matrix.configuration }}
        path: ./TestResults
      if: ${{ always() }}
dotnet-format matrix .github/workflows/dotnet-format.yml
Triggers
workflow_dispatch, pull_request
Runs on
${{ matrix.os }}
Jobs
check-format
Matrix
include, include.configuration, include.dotnet, include.os→ 10.0, Release, ubuntu-latest
Actions
jitterbit/get-changed-files
Commands
  • echo "No C# files changed"
  • csproj_files=() exclude_files=("Experimental.Orchestration.Flow.csproj" "Experimental.Orchestration.Flow.UnitTests.csproj" "Experimental.Orchestration.Flow.IntegrationTests.csproj") if [[ ${{ steps.changed-files.outcome }} == 'success' ]]; then for file in ${{ steps.changed-files.outputs.added_modified }}; do echo "$file was changed" dir="./$file" while [[ $dir != "." && $dir != "/" && $dir != $GITHUB_WORKSPACE ]]; do if find "$dir" -maxdepth 1 -name "*.csproj" -print -quit | grep -q .; then csproj_path="$(find "$dir" -maxdepth 1 -name "*.csproj" -print -quit)" if [[ ! "${exclude_files[@]}" =~ "${csproj_path##*/}" ]]; then csproj_files+=("$csproj_path") fi break fi dir=$(echo ${dir%/*}) done done else # if the changed-files step failed, run dotnet on the whole slnx instead of specific projects csproj_files=$(find ./ -type f -name "*.slnx" | tr '\n' ' '); fi csproj_files=($(printf "%s\n" "${csproj_files[@]}" | sort -u)) echo "Found ${#csproj_files[@]} unique csproj/slnx files: ${csproj_files[*]}" echo "::set-output name=csproj_files::${csproj_files[*]}"
  • docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}
  • for csproj in ${{ steps.find-csproj.outputs.csproj_files }}; do echo "Running dotnet format on $csproj" docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet format $csproj --verify-no-changes --verbosity diagnostic" done
View raw YAML
#
# This workflow runs the dotnet formatter on all c-sharp code.
#

name: dotnet-format

on:
  workflow_dispatch:
  pull_request:
    branches: ["main", "feature*"]
    paths:
      - "dotnet/**"
      - "samples/dotnet/**"
      - "**.cs"
      - "**.csproj"
      - "**.editorconfig"

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  check-format:
    strategy:
      fail-fast: false
      matrix:
        include:
          - { dotnet: "10.0", configuration: Release, os: ubuntu-latest }

    runs-on: ${{ matrix.os }}
    env:
      NUGET_CERT_REVOCATION_MODE: offline

    steps:
      - name: Check out code
        uses: actions/checkout@v5
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Get changed files
        id: changed-files
        if: github.event_name == 'pull_request'
        uses: jitterbit/get-changed-files@v1
        continue-on-error: true

      - name: No C# files changed
        id: no-csharp
        if: github.event_name == 'pull_request' && steps.changed-files.outputs.added_modified == ''
        run: echo "No C# files changed"

      # This step will loop over the changed files and find the nearest .csproj file for each one, then store the unique csproj files in a variable
      - name: Find csproj files
        id: find-csproj
        if: github.event_name != 'pull_request' || steps.changed-files.outputs.added_modified != '' || steps.changed-files.outcome == 'failure'
        run: |
          csproj_files=()
          exclude_files=("Experimental.Orchestration.Flow.csproj" "Experimental.Orchestration.Flow.UnitTests.csproj" "Experimental.Orchestration.Flow.IntegrationTests.csproj")
          if [[ ${{ steps.changed-files.outcome }} == 'success' ]]; then
            for file in ${{ steps.changed-files.outputs.added_modified }}; do
              echo "$file was changed"
              dir="./$file"
              while [[ $dir != "." && $dir != "/" && $dir != $GITHUB_WORKSPACE ]]; do
                if find "$dir" -maxdepth 1 -name "*.csproj" -print -quit | grep -q .; then
                  csproj_path="$(find "$dir" -maxdepth 1 -name "*.csproj" -print -quit)"
                  if [[ ! "${exclude_files[@]}" =~ "${csproj_path##*/}" ]]; then
                    csproj_files+=("$csproj_path")
                  fi
                  break
                fi

                dir=$(echo ${dir%/*})
              done
            done
          else
            # if the changed-files step failed, run dotnet on the whole slnx instead of specific projects
            csproj_files=$(find ./ -type f -name "*.slnx" | tr '\n' ' ');
          fi
          csproj_files=($(printf "%s\n" "${csproj_files[@]}" | sort -u))
          echo "Found ${#csproj_files[@]} unique csproj/slnx files: ${csproj_files[*]}"
          echo "::set-output name=csproj_files::${csproj_files[*]}"

      - name: Pull container dotnet/sdk:${{ matrix.dotnet }}
        if: steps.find-csproj.outputs.csproj_files != ''
        run: docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}

      # This step will run dotnet format on each of the unique csproj files and fail if any changes are made
      - name: Run dotnet format
        if: steps.find-csproj.outputs.csproj_files != ''
        run: |
          for csproj in ${{ steps.find-csproj.outputs.csproj_files }}; do
            echo "Running dotnet format on $csproj"
            docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet format $csproj --verify-no-changes --verbosity diagnostic"
          done
dotnet-integration-tests matrix perms .github/workflows/dotnet-integration-tests.yml
Triggers
workflow_dispatch, pull_request, merge_group
Runs on
${{ matrix.os }}
Jobs
integration-tests
Matrix
configuration, os→ Debug, ubuntu-latest
Commands
  • echo "projects=$(find ./dotnet -type f -name "*Tests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV
  • for project in ${{ env.projects }}; do dotnet test $project --verbosity normal --logger trx --results-directory ./TestResults --configuration ${{ matrix.configuration }} done
View raw YAML
#
# This workflow will run all dotnet integrations tests.
#

name: dotnet-integration-tests

on:
  workflow_dispatch:
  pull_request:
    branches: ["main"]
  merge_group:
    branches: ["main"]

permissions:
  contents: read

jobs:
  integration-tests:
    strategy:
      matrix:
        os: [ubuntu-latest]
        configuration: [Debug]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v5
        if: ${{ github.event_name != 'pull_request' }}
        with:
          clean: true
          persist-credentials: false

      - name: Setup .NET
        uses: actions/setup-dotnet@v5
        if: ${{ github.event_name != 'pull_request' }}
        with:
          dotnet-version: 10.0.x

      - name: Find projects
        shell: bash
        if: ${{ github.event_name != 'pull_request' }}
        run: echo "projects=$(find ./dotnet -type f -name "*Tests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV

      - name: Integration Tests
        shell: bash
        if: ${{ github.event_name != 'pull_request' }}
        env: # Set Azure credentials secret as an input
          AzureOpenAI__Label: azure-text-davinci-003
          AzureOpenAIEmbedding__Label: azure-text-embedding-ada-002
          AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
          AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }}
          AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
          AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
          AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
          AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
          AzureAI__ConnectionString: ${{ secrets.AZUREAI__CONNECTIONSTRING }}
          Bing__ApiKey: ${{ secrets.BING__APIKEY }}
          OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
        run: |
          for project in ${{ env.projects }}; do
            dotnet test $project --verbosity normal --logger trx --results-directory ./TestResults --configuration ${{ matrix.configuration }}
          done

      - name: Upload dotnet test results
        uses: actions/upload-artifact@v4
        with:
          name: dotnet-testresults-${{ matrix.configuration }}
          path: ./TestResults
        if: ${{ github.event_name != 'pull_request' && always() }}
label-discussions .github/workflows/label-discussions.yml
Triggers
discussion
Runs on
ubuntu-latest
Jobs
label_discussions
Actions
octokit/graphql-action, octokit/graphql-action, octokit/graphql-action
View raw YAML
name: Label Discussions

on:
  discussion:
    types:
      - created

jobs:
  label_discussions:
    runs-on: ubuntu-latest
    permissions:
      discussions: write
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - name: Always add "triage" label
        if: >
          github.event.discussion.node_id != '' &&
          (github.event.discussion.category.name == 'General' ||
           github.event.discussion.category.name == 'Ideas' ||
           github.event.discussion.category.name == 'Q&A')
        uses: octokit/graphql-action@v2.x
        with:
          query: |
            mutation($labelableId: ID!) {
              addLabelsToLabelable(
                input: {
                  labelableId: $labelableId
                  labelIds: ["LA_kwDOJDJ_Yc8AAAABU2klmQ"]
                }
              ) {
                clientMutationId
              }
            }
          variables: |
            {
              "labelableId": "${{ github.event.discussion.node_id }}"
            }

      - name: Conditionally add "python" label
        if: >
          github.event.discussion.node_id != '' &&
          (github.event.discussion.category.name == 'General' ||
           github.event.discussion.category.name == 'Ideas' ||
           github.event.discussion.category.name == 'Q&A') &&
          (contains(github.event.discussion.body, 'python') ||
           contains(github.event.discussion.body, 'Python') ||
           contains(github.event.discussion.title, 'python') ||
           contains(github.event.discussion.title, 'Python'))
        uses: octokit/graphql-action@v2.x
        with:
          query: |
            mutation($labelableId: ID!) {
              addLabelsToLabelable(
                input: {
                  labelableId: $labelableId
                  labelIds: ["LA_kwDOJDJ_Yc8AAAABO0f2Lg"]
                }
              ) {
                clientMutationId
              }
            }
          variables: |
            {
              "labelableId": "${{ github.event.discussion.node_id }}"
            }

      - name: Conditionally add ".NET" label
        if: >
          github.event.discussion.node_id != '' &&
          (github.event.discussion.category.name == 'General' ||
           github.event.discussion.category.name == 'Ideas' ||
           github.event.discussion.category.name == 'Q&A') &&
          (contains(github.event.discussion.body, '.net') ||
           contains(github.event.discussion.body, '.NET') ||
           contains(github.event.discussion.title, '.net') ||
           contains(github.event.discussion.title, '.NET') ||
           contains(github.event.discussion.body, 'dotnet') ||
           contains(github.event.discussion.body, 'DotNet') ||
           contains(github.event.discussion.title, 'dotnet') ||
           contains(github.event.discussion.title, 'DotNet') ||
           contains(github.event.discussion.body, 'c#') ||
           contains(github.event.discussion.body, 'C#') ||
           contains(github.event.discussion.title, 'c#') ||
           contains(github.event.discussion.title, 'C#') ||
           contains(github.event.discussion.body, 'csharp') ||
           contains(github.event.discussion.body, 'CSharp') ||
           contains(github.event.discussion.title, 'csharp') ||
           contains(github.event.discussion.title, 'CSharp'))
        uses: octokit/graphql-action@v2.x
        with:
          query: |
            mutation($labelableId: ID!) {
              addLabelsToLabelable(
                input: {
                  labelableId: $labelableId
                  labelIds: ["LA_kwDOJDJ_Yc8AAAABN_r7Lw"]
                }
              ) {
                clientMutationId
              }
            }
          variables: |
            {
              "labelableId": "${{ github.event.discussion.node_id }}"
            }
label-issues .github/workflows/label-issues.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
label_issues
View raw YAML
name: Label issues
on:
  issues:
    types:
      - reopened
      - opened

jobs:
  label_issues:
    name: "Issue: add labels"
    if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
    steps:
      - uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
          script: |
            // Get the issue body and title
            const body = context.payload.issue.body
            let title = context.payload.issue.title
            
            // Define the labels array
            let labels = ["triage"]
            
            // Check if the body or the title contains the word 'python' (case-insensitive)
            if ((body != null && body.match(/python/i)) || (title != null && title.match(/python/i))) {
              // Add the 'python' label to the array
              labels.push("python")
            }
            
            // Check if the body or the title contains the word 'java' (case-insensitive)
            if ((body != null && body.match(/java/i)) || (title != null && title.match(/java/i))) {
              // Add the 'java' label to the array
              labels.push("java")
            }
            
            // Check if the body or the title contains the words 'dotnet', '.net', 'c#' or 'csharp' (case-insensitive)
            if ((body != null && body.match(/.net/i)) || (title != null && title.match(/.net/i)) ||
                (body != null && body.match(/dotnet/i)) || (title != null && title.match(/dotnet/i)) ||
                (body != null && body.match(/C#/i)) || (title != null && title.match(/C#/i)) ||
                (body != null && body.match(/csharp/i)) || (title != null && title.match(/csharp/i))) {
              // Add the '.NET' label to the array
              labels.push(".NET")
            }

            // Add the labels to the issue
            github.rest.issues.addLabels({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: labels
            });
label-pr .github/workflows/label-pr.yml
Triggers
pull_request_target
Runs on
ubuntu-latest
Jobs
add_label
Actions
actions/labeler
View raw YAML
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# To use this workflow, you will need to set up a .github/labeler.yml
# file with configuration.  For more information, see:
# https://github.com/actions/labeler

name: Label pull request
on: [pull_request_target]

jobs:
  add_label:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
      - uses: actions/labeler@v4
        with:
          repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}"
label-title-prefix .github/workflows/label-title-prefix.yml
Triggers
issues, pull_request_target
Runs on
ubuntu-latest
Jobs
add_title_prefix
View raw YAML
name: Label title prefix
on:
  issues:
    types: [labeled]
  pull_request_target:
    types: [labeled]

jobs:
  add_title_prefix:
    name: "Issue/PR: add title prefix"
    continue-on-error: true
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write

    steps:
      - uses: actions/github-script@v6
        name: "Issue/PR: update title"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            let prefixLabels = {
              "python": "Python",
              "java": "Java",
              ".NET": ".Net"
            };

            function addTitlePrefix(title, prefix)
            {
              // Update the title based on the label and prefix
              // Check if the title starts with the prefix (case-sensitive)
              if (!title.startsWith(prefix + ": ")) {
                // If not, check if the first word is the label (case-insensitive)
                if (title.match(new RegExp(`^${prefix}`, 'i'))) {
                  // If yes, replace it with the prefix (case-sensitive)
                  title = title.replace(new RegExp(`^${prefix}`, 'i'), prefix);
                } else {
                  // If not, prepend the prefix to the title
                  title = prefix + ": " + title;
                }
              }

              return title;
            }

            labelAdded = context.payload.label.name

            // Check if the issue or PR has the label
            if (labelAdded in prefixLabels) {
              let prefix = prefixLabels[labelAdded];
              switch(context.eventName) {
                case 'issues':
                  github.rest.issues.update({
                    issue_number: context.issue.number,
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    title: addTitlePrefix(context.payload.issue.title, prefix)
                  });
                  break

                case 'pull_request_target':
                  github.rest.pulls.update({
                    pull_number: context.issue.number,
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    title: addTitlePrefix(context.payload.pull_request.title, prefix)
                  });
                  break
                default:
                  core.setFailed('Unrecognited eventName: ' + context.eventName);
              }
            }
markdown-link-check perms .github/workflows/markdown-link-check.yml
Triggers
workflow_dispatch, pull_request
Runs on
ubuntu-22.04
Jobs
markdown-link-check
Actions
umbrelladocs/action-linkspector
View raw YAML
name: Check .md links

on:
  workflow_dispatch:
  pull_request:
    branches: ["main"]

permissions:
  contents: read

jobs:
  markdown-link-check:
    runs-on: ubuntu-22.04
    # check out the latest version of the code
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false

      # Checks the status of hyperlinks in all files
      - name: Run linkspector
        uses: umbrelladocs/action-linkspector@v1
        with:
          reporter: local
          filter_mode: nofilter
          fail_on_error: true
          config_file: ".github/.linkspector.yml"
merge-gatekeeper .github/workflows/merge-gatekeeper.yml
Triggers
pull_request, merge_group
Runs on
ubuntu-latest
Jobs
merge-gatekeeper
Actions
upsidr/merge-gatekeeper
View raw YAML
name: Merge Gatekeeper

on:
  pull_request:
    branches: [ "main", "feature*" ]
  merge_group:
    branches: ["main"]

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  merge-gatekeeper:
    runs-on: ubuntu-latest
    # Restrict permissions of the GITHUB_TOKEN.
    # Docs: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
    permissions:
      checks: read
      statuses: read
    steps:
      - name: Run Merge Gatekeeper
        # NOTE: v1 is updated to reflect the latest v1.x.y. Please use any tag/branch that suits your needs:
        #       https://github.com/upsidr/merge-gatekeeper/tags
        #       https://github.com/upsidr/merge-gatekeeper/branches
        uses: upsidr/merge-gatekeeper@v1
        if: github.event_name == 'pull_request'
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          timeout: 3600
          interval: 30
          ignored: python-tests-coverage
python-build perms .github/workflows/python-build.yml
Triggers
release
Runs on
ubuntu-latest
Jobs
python-build-assets
Actions
astral-sh/setup-uv, softprops/action-gh-release, azure/login, azure/cli
Commands
  • echo "Building and uploading Python package version: ${{ github.event.release.tag_name }}"
  • cd python && make build
View raw YAML
name: Python Build Assets

on:
  release:
    types: [published]

permissions:
  contents: write
  id-token: write

jobs:
  python-build-assets:
    if: github.event_name == 'release' && startsWith(github.event.release.tag_name, 'python-')
    name: Python Build Assets and add to Release
    runs-on: ubuntu-latest
    environment: "integration"
    env:
      UV_PYTHON: "3.10"
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Check version
        run: |
          echo "Building and uploading Python package version: ${{ github.event.release.tag_name }}"
      - name: Build the package
        run: cd python && make build
      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            python/dist/*
      - name: Azure Login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Start DevOps pipeline
        uses: azure/cli@v2
        with:
          inlineScript: |
            az pipelines run --id ${{ vars.ADO_PYTHON_RELEASE_ID }} --org ${{ vars.ADO_ORG }} --project ${{ vars.ADO_PROJECT_NAME }} --parameters tag=${{ github.event.release.tag_name }} delay=0
python-integration-tests matrix perms .github/workflows/python-integration-tests.yml
Triggers
workflow_dispatch, pull_request, merge_group, schedule
Runs on
ubuntu-latest, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ubuntu-latest
Jobs
paths-filter, python-merge-gate-ai-services, python-merge-gate-multi-modality, python-merge-gate-agents, python-merge-gate-ollama, python-merge-gate-memory, python-integration-tests, python-integration-tests-check
Matrix
os, python-version→ 3.10, 3.11, 3.12, macos-latest, ubuntu-latest, windows-latest
Actions
dorny/paths-filter, astral-sh/setup-uv, google-github-actions/auth, google-github-actions/setup-gcloud, aws-actions/configure-aws-credentials, azure/login, astral-sh/setup-uv, azure/login, astral-sh/setup-uv, aws-actions/configure-aws-credentials, azure/login, astral-sh/setup-uv, astral-sh/setup-uv, azure/login, astral-sh/setup-uv, google-github-actions/auth, google-github-actions/setup-gcloud, aws-actions/configure-aws-credentials, azure/login, skitionek/notify-microsoft-teams, skitionek/notify-microsoft-teams
Commands
  • echo "Python file"
  • echo "NOT python file"
  • uv sync --all-extras --dev
  • uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/completions ./tests/integration/embeddings ./tests/samples ./tests/integration/cross_language
  • uv sync --all-extras --dev
  • uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/audio_to_text ./tests/integration/text_to_audio ./tests/integration/text_to_image
  • uv sync --all-extras --dev
  • uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/agents
View raw YAML
#
# This workflow will run all python integrations tests.
#

name: Python Integration Tests

on:
  workflow_dispatch:
  pull_request:
    branches: ["main"]
  merge_group:
    branches: ["main"]
  schedule:
    - cron: "0 0 * * *" # Run at midnight UTC daily

permissions:
  contents: read
  id-token: "write"

env:
  # Configure a constant location for the uv cache
  UV_CACHE_DIR: /tmp/.uv-cache
  Python_Integration_Tests: Python_Integration_Tests
  INTEGRATION_TEST_SERVICE_SETUP_EXCEPTION: ${{ true }}
  AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }} # azure-text-embedding-ada-002
  AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_CHAT_DEPLOYMENT_NAME }}
  AZURE_OPENAI_TEXT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_DEPLOYMENT_NAME }}
  AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME }}
  AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME }}
  AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME }}
  AZURE_OPENAI_API_VERSION: ${{ vars.AZURE_OPENAI_API_VERSION }}
  AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
  AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT: ${{ secrets.AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT }}
  AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT: ${{ secrets.AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT }}
  AZURE_AI_AGENT_ENDPOINT: ${{ secrets.AZURE_AI_AGENT_ENDPOINT }}
  AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME: ${{ secrets.AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME }}
  AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME }}
  BING_API_KEY: ${{ secrets.BING_API_KEY }}
  OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI_RESPONSES_MODEL_ID }}
  OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI_CHAT_MODEL_ID }}
  OPENAI_TEXT_MODEL_ID: ${{ vars.OPENAI_TEXT_MODEL_ID }}
  OPENAI_EMBEDDING_MODEL_ID: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
  OPENAI_AUDIO_TO_TEXT_MODEL_ID: ${{ vars.OPENAI_AUDIO_TO_TEXT_MODEL_ID }}
  OPENAI_TEXT_TO_AUDIO_MODEL_ID: ${{ vars.OPENAI_TEXT_TO_AUDIO_MODEL_ID }}
  OPENAI_TEXT_TO_IMAGE_MODEL_ID: ${{ vars.OPENAI_TEXT_TO_IMAGE_MODEL_ID }}
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  PINECONE_API_KEY: ${{ secrets.PINECONE__APIKEY }}
  POSTGRES_CONNECTION_STRING: ${{secrets.POSTGRES__CONNECTIONSTR}}
  POSTGRES_MAX_POOL: ${{ vars.POSTGRES_MAX_POOL }}
  AZURE_AI_SEARCH_API_KEY: ${{secrets.AZURE_AI_SEARCH_API_KEY}}
  AZURE_AI_SEARCH_ENDPOINT: ${{secrets.AZURE_AI_SEARCH_ENDPOINT}}
  MONGODB_ATLAS_CONNECTION_STRING: ${{secrets.MONGODB_ATLAS_CONNECTION_STRING}}
  AZURE_KEY_VAULT_ENDPOINT: ${{secrets.AZURE_KEY_VAULT_ENDPOINT}}
  AZURE_KEY_VAULT_CLIENT_ID: ${{secrets.AZURE_KEY_VAULT_CLIENT_ID}}
  AZURE_KEY_VAULT_CLIENT_SECRET: ${{secrets.AZURE_KEY_VAULT_CLIENT_SECRET}}
  ACA_POOL_MANAGEMENT_ENDPOINT: ${{secrets.ACA_POOL_MANAGEMENT_ENDPOINT}}
  MISTRALAI_API_KEY: ${{secrets.MISTRALAI_API_KEY}}
  MISTRALAI_CHAT_MODEL_ID: ${{ vars.MISTRALAI_CHAT_MODEL_ID }}
  MISTRALAI_EMBEDDING_MODEL_ID: ${{ vars.MISTRALAI_EMBEDDING_MODEL_ID }}
  ANTHROPIC_API_KEY: ${{secrets.ANTHROPIC_API_KEY}}
  ANTHROPIC_CHAT_MODEL_ID: ${{ vars.ANTHROPIC_CHAT_MODEL_ID }}
  OLLAMA_CHAT_MODEL_ID: "${{ vars.OLLAMA_CHAT_MODEL_ID || '' }}" # llama3.2:1b
  OLLAMA_CHAT_MODEL_ID_IMAGE: "${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE || '' }}" # moondream
  OLLAMA_CHAT_MODEL_ID_TOOL_CALL: "${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL || '' }}" # llama3.2:1b
  OLLAMA_TEXT_MODEL_ID: "${{ vars.OLLAMA_TEXT_MODEL_ID || '' }}" # llama3.2:1b
  OLLAMA_EMBEDDING_MODEL_ID: "${{ vars.OLLAMA_EMBEDDING_MODEL_ID || '' }}" # nomic-embed-text
  GOOGLE_AI_GEMINI_MODEL_ID: ${{ vars.GOOGLE_AI_GEMINI_MODEL_ID }}
  GOOGLE_AI_EMBEDDING_MODEL_ID: ${{ vars.GOOGLE_AI_EMBEDDING_MODEL_ID }}
  GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
  GOOGLE_AI_CLOUD_PROJECT_ID: ${{ vars.GOOGLE_AI_CLOUD_PROJECT_ID }}
  GOOGLE_AI_CLOUD_REGION: ${{ vars.GOOGLE_AI_CLOUD_REGION }}
  VERTEX_AI_PROJECT_ID: ${{ vars.VERTEX_AI_PROJECT_ID }}
  VERTEX_AI_GEMINI_MODEL_ID: ${{ vars.VERTEX_AI_GEMINI_MODEL_ID }}
  VERTEX_AI_EMBEDDING_MODEL_ID: ${{ vars.VERTEX_AI_EMBEDDING_MODEL_ID }}
  REDIS_CONNECTION_STRING: ${{ vars.REDIS_CONNECTION_STRING }}
  AZURE_COSMOS_DB_NO_SQL_URL: ${{ vars.AZURE_COSMOS_DB_NO_SQL_URL }}
  AZURE_COSMOS_DB_NO_SQL_KEY: ${{ secrets.AZURE_COSMOS_DB_NO_SQL_KEY }}
  BEDROCK_AGENT_AGENT_RESOURCE_ROLE_ARN: ${{ secrets.BEDROCK_AGENT_AGENT_RESOURCE_ROLE_ARN }}
  BEDROCK_AGENT_FOUNDATION_MODEL: ${{ vars.BEDROCK_AGENT_FOUNDATION_MODEL }}

jobs:
  paths-filter:
    runs-on: ubuntu-latest
    outputs:
      pythonChanges: ${{ steps.filter.outputs.python}}
    steps:
      - uses: actions/checkout@v5
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            python:
              - 'python/**'
      # run only if 'python' files were changed
      - name: python tests
        if: steps.filter.outputs.python == 'true'
        run: echo "Python file"
      # run only if not 'python' files were changed
      - name: not python tests
        if: steps.filter.outputs.python != 'true'
        run: echo "NOT python file"

  python-merge-gate-ai-services:
    name: Python Pre-Merge Integration Tests - AI Services (incl samples using those)
    needs: paths-filter
    if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: [ubuntu-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    environment: "integration"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
      COMPLETIONS_CONCEPT_SAMPLE: "true"
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Google auth
        uses: google-github-actions/auth@v3
        with:
          project_id: ${{ vars.VERTEX_AI_PROJECT_ID }}
          credentials_json: ${{ secrets.VERTEX_AI_SERVICE_ACCOUNT_KEY }}
      - name: Set up gcloud
        uses: google-github-actions/setup-gcloud@v3
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v5
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ vars.AWS_REGION }}
      - name: Azure CLI Login
        if: github.event_name != 'pull_request'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Run Integration Tests
        id: run_tests_ai_services
        shell: bash
        run: |
          uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/completions ./tests/integration/embeddings ./tests/samples ./tests/integration/cross_language

  python-merge-gate-multi-modality:
    name: Python Pre-Merge Integration Tests - Multi-Modality
    needs: paths-filter
    if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: [ubuntu-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    environment: "integration"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Azure CLI Login
        if: github.event_name != 'pull_request'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Run Integration Tests
        id: run_tests_multi_modality
        shell: bash
        run: |
          uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/audio_to_text ./tests/integration/text_to_audio ./tests/integration/text_to_image

  python-merge-gate-agents:
    name: Python Pre-Merge Integration Tests - Agents
    needs: paths-filter
    if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: [ubuntu-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    environment: "integration"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v5
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ vars.AWS_REGION }}
      - name: Azure CLI Login
        if: github.event_name != 'pull_request'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Run Integration Tests
        id: run_tests_agents
        shell: bash
        run: |
          uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/agents

  python-merge-gate-ollama:
    name: Python Pre-Merge Integration Tests - Ollama
    needs: paths-filter
    # Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, this job is disabled for now.
    if: false && github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: [ubuntu-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    environment: "integration"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
      COMPLETIONS_CONCEPT_SAMPLE: "true"
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Install Ollama
        if: matrix.os == 'ubuntu-latest'
        run: |
          curl -fsSL https://ollama.com/install.sh | sh
          ollama serve &
          sleep 5
      - name: Pull model in Ollama
        if: matrix.os == 'ubuntu-latest'
        run: |
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID }}
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE }}
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL }}
          ollama pull ${{ vars.OLLAMA_TEXT_MODEL_ID }}
          ollama pull ${{ vars.OLLAMA_EMBEDDING_MODEL_ID }}
          ollama list
      - name: Run Integration Tests
        id: run_tests_ai_services
        shell: bash
        run: |
          uv run pytest -v --log-cli-level=INFO --durations=0 -n logical --dist loadfile --dist worksteal -m ollama --timeout=300 ./tests/integration/completions ./tests/integration/embeddings

  python-merge-gate-memory:
    name: Python Pre-Merge Integration Tests - Memory (incl samples using those)
    needs: paths-filter
    if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.11"]
        os: [ubuntu-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    environment: "integration"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
      MEMORY_CONCEPT_SAMPLE: "true"
    # Service containers to run with for the memory connectors, this only works on Ubuntu
    services:
      # Label used to access the service container
      redis:
        # Docker Hub image
        image: redis/redis-stack-server:latest
        ports:
          # Opens tcp port 6379 on the host and service container
          - 6379:6379
      weaviate:
        image: cr.weaviate.io/semitechnologies/weaviate:1.33.3
        ports:
          - 8080:8080
          - 50051:50051
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Azure CLI Login
        if: github.event_name != 'pull_request'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Run Integration Tests
        id: run_tests_memory
        timeout-minutes: 15
        shell: bash
        run: |
          uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/memory ./tests/samples

  python-integration-tests:
    name: Python Integration Tests - Scheduled run
    needs: paths-filter
    if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && needs.paths-filter.outputs.pythonChanges == 'true'
    strategy:
      max-parallel: 1
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
        os: [ubuntu-latest, windows-latest, macos-latest]
    defaults:
      run:
        working-directory: python
    runs-on: ${{ matrix.os }}
    env:
      UV_PYTHON: ${{ matrix.python-version }}
      MEMORY_CONCEPT_SAMPLE: "true"
      COMPLETIONS_CONCEPT_SAMPLE: "true"
    # Service containers to run with for the memory connectors, this only works on Ubuntu
    services:
      # Label used to access the service container
      redis:
        # Docker Hub image
        image: redis/redis-stack-server:latest
        ports:
          # Opens tcp port 6379 on the host and service container
          - 6379:6379
      weaviate:
        image: cr.weaviate.io/semitechnologies/weaviate:1.26.6
        ports:
          - 8080:8080
          - 50051:50051
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install dependencies
        run: |
          uv sync --all-extras --dev
      - name: Install Ollama
        if: matrix.os == 'ubuntu-latest'
        run: |
          curl -fsSL https://ollama.com/install.sh | sh
          ollama serve &
          sleep 5
      - name: Pull model in Ollama
        # Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
        if: false && matrix.os == 'ubuntu-latest'
        run: |
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID }}
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE }}
          ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL }}
          ollama pull ${{ vars.OLLAMA_TEXT_MODEL_ID }}
          ollama pull ${{ vars.OLLAMA_EMBEDDING_MODEL_ID }}
          ollama list
      - name: Google auth
        uses: google-github-actions/auth@v3
        with:
          project_id: ${{ vars.VERTEX_AI_PROJECT_ID }}
          credentials_json: ${{ secrets.VERTEX_AI_SERVICE_ACCOUNT_KEY }}
      - name: Set up gcloud
        uses: google-github-actions/setup-gcloud@v3
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v5
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ vars.AWS_REGION }}
      - name: Start Azure Cosmos DB emulator
        if: matrix.os == 'windows-latest'
        run: |
          Write-Host "Launching Cosmos DB Emulator"
          Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
          Start-CosmosDbEmulator
      - name: Azure CLI Login
        if: github.event_name != 'pull_request'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Run Integration Tests - Completions
        id: run_tests_completions
        timeout-minutes: 10
        shell: bash
        # Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/completions
      - name: Run Integration Tests - Embeddings
        id: run_tests_embeddings
        timeout-minutes: 5
        shell: bash
        # Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/embeddings
      - name: Run Integration Tests - Memory
        id: run_tests_memory
        timeout-minutes: 5
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/memory
      - name: Run Integration Tests - Cross Language
        id: run_tests_cross_language
        timeout-minutes: 5
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/cross_language
      - name: Run Integration Tests - Planning
        id: run_tests_planning
        timeout-minutes: 5
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/planning
      - name: Run Integration Tests - Samples
        id: run_tests_samples
        timeout-minutes: 5
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/samples
      - name: Run Integration Tests - Agents
        id: run_tests_agents
        timeout-minutes: 10
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/agents
      - name: Run Integration Tests - Multi-Modality
        id: run_tests_multi_modality
        timeout-minutes: 5
        shell: bash
        run: |
          uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/audio_to_text ./tests/integration/text_to_audio ./tests/integration/text_to_image

  # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed
  python-integration-tests-check:
    if: always()
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 1
      fail-fast: false
    needs:
      [
        python-merge-gate-ai-services,
        python-merge-gate-ollama,
        python-merge-gate-memory,
        python-merge-gate-agents,
        python-merge-gate-multi-modality,
        python-integration-tests,
      ]
    steps:
      - name: Get Date
        shell: bash
        run: |
          echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV"

      - name: Run Type is Daily
        if: ${{ github.event_name == 'schedule' }}
        shell: bash
        run: |
          echo "run_type=Daily" >> "$GITHUB_ENV"

      - name: Run Type is Manual
        if: ${{ github.event_name == 'workflow_dispatch' }}
        shell: bash
        run: |
          echo "run_type=Manual" >> "$GITHUB_ENV"

      - name: Run Type is ${{ github.event_name }}
        if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}}
        shell: bash
        run: |
          echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV"

      - name: Fail workflow if tests failed
        id: check_tests_failed
        if: contains(join(needs.*.result, ','), 'failure')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Failed!')

      - name: Fail workflow if tests cancelled
        id: check_tests_cancelled
        if: contains(join(needs.*.result, ','), 'cancelled')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Cancelled!')

      - name: Microsoft Teams Notification
        uses: skitionek/notify-microsoft-teams@v1.0.9
        if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
        with:
          webhook_url: ${{ secrets.MSTEAMS_WEBHOOK }}
          dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}}
          job: ${{ toJson(job) }}
          steps: ${{ toJson(steps) }}
          title: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}"

      - name: Microsoft Teams Notification (Dry Run)
        uses: skitionek/notify-microsoft-teams@v1.0.9
        if: github.ref != 'refs/heads/main'
        with:
          webhook_url: NONE
          dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}}
          job: ${{ toJson(job) }}
          steps: ${{ toJson(steps) }}
          title: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}"
python-lint matrix .github/workflows/python-lint.yml
Triggers
workflow_dispatch, pull_request
Runs on
ubuntu-latest
Jobs
pre-commit
Matrix
python-version→ 3.10
Actions
astral-sh/setup-uv, pre-commit/action
Commands
  • uv sync --all-extras --dev
  • uv run mypy -p semantic_kernel --config-file mypy.ini
View raw YAML
name: Python Code Quality Checks
on:
  workflow_dispatch:
  pull_request:
    branches: ["main", "feature*"]
    paths:
      - "python/**"

jobs:
  pre-commit:
    if: "!cancelled()"
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10"]
    runs-on: ubuntu-latest
    continue-on-error: true
    defaults:
      run:
        working-directory: python
    env:
      # Configure a constant location for the uv cache
      UV_CACHE_DIR: /tmp/.uv-cache
      UV_PYTHON: ${{ matrix.python-version }}
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install the project
        run: uv sync --all-extras --dev
      - uses: pre-commit/action@v3.0.1
        name: Run Pre-Commit Hooks
        with:
          extra_args: --config python/.pre-commit-config.yaml --all-files
      - name: Run Mypy
        run: uv run mypy -p semantic_kernel --config-file mypy.ini
python-manual-release perms .github/workflows/python-manual-release.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
python-start-ado-release-job
Actions
azure/login, azure/cli
View raw YAML
name: Python Start Release on ADO

on:
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release"
        required: true

permissions:
  contents: read
  id-token: write

jobs:
  python-start-ado-release-job:
    name: Trigger ADO Pipeline for Python Release
    runs-on: ubuntu-latest
    environment: "integration"
    steps:
      - name: Azure Login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Start DevOps pipeline
        uses: azure/cli@v2
        with:
          inlineScript: |
            az pipelines run --id ${{ vars.ADO_PYTHON_RELEASE_ID }} --org ${{ vars.ADO_ORG }} --project ${{ vars.ADO_PROJECT_NAME }}  --parameters tag=${{ inputs.tag }} delay=0
python-test-coverage .github/workflows/python-test-coverage.yml
Triggers
pull_request
Runs on
ubuntu-latest
Jobs
python-tests-coverage
Actions
astral-sh/setup-uv
Commands
  • echo ${{ github.event.number }} > ./pr_number
  • uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
  • uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered --cov-report=xml:python-coverage.xml ./tests/unit --ignore=./tests/unit/processes/dapr_runtime
View raw YAML
name: Python Test Coverage

on:
  pull_request:
    branches: ["main", "feature*"]
    paths:
      - "python/semantic_kernel/**"
      - "python/tests/unit/**"
env:
  # Configure a constant location for the uv cache
  UV_CACHE_DIR: /tmp/.uv-cache

jobs:
  python-tests-coverage:
    runs-on: ubuntu-latest
    continue-on-error: false
    defaults:
      run:
        working-directory: python
    env:
      UV_PYTHON: "3.10"
    steps:
      - uses: actions/checkout@v5
      # Save the PR number to a file since the workflow_run event
      # in the coverage report workflow does not have access to it
      - name: Save PR number
        run: |
          echo ${{ github.event.number }} > ./pr_number
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install the project
        run: uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
      - name: Test with pytest
        run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered --cov-report=xml:python-coverage.xml ./tests/unit --ignore=./tests/unit/processes/dapr_runtime
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          path: |
            python/python-coverage.xml
            python/pytest.xml
            python/pr_number
          overwrite: true
          retention-days: 1
          if-no-files-found: error
python-test-coverage-report perms .github/workflows/python-test-coverage-report.yml
Triggers
workflow_run
Runs on
ubuntu-latest
Jobs
python-test-coverage-report
Actions
MishaKav/pytest-coverage-comment
Commands
  • ls
  • PR_NUMBER=$(cat pr_number) echo "PR number: $PR_NUMBER" echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
View raw YAML
name: Python Test Coverage Report

on:
  workflow_run:
    workflows: ["Python Test Coverage"]
    types:
      - completed

permissions:
  contents: read
  pull-requests: write

jobs:
  python-test-coverage-report:
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion == 'success'
    continue-on-error: false
    defaults:
      run:
        working-directory: python
    steps:
      - uses: actions/checkout@v5
      - name: Download coverage report
        uses: actions/download-artifact@v5
        with:
          github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
          run-id: ${{ github.event.workflow_run.id }}
          path: ./python
          merge-multiple: true
      - name: Display structure of downloaded files
        run: ls
      - name: Read and set PR number
        # Need to read the PR number from the file saved in the previous workflow
        # because the workflow_run event does not have access to the PR number
        # The PR number is needed to post the comment on the PR
        run: |
          PR_NUMBER=$(cat pr_number)
          echo "PR number: $PR_NUMBER"
          echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
      - name: Pytest coverage comment
        id: coverageComment
        uses: MishaKav/pytest-coverage-comment@v1.1.57
        with:
          github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
          issue-number: ${{ env.PR_NUMBER }}
          pytest-xml-coverage-path: python/python-coverage.xml
          title: "Python Test Coverage Report"
          badge-title: "Python Test Coverage"
          junitxml-title: "Python Unit Test Overview"
          junitxml-path: python/pytest.xml
          default-branch: "main"
          report-only-changed-files: true
python-unit-tests matrix .github/workflows/python-unit-tests.yml
Triggers
pull_request
Runs on
${{ matrix.os }}
Jobs
python-unit-tests
Matrix
exclude, exclude.os, exclude.python-version, experimental, include, include.experimental, include.os, include.python-version, include.test-suite, os, python-version, test-suite→ 3.10, 3.11, 3.12, 3.13, False, True, dapr, macos-latest, ubuntu-latest, unit-all-except-dapr, windows-latest
Actions
astral-sh/setup-uv, pmeier/pytest-results-action
Commands
  • uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
  • uv sync --extra pandas --dev -U --prerelease=if-necessary-or-explicit && uv pip install "dapr>=1.14.0" "dapr-ext-fastapi>=1.14.0" "flask-dapr>=1.14.0"
  • uv run --frozen pytest --junitxml=pytest.xml ./tests/unit --ignore=tests/unit/processes/dapr_runtime
  • uv run --frozen pytest --junitxml=pytest-dapr.xml ./tests/unit/processes/dapr_runtime
View raw YAML
name: Python Unit Tests

on:
  pull_request:
    branches: ["main", "feature*"]
    paths:
      - "python/**"
env:
  # Configure a constant location for the uv cache
  UV_CACHE_DIR: /tmp/.uv-cache

jobs:
  python-unit-tests:
    name: Python Unit Tests
    runs-on: ${{ matrix.os }}
    continue-on-error: ${{ matrix.experimental }}
    strategy:
      fail-fast: true
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
        os: [ubuntu-latest, windows-latest, macos-latest]
        experimental: [false]
        test-suite: ["unit-all-except-dapr", "dapr"]
        exclude:
          - python-version: "3.10"
            os: macos-latest
          - python-version: "3.11"
            os: macos-latest
        include:
          - python-version: "3.13"
            os: "ubuntu-latest"
            experimental: true
            test-suite: "unit-all-except-dapr"
    env:
      UV_PYTHON: ${{ matrix.python-version }}
    permissions:
      contents: write
    defaults:
      run:
        working-directory: python
    steps:
      - uses: actions/checkout@v5
      - name: Set up uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.5.x"
          enable-cache: true
          cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
          cache-dependency-glob: "**/uv.lock"
      - name: Install the project (all extras)
        if: matrix.test-suite == 'unit-all-except-dapr'
        run: uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
      - name: Install the project (dapr tests)
        if: matrix.test-suite == 'dapr'
        run: uv sync --extra pandas --dev -U --prerelease=if-necessary-or-explicit && uv pip install "dapr>=1.14.0" "dapr-ext-fastapi>=1.14.0" "flask-dapr>=1.14.0"
      - name: Test with pytest (all except dapr)
        if: matrix.test-suite == 'unit-all-except-dapr'
        env:
          PYTHON_GIL: ${{ matrix.gil }}
        run: uv run --frozen pytest --junitxml=pytest.xml ./tests/unit --ignore=tests/unit/processes/dapr_runtime
      - name: Test dapr with pytest
        if: matrix.test-suite == 'dapr'
        env:
          PYTHON_GIL: ${{ matrix.gil }}
        run: uv run --frozen pytest --junitxml=pytest-dapr.xml ./tests/unit/processes/dapr_runtime
      - name: Surface failing tests
        if: ${{ !matrix.experimental && matrix.test-suite == 'unit-all-except-dapr' }}
        uses: pmeier/pytest-results-action@v0.7.2
        with:
          path: python/pytest.xml
          summary: true
          display-options: fEX
          fail-on-empty: true
          title: Test results
typos .github/workflows/typos.yaml
Triggers
workflow_dispatch, pull_request
Runs on
ubuntu-latest
Jobs
run
Actions
crate-ci/typos
View raw YAML
# Check pull requests for typos.
#
# Configuration: .github/_typos.toml
#
# Info:          https://github.com/marketplace/actions/typos-action
# Local install: brew install typos-cli
# Local install: conda install typos
# Local run:     typos -c .github/_typos.toml

name: Spell Check

on:
  workflow_dispatch:
  pull_request:
    branches: ["main", "feature*"]

jobs:
  run:
    name: Spell Check with Typos
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v5
        with:
          persist-credentials: false

      - name: Use custom config file
        uses: crate-ci/typos@v1.36.2
        with:
          config: .github/_typos.toml
          write_changes: false