.NET: fix: RunState is sometimes Running when it should be Idle #7954
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}} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| dotnet: | |
| - 'dotnet/**' | |
| # 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: | |
| - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" } | |
| - { targetFramework: "net9.0", os: "windows-latest", configuration: Debug } | |
| - { targetFramework: "net8.0", os: "ubuntu-latest", configuration: Release } | |
| - { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" } | |
| runs-on: ${{ matrix.os }} | |
| environment: ${{ matrix.environment }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| . | |
| .github | |
| dotnet | |
| python | |
| workflow-samples | |
| - name: Setup dotnet | |
| uses: actions/[email protected] | |
| 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" | |
| # Start Cosmos DB Emulator for Cosmos-based unit tests (only on Windows) | |
| - name: Start Azure Cosmos DB Emulator | |
| if: runner.os == 'Windows' | |
| 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 "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV | |
| - name: Run Unit Tests | |
| shell: bash | |
| run: | | |
| export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | tr '\n' ' ') | |
| for project in $UT_PROJECTS; do | |
| # Query the project's target frameworks using MSBuild with the current configuration | |
| target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') | |
| # Check if the project supports the target framework | |
| if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then | |
| if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then | |
| dotnet test -f ${{ matrix.targetFramework }} -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 | |
| else | |
| dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx | |
| fi | |
| else | |
| echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" | |
| fi | |
| done | |
| 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: bash | |
| if: github.event_name != 'pull_request' && matrix.integration-tests | |
| run: | | |
| export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | tr '\n' ' ') | |
| for project in $INTEGRATION_TEST_PROJECTS; do | |
| # Query the project's target frameworks using MSBuild with the current configuration | |
| target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') | |
| # Check if the project supports the target framework | |
| if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then | |
| dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx | |
| else | |
| echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" | |
| fi | |
| done | |
| env: | |
| # Cosmos DB Emulator connection settings | |
| COSMOSDB_ENDPOINT: https://localhost:8081 | |
| COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== | |
| # OpenAI Models | |
| OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }} | |
| OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }} | |
| OpenAI__ChatReasoningModelId: ${{ vars.OPENAI__CHATREASONINGMODELID }} | |
| # Azure OpenAI Models | |
| AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} | |
| AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} | |
| # Azure AI Foundry | |
| AzureAI__Endpoint: ${{ secrets.AZUREAI__ENDPOINT }} | |
| AzureAI__DeploymentName: ${{ vars.AZUREAI__DEPLOYMENTNAME }} | |
| AzureAI__BingConnectionId: ${{ vars.AZUREAI__BINGCONECTIONID }} | |
| FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} | |
| FOUNDRY_MEDIA_DEPLOYMENT_NAME: ${{ vars.FOUNDRY_MEDIA_DEPLOYMENT_NAME }} | |
| FOUNDRY_MODEL_DEPLOYMENT_NAME: ${{ vars.FOUNDRY_MODEL_DEPLOYMENT_NAME }} | |
| FOUNDRY_CONNECTION_GROUNDING_TOOL: ${{ vars.FOUNDRY_CONNECTION_GROUNDING_TOOL }} | |
| # Generate test reports and check coverage | |
| - name: Generate test reports | |
| if: matrix.targetFramework == env.COVERAGE_FRAMEWORK | |
| uses: danielpalme/[email protected] | |
| with: | |
| reports: "./TestResults/Coverage/**/coverage.cobertura.xml" | |
| targetdir: "./TestResults/Reports" | |
| reporttypes: "HtmlInline;JsonSummary" | |
| - name: Upload coverage report artifact | |
| if: matrix.targetFramework == env.COVERAGE_FRAMEWORK | |
| uses: actions/upload-artifact@v5 | |
| 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: .github/workflows/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-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@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!') |