dotnet-build-and-test #3
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"] | |
| merge_group: | |
| branches: ["main"] | |
| push: | |
| branches: ["main"] | |
| schedule: | |
| - cron: "0 0 * * *" # Run at midnight UTC daily | |
| 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 | |
| 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: "net9.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" } | |
| - { targetFramework: "net9.0", os: "ubuntu-latest", configuration: Debug } | |
| - { targetFramework: "net9.0", os: "windows-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/setup-dotnet@v5.0.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" | |
| - name: Run Unit Tests Windows | |
| 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 | |
| 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 | |
| echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" | |
| fi | |
| done | |
| - 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 }} | |
| - 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: | |
| # OpenAI Models | |
| OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }} | |
| OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }} | |
| OpenAI__ChatReasoningModelId: ${{ vars.OPENAI__CHATREASONINGMODELID }} | |
| # 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 | |
| uses: danielpalme/ReportGenerator-GitHub-Action@5.4.18 | |
| with: | |
| reports: "./TestResults/Coverage/**/coverage.cobertura.xml" | |
| targetdir: "./TestResults/Reports" | |
| reporttypes: "HtmlInline;JsonSummary" | |
| - name: Upload coverage report artifact | |
| 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 | |
| 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!') |