From 76bdc5311e1d8890ea222e33319317920cc52954 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Sat, 24 Oct 2026 20:15:31 +0200 Subject: [PATCH 1/2] ci: run the macOS MAUI integration tests in their own job On macOS the MAUI cases in cli.Tests.ps1 build three Release apps, for Android, iOS and Mac Catalyst. That takes 8 to 10 of the macOS leg's 13 to 15 integration test minutes, and the macOS leg is the slowest leg in the build workflow. The cases move unchanged to cli-maui.Tests.ps1. The macOS leg skips them, and a new job on another Mac packs the six packages they use and runs them. The other legs still run the Android case. The new job's packages aren't the ones the macOS leg publishes. But CI_PUBLISHING_BUILD only adds the other desktop platforms' native libraries and sentry-cli binaries, which MAUI apps don't use. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 62 ++++++++++++ integration-test/cli-maui.Tests.ps1 | 151 ++++++++++++++++++++++++++++ integration-test/cli.Tests.ps1 | 147 --------------------------- 3 files changed, 213 insertions(+), 147 deletions(-) create mode 100644 integration-test/cli-maui.Tests.ps1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3b3ad321b..07db997c47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -322,6 +322,8 @@ jobs: # TODO: Re-enable once we have resolved https://github.com/getsentry/sentry-dotnet/issues/4788 if: ${{ (matrix.rid != 'linux-musl-x64') }} uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 + env: + SKIP_MAUI_TESTS: ${{ runner.os == 'macOS' }} with: path: integration-test @@ -336,6 +338,66 @@ jobs: # with: # path: integration-test + # The MAUI cases build three Release apps, which takes 8-10 minutes on macOS, so they run next to the macOS leg + # against packages built here. CI_PUBLISHING_BUILD only adds desktop native libraries, which MAUI apps don't use. + maui-integration-test: + needs: build-sentry-native + name: MAUI integration test (macos) + runs-on: xcode-27 + + steps: + - name: Cancel Previous Runs + if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') + uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: recursive + + - name: Setup Environment + uses: ./.github/actions/environment + + - name: Download sentry-native (macos) + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: src/Sentry/Platforms/Native/sentry-native + key: sentry-native-macos-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} + fail-on-cache-miss: true + + - name: Get Cocoa SDK cache key + id: cocoa-key + uses: ./.github/actions/cocoa-cache-key + + - name: Restore Cocoa SDK + id: cache-cocoa + if: ${{ !startsWith(github.ref_name, 'release/') }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: modules/sentry-cocoa/Carthage + key: ${{ steps.cocoa-key.outputs.key }} + + - name: Build Native Dependencies + uses: ./.github/actions/buildnative + + - name: Create NuGet Packages + run: | + for project in Sentry.Maui Sentry Sentry.Extensions.Logging Sentry.Bindings.Android Sentry.Bindings.Cocoa Sentry.Android.AssemblyReader; do + dotnet pack "src/$project" -c Release --nologo -v:minimal + done + + - name: Save Cocoa SDK + if: ${{ !cancelled() && steps.cache-cocoa.outputs.cache-primary-key != '' && steps.cache-cocoa.outputs.cache-hit != 'true' && hashFiles('modules/sentry-cocoa/Carthage/.built-from-sha') != '' }} + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: modules/sentry-cocoa/Carthage + key: ${{ steps.cache-cocoa.outputs.cache-primary-key }} + + - name: Integration test + uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 + with: + path: integration-test/cli-maui.Tests.ps1 + ms-build: needs: build-sentry-native name: MSBuild diff --git a/integration-test/cli-maui.Tests.ps1 b/integration-test/cli-maui.Tests.ps1 new file mode 100644 index 0000000000..cc10d089df --- /dev/null +++ b/integration-test/cli-maui.Tests.ps1 @@ -0,0 +1,151 @@ +# This file contains test cases for https://pester.dev/ +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +. $PSScriptRoot/common.ps1 + +Describe 'MAUI ()' -ForEach @( + @{ framework = $previousFramework } +) -Skip:($env:NO_MOBILE -eq "true" -or $env:SKIP_MAUI_TESTS -eq "true") { + BeforeAll { + ResetLocalPackages + RegisterLocalPackage 'Sentry.Android.AssemblyReader' + RegisterLocalPackage 'Sentry.Bindings.Android' + RegisterLocalPackage 'Sentry.Extensions.Logging' + RegisterLocalPackage 'Sentry.Maui' + if ($IsMacOS) + { + RegisterLocalPackage 'Sentry.Bindings.Cocoa' + } + + $name = 'maui-app' + $androidTpv = GetAndroidTpv $framework + $iosTpv = GetIosTpv $framework + + DotnetNew 'maui' $name $framework + + # Workaround for the missing "ios" workload on Linux, see https://github.com/dotnet/maui/pull/18580 + $tfs = $IsMacos ? "$framework-android$androidTpv;$framework-ios$iosTpv;$framework-maccatalyst$iosTpv" : "$framework-android$androidTpv" + (Get-Content $name/$name.csproj) -replace '[^<]+', "$tfs" | Set-Content $name/$name.csproj + + Push-Location $name + try + { + dotnet remove "${name}.csproj" package 'Microsoft.Extensions.Logging.Debug' | ForEach-Object { Write-Host $_ } + if ($LASTEXITCODE -ne 0) + { + throw "Failed to remove package" + } + } + finally + { + Pop-Location + } + + if (Test-Path env:CI) + { + dotnet workload restore $name/$name.csproj | ForEach-Object { Write-Host $_ } + if ($LASTEXITCODE -ne 0) + { + throw "Failed to restore workloads." + } + } + + AddPackageReference $name 'Sentry.Maui' + + # Match on the PDB GUID: IL stripping rewrites the stamp in the bundle's CodeView entries + function UploadedPdbsNotInAppBundle($result, [string] $targetFramework) + { + $shippedGuids = Get-ChildItem "$name/bin/Release/$targetFramework" -Recurse -Directory -Filter "$name.app" ` + | Get-ChildItem -Recurse -Filter '*.dll' ` + | ForEach-Object { + $reader = [System.Reflection.PortableExecutable.PEReader]::new([System.IO.File]::OpenRead($_.FullName)) + try + { + $reader.ReadDebugDirectory() | Where-Object Type -EQ CodeView | ForEach-Object { + "$($reader.ReadCodeViewDebugDirectoryData($_).Guid)" + } + } + finally + { + $reader.Dispose() + } + } + $result.ScriptOutput ` + | Select-String 'json body: (\{.*"debug_id".*\})$' ` + | ForEach-Object { ($_.Matches[0].Groups[1].Value | ConvertFrom-Json -AsHashtable).Values } ` + | Where-Object { $_.name -like '*.pdb' -and $shippedGuids -notcontains $_.debug_id.Substring(0, 36) } ` + | ForEach-Object { $_.name } + } + } + + It "uploads symbols and sources for an Android build" { + $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-android$androidTpv" + Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + '/proguard/a5fb4278-bcb5-4464-8585-d811dc3c3959.txt', + 'libsentry-android.so', + 'libsentry.so', + 'libsentrysupplemental.so', + 'libxamarin-app.so', + 'maui-app.pdb' + ) + $result.ScriptOutput | Should -AnyElementMatch "Found 23 debug information files \(1 with embedded sources\)" + } + + It "uploads symbols and sources for an iOS build" -Skip:(!$IsMacOS) { + $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-ios$iosTpv" + Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'libmono-component-debugger.dylib', + 'libmono-component-diagnostics_tracing.dylib', + 'libmono-component-hot_reload.dylib', + 'libmono-component-marshal-ilgen.dylib', + 'libmonosgen-2.0.dylib', + 'libSystem.Globalization.Native.dylib', + 'libSystem.IO.Compression.Native.dylib', + 'libSystem.Native.dylib', + 'libSystem.Net.Security.Native.dylib', + 'libSystem.Security.Cryptography.Native.Apple.dylib', + 'libxamarin-dotnet-debug.dylib', + 'libxamarin-dotnet.dylib', + 'maui-app', + 'maui-app.pdb', + 'Microsoft.iOS.pdb', + 'Microsoft.Maui.Controls.pdb', + 'Microsoft.Maui.Controls.Xaml.pdb', + 'Microsoft.Maui.Essentials.pdb', + 'Microsoft.Maui.Graphics.pdb', + 'Microsoft.Maui.pdb', + 'SentryObjC' + ) + UploadedPdbsNotInAppBundle $result "$framework-ios$iosTpv" | Should -BeNullOrEmpty + # The specific number of debug information files seems to change with different SDK - so we just check for non-zero + $nonZeroNumberRegex = '[1-9][0-9]*'; + $result.ScriptOutput | Should -AnyElementMatch "Found $nonZeroNumberRegex debug information files \($nonZeroNumberRegex with embedded sources\)" + } + + It "uploads symbols and sources for a Mac Catalyst build" -Skip:(!$IsMacOS) { + $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-maccatalyst$iosTpv" + Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" + $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( + 'libmonosgen-2.0.dylib', + 'libSystem.Globalization.Native.dylib', + 'libSystem.IO.Compression.Native.dylib', + 'libSystem.Native.dylib', + 'libSystem.Net.Security.Native.dylib', + 'libSystem.Security.Cryptography.Native.Apple.dylib', + 'libxamarin-dotnet-debug.dylib', + 'libxamarin-dotnet.dylib', + 'maui-app', + 'maui-app.pdb', + 'Microsoft.MacCatalyst.pdb', + 'Microsoft.Maui.Controls.pdb', + 'Microsoft.Maui.Controls.Xaml.pdb', + 'Microsoft.Maui.Essentials.pdb', + 'Microsoft.Maui.Graphics.pdb', + 'Microsoft.Maui.pdb', + 'SentryObjC' + ) + UploadedPdbsNotInAppBundle $result "$framework-maccatalyst$iosTpv" | Should -BeNullOrEmpty + } +} diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1 index c19d6e79ae..cc7c523410 100644 --- a/integration-test/cli.Tests.ps1 +++ b/integration-test/cli.Tests.ps1 @@ -254,150 +254,3 @@ Describe 'Console apps () - native AOT publish' -ForEach @( $result.UploadedDebugFiles() | Should -BeNullOrEmpty } } - -Describe 'MAUI ()' -ForEach @( - @{ framework = $previousFramework } -) -Skip:($env:NO_MOBILE -eq "true") { - BeforeAll { - ResetLocalPackages - RegisterLocalPackage 'Sentry.Android.AssemblyReader' - RegisterLocalPackage 'Sentry.Bindings.Android' - RegisterLocalPackage 'Sentry.Extensions.Logging' - RegisterLocalPackage 'Sentry.Maui' - if ($IsMacOS) - { - RegisterLocalPackage 'Sentry.Bindings.Cocoa' - } - - $name = 'maui-app' - $androidTpv = GetAndroidTpv $framework - $iosTpv = GetIosTpv $framework - - DotnetNew 'maui' $name $framework - - # Workaround for the missing "ios" workload on Linux, see https://github.com/dotnet/maui/pull/18580 - $tfs = $IsMacos ? "$framework-android$androidTpv;$framework-ios$iosTpv;$framework-maccatalyst$iosTpv" : "$framework-android$androidTpv" - (Get-Content $name/$name.csproj) -replace '[^<]+', "$tfs" | Set-Content $name/$name.csproj - - Push-Location $name - try - { - dotnet remove "${name}.csproj" package 'Microsoft.Extensions.Logging.Debug' | ForEach-Object { Write-Host $_ } - if ($LASTEXITCODE -ne 0) - { - throw "Failed to remove package" - } - } - finally - { - Pop-Location - } - - if (Test-Path env:CI) - { - dotnet workload restore $name/$name.csproj | ForEach-Object { Write-Host $_ } - if ($LASTEXITCODE -ne 0) - { - throw "Failed to restore workloads." - } - } - - AddPackageReference $name 'Sentry.Maui' - - # Match on the PDB GUID: IL stripping rewrites the stamp in the bundle's CodeView entries - function UploadedPdbsNotInAppBundle($result, [string] $targetFramework) - { - $shippedGuids = Get-ChildItem "$name/bin/Release/$targetFramework" -Recurse -Directory -Filter "$name.app" ` - | Get-ChildItem -Recurse -Filter '*.dll' ` - | ForEach-Object { - $reader = [System.Reflection.PortableExecutable.PEReader]::new([System.IO.File]::OpenRead($_.FullName)) - try - { - $reader.ReadDebugDirectory() | Where-Object Type -EQ CodeView | ForEach-Object { - "$($reader.ReadCodeViewDebugDirectoryData($_).Guid)" - } - } - finally - { - $reader.Dispose() - } - } - $result.ScriptOutput ` - | Select-String 'json body: (\{.*"debug_id".*\})$' ` - | ForEach-Object { ($_.Matches[0].Groups[1].Value | ConvertFrom-Json -AsHashtable).Values } ` - | Where-Object { $_.name -like '*.pdb' -and $shippedGuids -notcontains $_.debug_id.Substring(0, 36) } ` - | ForEach-Object { $_.name } - } - } - - It "uploads symbols and sources for an Android build" { - $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-android$androidTpv" - Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" - $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( - '/proguard/a5fb4278-bcb5-4464-8585-d811dc3c3959.txt', - 'libsentry-android.so', - 'libsentry.so', - 'libsentrysupplemental.so', - 'libxamarin-app.so', - 'maui-app.pdb' - ) - $result.ScriptOutput | Should -AnyElementMatch "Found 23 debug information files \(1 with embedded sources\)" - } - - It "uploads symbols and sources for an iOS build" -Skip:(!$IsMacOS) { - $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-ios$iosTpv" - Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" - $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( - 'libmono-component-debugger.dylib', - 'libmono-component-diagnostics_tracing.dylib', - 'libmono-component-hot_reload.dylib', - 'libmono-component-marshal-ilgen.dylib', - 'libmonosgen-2.0.dylib', - 'libSystem.Globalization.Native.dylib', - 'libSystem.IO.Compression.Native.dylib', - 'libSystem.Native.dylib', - 'libSystem.Net.Security.Native.dylib', - 'libSystem.Security.Cryptography.Native.Apple.dylib', - 'libxamarin-dotnet-debug.dylib', - 'libxamarin-dotnet.dylib', - 'maui-app', - 'maui-app.pdb', - 'Microsoft.iOS.pdb', - 'Microsoft.Maui.Controls.pdb', - 'Microsoft.Maui.Controls.Xaml.pdb', - 'Microsoft.Maui.Essentials.pdb', - 'Microsoft.Maui.Graphics.pdb', - 'Microsoft.Maui.pdb', - 'SentryObjC' - ) - UploadedPdbsNotInAppBundle $result "$framework-ios$iosTpv" | Should -BeNullOrEmpty - # The specific number of debug information files seems to change with different SDK - so we just check for non-zero - $nonZeroNumberRegex = '[1-9][0-9]*'; - $result.ScriptOutput | Should -AnyElementMatch "Found $nonZeroNumberRegex debug information files \($nonZeroNumberRegex with embedded sources\)" - } - - It "uploads symbols and sources for a Mac Catalyst build" -Skip:(!$IsMacOS) { - $result = RunDotnetWithSentryCLI 'build' 'maui-app' $True $True "$framework-maccatalyst$iosTpv" - Write-Host "UploadedDebugFiles: $($result.UploadedDebugFiles() | Out-String)" - $result.UploadedDebugFiles() | Sort-Object -Unique | Should -Be @( - 'libmonosgen-2.0.dylib', - 'libSystem.Globalization.Native.dylib', - 'libSystem.IO.Compression.Native.dylib', - 'libSystem.Native.dylib', - 'libSystem.Net.Security.Native.dylib', - 'libSystem.Security.Cryptography.Native.Apple.dylib', - 'libxamarin-dotnet-debug.dylib', - 'libxamarin-dotnet.dylib', - 'maui-app', - 'maui-app.pdb', - 'Microsoft.MacCatalyst.pdb', - 'Microsoft.Maui.Controls.pdb', - 'Microsoft.Maui.Controls.Xaml.pdb', - 'Microsoft.Maui.Essentials.pdb', - 'Microsoft.Maui.Graphics.pdb', - 'Microsoft.Maui.pdb', - 'SentryObjC' - ) - UploadedPdbsNotInAppBundle $result "$framework-maccatalyst$iosTpv" | Should -BeNullOrEmpty - } -} From 9e33f113b81dfd1831c40ebcc2e3aa3a3e98e09a Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Sat, 24 Oct 2026 22:03:15 +0200 Subject: [PATCH 2/2] ci: pack the MAUI job's packages after building them dotnet pack with a build failed with NU5026 on Sentry.Maui's Windows target, looking for a .pri file that isn't generated on macOS. The build legs build first and pack with --no-build, so this job does too. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07db997c47..b9015977c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -344,6 +344,8 @@ jobs: needs: build-sentry-native name: MAUI integration test (macos) runs-on: xcode-27 + env: + MAUI_PACKAGES: Sentry.Maui Sentry Sentry.Extensions.Logging Sentry.Bindings.Android Sentry.Bindings.Cocoa Sentry.Android.AssemblyReader steps: - name: Cancel Previous Runs @@ -380,10 +382,10 @@ jobs: - name: Build Native Dependencies uses: ./.github/actions/buildnative - - name: Create NuGet Packages + - name: Build run: | - for project in Sentry.Maui Sentry Sentry.Extensions.Logging Sentry.Bindings.Android Sentry.Bindings.Cocoa Sentry.Android.AssemblyReader; do - dotnet pack "src/$project" -c Release --nologo -v:minimal + for project in $MAUI_PACKAGES; do + dotnet build "src/$project" -c Release --nologo -v:minimal done - name: Save Cocoa SDK @@ -393,6 +395,14 @@ jobs: path: modules/sentry-cocoa/Carthage key: ${{ steps.cache-cocoa.outputs.cache-primary-key }} + # Like the build legs, pack the build outputs. Packing Sentry.Maui while building it on macOS fails with NU5026, + # looking for the Windows target's .pri file, which isn't generated on macOS. + - name: Create NuGet Packages + run: | + for project in $MAUI_PACKAGES; do + dotnet pack "src/$project" -c Release --no-build --nologo + done + - name: Integration test uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 with: