Fix weekly all-TFM UA.slnx build (net8/net9 + netstandard) and add Windows/Linux all-TFM CI build jobs #7857
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
| name: Build and Test .NET 10.0 | |
| on: | |
| push: | |
| branches: [ master, main, master378, develop/* ] | |
| pull_request: | |
| branches: [ master, main, master378, develop/* ] | |
| paths: | |
| - '**.cs' | |
| - '**.csproj' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| discover: | |
| name: Discover test projects | |
| runs-on: ubuntu-latest | |
| outputs: | |
| csprojs: ${{ steps.list.outputs.csprojs }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Enumerate Tests/Opc.Ua.*.Tests projects | |
| id: list | |
| shell: pwsh | |
| run: | | |
| # Discover test projects from the filesystem so this matrix does | |
| # not have to be hand-maintained as projects are added/renamed. | |
| # Projects whose name is in EXCLUDE are run by other workflows | |
| # (AOT has its own workflow; the Stress suite is opt-in via the | |
| # stress-test workflow). | |
| $exclude = @('Aot', 'Stress') | |
| $projects = Get-ChildItem -Path Tests -Directory -Filter 'Opc.Ua.*.Tests' | | |
| Where-Object { Test-Path (Join-Path $_.FullName "$($_.Name).csproj") } | | |
| ForEach-Object { $_.Name -replace '^Opc\.Ua\.', '' -replace '\.Tests$', '' } | | |
| Where-Object { $exclude -notcontains $_ } | | |
| Sort-Object | |
| $json = ConvertTo-Json -InputObject @($projects) -Compress | |
| Write-Host "Discovered $(@($projects).Count) project(s): $json" | |
| "csprojs=$json" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| build-and-test: | |
| needs: discover | |
| name: test-${{matrix.os}}-${{matrix.csproj}} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # os: [ubuntu-latest, windows-latest, macOS-latest] - disable mac os due to cost | |
| os: [ubuntu-latest, windows-latest] | |
| csproj: ${{ fromJSON(needs.discover.outputs.csprojs) }} | |
| include: | |
| - framework: 'net10.0' | |
| dotnet-version: '10.0.x' | |
| configuration: 'Release' | |
| customtesttarget: net10.0 | |
| env: | |
| OS: ${{ matrix.os }} | |
| DOTNET_VERSION: ${{ matrix.dotnet-version }} | |
| CONFIGURATION: ${{ matrix.configuration }} | |
| CSPROJ: ${{ matrix.csproj }} | |
| CSPROJECT: "./Tests/Opc.Ua.${{ matrix.csproj }}.Tests/Opc.Ua.${{ matrix.csproj }}.Tests.csproj" | |
| TESTRESULTS: "TestResults-${{matrix.csproj}}-${{matrix.os}}-${{matrix.framework}}-${{matrix.configuration}}" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET ${{ matrix.dotnet-version }} | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ matrix.dotnet-version }} | |
| - name: Set Cloud Version | |
| shell: pwsh | |
| run: ./.azurepipelines/set-version.ps1 | |
| - name: Build | |
| run: dotnet build ${{ env.CSPROJECT }} --framework ${{ matrix.framework }} --configuration ${{ matrix.configuration }} /p:CustomTestTarget=${{ matrix.customtesttarget }} /p:UseSharedCompilation=false -maxcpucount:1 | |
| - name: Test | |
| # note: /p:CollectCoverage=true is only used to disable deterministic builds | |
| shell: pwsh | |
| run: | | |
| $testArgs = @( | |
| '${{ env.CSPROJECT }}', | |
| '--no-build', | |
| '--framework', | |
| '${{ matrix.framework }}', | |
| '--logger', | |
| 'trx', | |
| '--configuration', | |
| '${{ matrix.configuration }}', | |
| '/p:CollectCoverage=true', | |
| '/p:CustomTestTarget=${{ matrix.customtesttarget }}', | |
| '--collect:XPlat Code Coverage', | |
| '--settings', | |
| './Tests/coverlet.runsettings.xml', | |
| '--results-directory', | |
| '${{ env.TESTRESULTS }}') | |
| if ('${{ matrix.csproj }}' -eq 'Channels.Stress') | |
| { | |
| $testArgs += @('--filter', '(Category=Contract|Category=Integration)&Category!=ChaosTCP&Category!=Soak') | |
| } | |
| dotnet test @testArgs | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: dotnet-results-${{matrix.csproj}}-${{matrix.os}}-${{matrix.framework}}-${{matrix.configuration}} | |
| path: ${{ env.TESTRESULTS }} | |
| # Use always() to always run this step to publish test results when there are test failures | |
| if: ${{ always() }} | |
| - name: Upload to Codecov | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| name: codecov-umbrella | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| directory: ${{ env.TESTRESULTS }} | |
| env_vars: CSPROJ,OS,DOTNET_VERSION,CONFIGURATION | |
| fail_ci_if_error: false | |
| #path_to_write_report: "./coverage/codecov_report-${{matrix.os}}-${{matrix.framework}}-${{matrix.configuration}}/" | |
| verbose: true | |
| # Use always() to always run this step to publish test results when there are test failures | |
| if: ${{ always() }} | |
| aot-test: | |
| name: aot-${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| include: | |
| - dotnet-version: '10.0.x' | |
| configuration: 'Release' | |
| env: | |
| AOTPROJECT: './Tests/Opc.Ua.Aot.Tests/Opc.Ua.Aot.Tests.csproj' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET ${{ matrix.dotnet-version }} | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ matrix.dotnet-version }} | |
| - name: Set Cloud Version | |
| shell: pwsh | |
| run: ./.azurepipelines/set-version.ps1 | |
| - name: Publish NativeAOT | |
| run: dotnet publish ${{ env.AOTPROJECT }} --configuration ${{ matrix.configuration }} | |
| - name: Run NativeAOT Tests (Linux) | |
| if: runner.os == 'Linux' | |
| run: ./Tests/Opc.Ua.Aot.Tests/bin/${{ matrix.configuration }}/net10.0/linux-x64/publish/Opc.Ua.Aot.Tests | |
| - name: Run NativeAOT Tests (Windows) | |
| if: runner.os == 'Windows' | |
| run: ./Tests/Opc.Ua.Aot.Tests/bin/${{ matrix.configuration }}/net10.0/win-x64/publish/Opc.Ua.Aot.Tests.exe | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: aot-results-${{ matrix.os }} | |
| path: '**/TestResults/**' | |
| if: ${{ always() }} | |
| - name: Upload HTML Test Report | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: TestReport-aot-${{ matrix.os }} | |
| path: '**/*-report.html' | |
| if: ${{ always() }} | |
| # Full UA.slnx build across every target framework, in parallel with the unit | |
| # tests. Each TFM is built by pinning the solution to it via CustomTestTarget | |
| # (the same mechanism the Azure full build uses). All TFMs are attempted so a | |
| # single failure reports every broken TFM; the job fails if any TFM fails. | |
| build-all-tfm-windows: | |
| name: build-windows-all-tfm | |
| runs-on: windows-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: | | |
| 8.0.x | |
| 9.0.x | |
| 10.0.x | |
| - name: Set Cloud Version | |
| shell: pwsh | |
| run: ./.azurepipelines/set-version.ps1 | |
| - name: Build UA.slnx for all target frameworks | |
| shell: pwsh | |
| run: | | |
| $tfms = @('net472', 'net48', 'netstandard2.0', 'netstandard2.1', 'net8.0', 'net9.0', 'net10.0') | |
| $failed = @() | |
| foreach ($tfm in $tfms) { | |
| Write-Host "::group::Build UA.slnx ($tfm)" | |
| dotnet build UA.slnx --configuration Release /p:CustomTestTarget=$tfm /p:UseSharedCompilation=false | |
| if ($LASTEXITCODE -ne 0) { | |
| $failed += $tfm | |
| Write-Host "::error::UA.slnx build failed for $tfm" | |
| } | |
| Write-Host "::endgroup::" | |
| } | |
| if ($failed.Count -gt 0) { | |
| Write-Host "UA.slnx build failed for: $($failed -join ', ')" | |
| exit 1 | |
| } | |
| Write-Host "UA.slnx built successfully for all target frameworks." | |
| build-all-tfm-linux: | |
| name: build-linux-all-tfm | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: | | |
| 8.0.x | |
| 9.0.x | |
| 10.0.x | |
| - name: Set Cloud Version | |
| shell: pwsh | |
| run: ./.azurepipelines/set-version.ps1 | |
| - name: Build UA.slnx for all Linux-capable target frameworks | |
| shell: pwsh | |
| run: | | |
| # net472/net48/netstandard2.0 (which the build maps onto net48) are .NET | |
| # Framework targets and are not built on Linux. | |
| $tfms = @('net8.0', 'net9.0', 'net10.0', 'netstandard2.1') | |
| $failed = @() | |
| foreach ($tfm in $tfms) { | |
| Write-Host "::group::Build UA.slnx ($tfm)" | |
| dotnet build UA.slnx --configuration Release /p:CustomTestTarget=$tfm /p:UseSharedCompilation=false | |
| if ($LASTEXITCODE -ne 0) { | |
| $failed += $tfm | |
| Write-Host "::error::UA.slnx build failed for $tfm" | |
| } | |
| Write-Host "::endgroup::" | |
| } | |
| if ($failed.Count -gt 0) { | |
| Write-Host "UA.slnx build failed for: $($failed -join ', ')" | |
| exit 1 | |
| } | |
| Write-Host "UA.slnx built successfully for all target frameworks." |