dotnet-build-and-test #233
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # This workflow will build all .slnx files in the dotnet folder, and run all unit tests and integration 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", "feature*"] | |
| push: | |
| branches: ["main", "feature*"] | |
| schedule: | |
| - cron: "0 0 * * *" # Run at midnight UTC daily | |
| env: | |
| COVERAGE_THRESHOLD: 80 | |
| COVERAGE_FRAMEWORK: net10.0 # framework target for which we run/report code coverage | |
| 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 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| dotnetChanges: ${{ steps.filter.outputs.dotnet }} | |
| cosmosDbChanges: ${{ steps.filter.outputs.cosmosdb }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| dotnet: | |
| - 'dotnet/**' | |
| cosmosdb: | |
| - 'dotnet/src/Microsoft.Agents.AI.CosmosNoSql/**' | |
| # run only if 'dotnet' files were changed | |
| - name: dotnet tests | |
| if: steps.filter.outputs.dotnet == 'true' | |
| run: echo "Dotnet file" | |
| - name: dotnet CosmosDB tests | |
| if: steps.filter.outputs.cosmosdb == 'true' | |
| run: echo "Dotnet CosmosDB changes" | |
| # run only if not 'dotnet' files were changed | |
| - name: not dotnet tests | |
| if: steps.filter.outputs.dotnet != 'true' | |
| run: echo "NOT dotnet file" | |
| # Build the full solution (including samples) on all TFMs. No tests. | |
| dotnet-build: | |
| needs: paths-filter | |
| if: needs.paths-filter.outputs.dotnetChanges == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release } | |
| - { targetFramework: "net9.0", os: "windows-latest", configuration: Debug } | |
| - { targetFramework: "net8.0", os: "ubuntu-latest", configuration: Release } | |
| - { targetFramework: "net472", os: "windows-latest", configuration: Release } | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| . | |
| .github | |
| dotnet | |
| python | |
| workflow-samples | |
| - name: Setup dotnet | |
| uses: actions/setup-dotnet@v5.2.0 | |
| 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 | |
| # All frameworks are only built for the release configuration, so we only run this step for the release configuration | |
| # and dotnet new doesn't support net472 | |
| if: matrix.configuration == 'Release' && matrix.targetFramework != 'net472' | |
| run: | | |
| TEMP_DIR=$(mktemp -d) | |
| export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ') | |
| for solution in $SOLUTIONS; do | |
| dotnet pack $solution /property:TargetFrameworks=${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build --no-restore --output "$TEMP_DIR/artifacts" | |
| done | |
| pushd "$TEMP_DIR" | |
| # Create a new console app to test the package installation | |
| dotnet new console -f ${{ matrix.targetFramework }} --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 | |
| pushd consoleapp | |
| dotnet add packcheck.csproj package Microsoft.Agents.AI --prerelease | |
| dotnet build -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} packcheck.csproj | |
| # Clean up | |
| popd | |
| popd | |
| rm -rf "$TEMP_DIR" | |
| # Build src+tests only (no samples) for a single TFM and run tests. | |
| dotnet-test: | |
| needs: paths-filter | |
| if: needs.paths-filter.outputs.dotnetChanges == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" } | |
| - { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" } | |
| runs-on: ${{ matrix.os }} | |
| environment: ${{ matrix.environment }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| . | |
| .github | |
| dotnet | |
| python | |
| workflow-samples | |
| # Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened) | |
| - name: Start Azure Cosmos DB Emulator | |
| if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }} | |
| shell: pwsh | |
| run: | | |
| Write-Host "Launching Azure Cosmos DB Emulator" | |
| Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator" | |
| Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" | |
| echo "COSMOSDB_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV | |
| - name: Setup dotnet | |
| uses: actions/setup-dotnet@v5.2.0 | |
| with: | |
| global-json-file: ${{ github.workspace }}/dotnet/global.json | |
| - name: Generate test solution (no samples) | |
| shell: pwsh | |
| run: | | |
| ./dotnet/eng/scripts/New-FilteredSolution.ps1 ` | |
| -Solution dotnet/agent-framework-dotnet.slnx ` | |
| -TargetFramework ${{ matrix.targetFramework }} ` | |
| -Configuration ${{ matrix.configuration }} ` | |
| -ExcludeSamples ` | |
| -OutputPath dotnet/filtered.slnx ` | |
| -Verbose | |
| - name: Build src and tests | |
| shell: bash | |
| run: dotnet build dotnet/filtered.slnx -c ${{ matrix.configuration }} -f ${{ matrix.targetFramework }} --warnaserror | |
| - name: Generate test-type filtered solutions | |
| shell: pwsh | |
| run: | | |
| $commonArgs = @{ | |
| Solution = "dotnet/filtered.slnx" | |
| TargetFramework = "${{ matrix.targetFramework }}" | |
| Configuration = "${{ matrix.configuration }}" | |
| Verbose = $true | |
| } | |
| ./dotnet/eng/scripts/New-FilteredSolution.ps1 @commonArgs ` | |
| -TestProjectNameFilter "*UnitTests*" ` | |
| -OutputPath dotnet/filtered-unit.slnx | |
| ./dotnet/eng/scripts/New-FilteredSolution.ps1 @commonArgs ` | |
| -TestProjectNameFilter "*IntegrationTests*" ` | |
| -OutputPath dotnet/filtered-integration.slnx | |
| - name: Run Unit Tests | |
| shell: pwsh | |
| working-directory: dotnet | |
| run: | | |
| $coverageSettings = Join-Path $PWD "tests/coverage.runsettings" | |
| $coverageArgs = @() | |
| if ("${{ matrix.targetFramework }}" -eq "${{ env.COVERAGE_FRAMEWORK }}") { | |
| $coverageArgs = @( | |
| "--coverage", | |
| "--coverage-output-format", "cobertura", | |
| "--coverage-settings", $coverageSettings, | |
| "--results-directory", "../TestResults/Coverage/" | |
| ) | |
| } | |
| dotnet test --solution ./filtered-unit.slnx ` | |
| -f ${{ matrix.targetFramework }} ` | |
| -c ${{ matrix.configuration }} ` | |
| --no-build -v Normal ` | |
| --report-xunit-trx ` | |
| --ignore-exit-code 8 ` | |
| @coverageArgs | |
| env: | |
| # Cosmos DB Emulator connection settings | |
| COSMOSDB_ENDPOINT: https://localhost:8081 | |
| COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== | |
| - name: Log event name and matrix integration-tests | |
| shell: bash | |
| run: echo "github.event_name:${{ github.event_name }} matrix.integration-tests:${{ matrix.integration-tests }} github.event.action:${{ github.event.action }} github.event.pull_request.merged:${{ github.event.pull_request.merged }}" | |
| - 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 }} | |
| # This setup action is required for both Durable Task and Azure Functions integration tests. | |
| # We only run it on Ubuntu since the Durable Task and Azure Functions features are not available | |
| # on .NET Framework (net472) which is what we use the Windows runner for. | |
| - name: Set up Durable Task and Azure Functions Integration Test Emulators | |
| if: github.event_name != 'pull_request' && matrix.integration-tests && matrix.os == 'ubuntu-latest' | |
| uses: ./.github/actions/azure-functions-integration-setup | |
| id: azure-functions-setup | |
| - name: Run Integration Tests | |
| shell: pwsh | |
| working-directory: dotnet | |
| if: github.event_name != 'pull_request' && matrix.integration-tests | |
| run: | | |
| dotnet test --solution ./filtered-integration.slnx ` | |
| -f ${{ matrix.targetFramework }} ` | |
| -c ${{ matrix.configuration }} ` | |
| --no-build -v Normal ` | |
| --report-xunit-trx ` | |
| --ignore-exit-code 8 ` | |
| --filter-not-trait "Category=IntegrationDisabled" ` | |
| --parallel-algorithm aggressive ` | |
| --max-threads 2.0x | |
| env: | |
| # Cosmos DB Emulator connection settings | |
| COSMOSDB_ENDPOINT: https://localhost:8081 | |
| COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== | |
| # OpenAI Models | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| OPENAI_CHAT_MODEL_NAME: ${{ vars.OPENAI_CHAT_MODEL_NAME }} | |
| OPENAI_REASONING_MODEL_NAME: ${{ vars.OPENAI_REASONING_MODEL_NAME }} | |
| # Azure OpenAI Models | |
| AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME }} | |
| AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME }} | |
| AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }} | |
| # Azure AI Foundry | |
| AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }} | |
| AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZURE_AI_MODEL_DEPLOYMENT_NAME }} | |
| AZURE_AI_BING_CONNECTION_ID: ${{ vars.AZURE_AI_BING_CONNECTION_ID }} | |
| # Generate test reports and check coverage | |
| - name: Generate test reports | |
| if: matrix.targetFramework == env.COVERAGE_FRAMEWORK | |
| uses: danielpalme/ReportGenerator-GitHub-Action@5.5.3 | |
| with: | |
| reports: "./TestResults/Coverage/**/*.cobertura.xml" | |
| targetdir: "./TestResults/Reports" | |
| reporttypes: "HtmlInline;JsonSummary" | |
| - name: Upload coverage report artifact | |
| if: matrix.targetFramework == env.COVERAGE_FRAMEWORK | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: CoverageReport-${{ matrix.os }}-${{ matrix.targetFramework }}-${{ matrix.configuration }} # Artifact name | |
| path: ./TestResults/Reports # Directory containing files to upload | |
| - name: Check coverage | |
| if: matrix.targetFramework == env.COVERAGE_FRAMEWORK | |
| shell: pwsh | |
| run: ./dotnet/eng/scripts/dotnet-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, dotnet-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@v8 | |
| 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@v8 | |
| with: | |
| script: core.setFailed('Integration Tests Cancelled!') |