diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index 3754453a45..abe58f1845 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -1,5 +1,10 @@ name: Setup Environment description: Sets env vars and installs dependencies +inputs: + solution-filter: + description: The solution filter the job builds, so only the workloads its projects need are installed. Without one, every project in Sentry.slnx counts. + required: false + default: '' runs: using: composite @@ -39,37 +44,20 @@ runs: # Java 21 is needed by .NET Android - name: Install Java 21 - if: ${{ !matrix.container }} + if: ${{ !matrix.container && env.NO_MOBILE != 'true' }} uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: temurin java-version: '21' - name: Setup Android SDK - if: ${{ !matrix.container }} + if: ${{ !matrix.container && env.NO_MOBILE != 'true' }} uses: android-actions/setup-android@be39fa834029ff78f1a44aa3bb0819b8fc2bd8fd # v4.0.4 with: # Exclude "tools" because the emulator is not needed (nor available for Windows/Linux ARM64) packages: platform-tools platforms;android-35 platforms;android-36 build-tools;36.0.0 log-accepted-android-sdk-licenses: false - - name: Install Mono (macOS) - if: runner.os == 'macOS' - shell: bash - # Attempt to install Mono, allowing it to fail silently - # Then ensure mono is correctly linked, overwriting any conflicting symlinks - run: | - brew install mono || true - brew link --overwrite mono - - - name: Install Mono (Ubuntu) - if: ${{ runner.os == 'Linux' && !matrix.container }} - shell: bash - run: | - sudo apt install -y mono-devel - # restore perms for actions/setup-dotnet - sudo chmod -R a+rw /usr/share/dotnet - - name: Install .NET SDK uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: @@ -90,11 +78,25 @@ runs: run: | set -euo pipefail pwd - dotnet workload restore \ - --temp-dir "${{ runner.temp }}" \ - --skip-sign-check - dotnet workload restore test/AndroidTestApp/AndroidTestApp.csproj \ - --temp-dir "${{ runner.temp }}" \ - --skip-sign-check - # Restore the Android test app explicitly to ensure runtime packs are available - dotnet restore test/AndroidTestApp/AndroidTestApp.csproj -r android-x64 --nologo || true + filter="${{ inputs.solution-filter }}" + if [[ -n "$filter" ]]; then + # dotnet workload restore doesn't accept a solution filter, but it accepts the projects in one + projects=$(grep -o '"[^"]*\.csproj"' "$filter" | tr -d '"' | sed 's#\\\\#/#g' || true) + if [[ -z "$projects" ]]; then + echo "::error::Found no projects in $filter, so there are no workloads to restore" + exit 1 + fi + echo "$projects" | xargs dotnet workload restore --temp-dir "${{ runner.temp }}" --skip-sign-check + else + dotnet workload restore \ + --temp-dir "${{ runner.temp }}" \ + --skip-sign-check + fi + # Sentry.Android.AssemblyReader.Tests builds the Android test app. The Windows solution filters exclude both. + if [[ "$RUNNER_OS" != "Windows" ]] && { [[ -z "$filter" ]] || grep -q 'Sentry.Android.AssemblyReader.Tests' "$filter"; }; then + dotnet workload restore test/AndroidTestApp/AndroidTestApp.csproj \ + --temp-dir "${{ runner.temp }}" \ + --skip-sign-check + # Restore the Android test app explicitly to ensure runtime packs are available + dotnet restore test/AndroidTestApp/AndroidTestApp.csproj -r android-x64 --nologo || true + fi diff --git a/.github/actions/windows-dotnet-drive/action.yml b/.github/actions/windows-dotnet-drive/action.yml new file mode 100644 index 0000000000..1e0a2a1ed3 --- /dev/null +++ b/.github/actions/windows-dotnet-drive/action.yml @@ -0,0 +1,42 @@ +name: Windows .NET drive +description: Installs .NET and the NuGet packages on a fast drive on Windows runners +runs: + using: composite + + steps: + # The Windows images keep .NET under Program Files, where workloads install as MSI packages, and the NuGet cache + # in the user profile on C:. So .NET goes next to the workspace, where workloads install as plain files. The x64 + # runners put the workspace on D:, so the NuGet packages go there too. The arm64 runners only have C:, so the + # NuGet packages and TEMP go on a Dev Drive there. Defender already excludes C:\ on these images. + - name: Install .NET and NuGet packages on a fast drive + shell: pwsh + run: | + $root = Split-Path $env:GITHUB_WORKSPACE + "DOTNET_INSTALL_DIR=$root\.dotnet" >> $env:GITHUB_ENV + if ($root.StartsWith('C:')) + { + # Only volumes mounted after this get no antivirus filters + fsutil devdrv enable /disallowAv | Out-Host + 'create vdisk file="C:\devdrive.vhdx" maximum=81920 type=expandable', 'select vdisk file="C:\devdrive.vhdx"', 'attach vdisk', 'convert gpt', 'create partition primary' | + Set-Content "$env:RUNNER_TEMP\diskpart.txt" + diskpart /s "$env:RUNNER_TEMP\diskpart.txt" | Out-Host + if ($LASTEXITCODE -ne 0) { throw "diskpart failed with exit code $LASTEXITCODE" } + $partition = Get-DiskImage -ImagePath 'C:\devdrive.vhdx' | Get-Disk | Get-Partition | Where-Object Type -eq 'Basic' + $partition | Format-Volume -DevDrive -Confirm:$false -Force | Out-Host + $partition | Add-PartitionAccessPath -AccessPath 'X:\' + # Right after the mount, trust can fail with "Access is denied" while the volume is still in use + for ($attempt = 1; $attempt -le 5; $attempt++) + { + fsutil devdrv trust X: | Out-Host + if ($LASTEXITCODE -eq 0) { break } + Start-Sleep -Seconds 3 + } + if ($LASTEXITCODE -ne 0) { Write-Warning "Couldn't mark X: as a trusted Dev Drive, so antivirus filters may still scan it" } + fsutil devdrv query X: | Out-Host + $root = 'X:' + New-Item -ItemType Directory "$root\tmp" | Out-Null + "TEMP=$root\tmp" >> $env:GITHUB_ENV + "TMP=$root\tmp" >> $env:GITHUB_ENV + } + "NUGET_PACKAGES=$root\.nuget\packages" >> $env:GITHUB_ENV + $global:LASTEXITCODE = 0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3b3ad321b..8e1ae5f719 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,8 +143,14 @@ jobs: if: ${{ !matrix.container }} uses: ./.github/actions/freediskspace + - name: Install .NET and NuGet packages on a fast drive + if: runner.os == 'Windows' + uses: ./.github/actions/windows-dotnet-drive + - name: Setup Environment uses: ./.github/actions/environment + with: + solution-filter: ${{ matrix.slnf }} # We use macOS for the final publishing build so we get all the iOS/macCatalyst targets in the packages - name: Set Environment Variables @@ -234,7 +240,7 @@ jobs: - name: Build id: build - run: dotnet build ${{ matrix.slnf }} -c Release --no-restore --nologo -v:minimal -flp:logfile=build.log -p:CopyLocalLockFileAssemblies=true -bl:build.binlog + run: dotnet build ${{ matrix.slnf }} -c Release --no-restore --nologo -v:minimal -p:CopyLocalLockFileAssemblies=true -bl:build.binlog # Only a completed Cocoa build writes the stamp, so a failed one is never cached. - name: Save Cocoa SDK @@ -249,9 +255,7 @@ jobs: uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: ${{ matrix.rid }}-build-logs - path: | - build.log - build.binlog + path: build.binlog if-no-files-found: ignore - name: Free disk space before tests @@ -269,16 +273,21 @@ jobs: echo "Disk usage after cleanup:" df -h + # The publishing build's sparse checkout below removes the test outputs, so it runs the tests and uploads + # their results first. The other legs run the tests in the background, next to the integration tests, and + # upload the results after waiting for them. - name: Test + if: env.CI_PUBLISHING_BUILD == 'true' run: dotnet test ${{ matrix.slnf }} -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute - name: Upload code coverage + if: env.CI_PUBLISHING_BUILD == 'true' uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 with: token: ${{ secrets.CODECOV_TOKEN }} - name: Upload build and test outputs - if: failure() + if: failure() && env.CI_PUBLISHING_BUILD == 'true' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: ${{ matrix.rid }}-verify-test-results @@ -287,6 +296,14 @@ jobs: - name: Create NuGet Packages run: dotnet pack ${{ matrix.slnf }} -c Release --no-build --nologo + # After packing, because every solution build runs before.Sentry.sln.targets' `git submodule update`, and two + # at once fail on git's config lock + - name: Test + id: background-test + if: env.CI_PUBLISHING_BUILD != 'true' + background: true + run: dotnet test ${{ matrix.slnf }} -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}" --collect "XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute + - name: Archive NuGet Packages if: env.CI_PUBLISHING_BUILD == 'true' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 @@ -320,11 +337,65 @@ jobs: # # Note that when upgrading .NET versions, linux-musl may need special handling... see comments below # if: ${{ (matrix.rid != 'linux-musl-x64') && (matrix.rid != 'linux-musl-arm64') }} # TODO: Re-enable once we have resolved https://github.com/getsentry/sentry-dotnet/issues/4788 - if: ${{ (matrix.rid != 'linux-musl-x64') }} + # The win-arm64 integration tests run in the integration-test job + if: ${{ (matrix.rid != 'linux-musl-x64') && (matrix.rid != 'win-arm64') }} uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 + env: + SKIP_MAUI_TESTS: ${{ runner.os == 'macOS' }} with: path: integration-test + # Sentry turns SentryNative off on runtimes that have no native SDK. aot.Tests.ps1 checks that against this + # leg's own packages: linux-arm on the linux-arm64 runner, and win-x86 on the win-x64 runner. + - name: Install Linux ARM 32-bit dependencies + if: matrix.rid == 'linux-arm64' + run: | + sudo dpkg --add-architecture armhf + sudo apt-get update + sudo apt-get install -y gcc-arm-linux-gnueabihf libc6:armhf + # Prints the machine a 32-bit ARM process sees: aarch64 when the arm64 kernel runs it, armv7l under QEMU + printf '#include \n#include \nint main(void) { struct utsname u; uname(&u); puts(u.machine); return 0; }\n' \ + | arm-linux-gnueabihf-gcc -x c -o "$RUNNER_TEMP/uname-armhf" - + grep -m1 'CPU part' /proc/cpuinfo || true + "$RUNNER_TEMP/uname-armhf" || echo "This host can't run 32-bit ARM binaries natively" + + # Some arm64 hosts can't run 32-bit ARM binaries ("Exec format error"), so QEMU runs them on every host + - name: Set up QEMU for 32-bit ARM + if: matrix.rid == 'linux-arm64' + uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: arm + + - name: Check that QEMU runs 32-bit ARM binaries + if: matrix.rid == 'linux-arm64' + run: | + grep -q '^enabled' /proc/sys/fs/binfmt_misc/qemu-arm + test "$("$RUNNER_TEMP/uname-armhf")" = armv7l + + - name: Test AOT on an unsupported runtime + if: matrix.rid == 'linux-arm64' || matrix.rid == 'win-x64' + uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 + env: + RuntimeIdentifier: ${{ matrix.rid == 'linux-arm64' && 'linux-arm' || 'win-x86' }} + with: + path: integration-test/aot.Tests.ps1 + + - name: Wait for the tests + wait-all: + + - name: Upload code coverage + if: ${{ !cancelled() && steps.background-test.outcome == 'success' }} + uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload build and test outputs + if: failure() && env.CI_PUBLISHING_BUILD != 'true' + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: ${{ matrix.rid }}-verify-test-results + path: "**/*.received.*" + # # For the linux-musl runtimes we have to pin the ContainerBaseImage for preview or RC builds, since # # these don't conform to the normal naming conventions and don't get resolved automatically. We do # # by passing it as parameter to aot.Tests.ps1 via an environment variable @@ -336,6 +407,99 @@ jobs: # with: # path: integration-test + # These run integration tests next to a build leg, against packages built here from only the projects the tests + # use. CI_PUBLISHING_BUILD only adds desktop native libraries, which these tests don't use. + # - macos: the MAUI cases build three Release apps, which takes 8-10 minutes. + # - win-arm64: the runner has 4 cores, which the leg's unit tests keep busy, so every integration test runs here. + integration-test: + needs: build-sentry-native + name: Integration test (${{ matrix.rid }}) + runs-on: ${{ matrix.os }} + env: + PACKAGES: ${{ matrix.packages }} + + strategy: + fail-fast: false + matrix: + include: + - os: xcode-27 + rid: macos + packages: Sentry.Maui Sentry Sentry.Extensions.Logging Sentry.Bindings.Android Sentry.Bindings.Cocoa Sentry.Android.AssemblyReader + tests: integration-test/cli-maui.Tests.ps1 + - os: windows-11-arm + rid: win-arm64 + packages: Sentry.Maui Sentry Sentry.Extensions.Logging Sentry.Bindings.Android Sentry.Android.AssemblyReader + tests: integration-test + + 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: Install .NET and NuGet packages on a fast drive + if: runner.os == 'Windows' + uses: ./.github/actions/windows-dotnet-drive + + - name: Setup Environment + uses: ./.github/actions/environment + + - name: Download sentry-native (${{ matrix.rid }}) + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: src/Sentry/Platforms/Native/sentry-native + key: sentry-native-${{ matrix.rid }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} + fail-on-cache-miss: true + enableCrossOsArchive: true + + - name: Get Cocoa SDK cache key + id: cocoa-key + if: runner.os == 'macOS' + uses: ./.github/actions/cocoa-cache-key + + - name: Restore Cocoa SDK + id: cache-cocoa + if: runner.os == 'macOS' && !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: Build + shell: bash + run: | + for project in $PACKAGES; do + dotnet build "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 }} + + # 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 + shell: bash + run: | + for project in $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: + path: ${{ matrix.tests }} + ms-build: needs: build-sentry-native name: MSBuild @@ -347,6 +511,9 @@ jobs: with: submodules: recursive + - name: Install .NET and NuGet packages on a fast drive + uses: ./.github/actions/windows-dotnet-drive + - name: Download sentry-native (win-x64) uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -366,7 +533,12 @@ jobs: - name: Run MSBuild id: msbuild - run: msbuild Sentry-CI-Build-Windows.slnf -t:Restore,Build,Pack -p:Configuration=Release --nologo -v:minimal -flp:logfile=msbuild.log -p:CopyLocalLockFileAssemblies=true -bl:msbuild.binlog + run: msbuild Sentry-CI-Build-Windows.slnf -t:Restore,Build,Pack -p:Configuration=Release -m -nr:false --nologo -v:minimal -p:CopyLocalLockFileAssemblies=true -bl:msbuild.binlog + + # The app samples build in their own job, with the .NET CLI, so the MAUI sample's Android and Windows targets + # get an MSBuild build here + - name: Build the MAUI sample + run: msbuild samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj -t:Restore,Build -p:Configuration=Release -m -nr:false --nologo -v:minimal -p:CopyLocalLockFileAssemblies=true -bl:msbuild-maui.binlog - name: Test MSBuild uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 @@ -379,61 +551,10 @@ jobs: with: name: ${{ runner.os }}-msbuild-logs path: | - msbuild.log msbuild.binlog + msbuild-maui.binlog if-no-files-found: ignore - # Unsupported Native AOT runtimes should have SentryNative auto-disabled - # to avoid native library loading errors on startup. - unsupported-aot: - needs: build-sentry - name: Unsupported AOT (${{ matrix.rid }}) - runs-on: ${{ matrix.os }} - env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - DOTNET_NOLOGO: 1 - - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-24.04-arm - rid: linux-arm - - os: windows-latest - rid: win-x86 - - # This job is on the critical path, after the whole build-sentry matrix. aot.Tests.ps1 only publishes a - # console app against the packages from the macOS build, so it needs the .NET SDK but not the full - # Setup Environment (Java, Android SDK, Mono, workloads). - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Install Linux ARM 32-bit dependencies - if: ${{ matrix.rid == 'linux-arm' }} - run: | - sudo dpkg --add-architecture armhf - sudo apt-get update - sudo apt-get install -y gcc-arm-linux-gnueabihf libc6:armhf - - - name: Install .NET SDK - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 - with: - global-json-file: global.json - - - name: Fetch NuGet Packages - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 - with: - name: ${{ github.sha }} - path: src - - - name: Test AOT - uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 - env: - RuntimeIdentifier: ${{ matrix.rid }} - with: - path: integration-test/aot.Tests.ps1 - trim-analysis: needs: build-sentry-native name: Trim analysis @@ -475,6 +596,84 @@ jobs: # - name: Publish Test app (iOS) # run: dotnet publish test/Sentry.MauiTrimTest/Sentry.MauiTrimTest.csproj -c Release -f net9.0-ios18.0 -r ios-arm64 + # Nothing consumes the app samples, but their Release builds trim and AOT-compile each app, which made them the + # last minutes of the Build step in the .NET legs. Here they build next to the legs instead of inside them. + build-samples: + needs: build-sentry-native + name: Samples (${{ matrix.rid }}) + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - os: xcode-27 + rid: macos + samples: Sentry-CI-Build-Samples-macOS.slnf + # The only build of the MAUI sample's Windows target + - os: windows-latest + rid: win-x64 + samples: samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj + # The Android and MAUI samples' Android targets on a Linux host, as the linux-x64 leg built them + - os: ubuntu-24.04 + rid: linux-x64 + samples: Sentry-CI-Build-Samples-Linux.slnf + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: recursive + + - name: Setup Environment + uses: ./.github/actions/environment + + - name: Download sentry-native (${{ matrix.rid }}) + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: src/Sentry/Platforms/Native/sentry-native + key: sentry-native-${{ matrix.rid }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} + fail-on-cache-miss: true + enableCrossOsArchive: true + + # See build-sentry + - name: Get Cocoa SDK cache key + id: cocoa-key + if: runner.os == 'macOS' + uses: ./.github/actions/cocoa-cache-key + + - name: Restore Cocoa SDK + id: cache-cocoa + if: runner.os == 'macOS' && !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: Restore .NET Dependencies + run: dotnet restore ${{ matrix.samples }} --nologo + + - name: Build + run: dotnet build ${{ matrix.samples }} -c Release --no-restore --nologo -v:minimal -bl:build.binlog + + - 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: Upload build logs + if: ${{ always() }} + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: samples-${{ matrix.rid }}-build-logs + path: build.binlog + if-no-files-found: ignore + test-solution-filters: runs-on: ubuntu-24.04 if: ${{ !startsWith(github.ref_name, 'release/') }} diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index d59fa75a9f..62756785ce 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -95,17 +95,18 @@ jobs: needs: [build] name: Run Android API-${{ matrix.api-level }} Test (${{ matrix.tfm }}) - # Requires a "larger runner", for nested virtualization support - runs-on: ubuntu-latest-4-cores + # Standard Linux runners support hardware-accelerated emulators since 2024, with the KVM step below: + # https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/ + runs-on: ubuntu-latest - strategy: + strategy: &android-emulator-matrix fail-fast: false matrix: tfm: [net9.0, net10.0] # Must be 34+ for new apps and app updates as of August 31, 2024. # See https://apilevels.com/ api-level: [34, 36] - env: + env: &android-emulator-env DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: 1 # We don't need the Google APIs, but the default images are not available for 32+ @@ -115,6 +116,9 @@ jobs: ANDROID_EMULATOR_DISK_SIZE: 4096M ANDROID_EMULATOR_OPTIONS: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none steps: + - name: Show disk space + run: df -h / + # See https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/ - name: Enable KVM group perms run: | @@ -162,14 +166,42 @@ jobs: uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0 with: *android-emulator-test - - name: Setup Environment - uses: ./.github/actions/environment + - name: Upload results + if: success() || failure() + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results + path: test_output + + - name: Show disk space + if: always() + run: df -h / + + # The integration tests build and run their own app, so they run next to the device tests instead of after them + android-integration-tests: + name: Run Android API-${{ matrix.api-level }} Integration Test (${{ matrix.tfm }}) + + # Standard Linux runners support hardware-accelerated emulators since 2024, with the KVM step below: + # https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/ + runs-on: ubuntu-latest - - name: Select Java 21 - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + strategy: *android-emulator-matrix + env: *android-emulator-env + steps: + # See https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/ + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - distribution: temurin - java-version: '21' + submodules: recursive + + - name: Setup Environment + uses: ./.github/actions/environment - name: Checkout github-workflows uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -204,7 +236,5 @@ jobs: if: success() || failure() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: - name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results - path: | - test_output - integration-test/mobile-app/test_output + name: device-test-android-integration-${{ matrix.api-level }}-${{ matrix.tfm }}-results + path: integration-test/mobile-app/test_output diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index 222f969c52..0c470a1316 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -108,6 +108,52 @@ jobs: timeout-minutes: 40 run: pwsh scripts/device-test.ps1 ios -Run -Verbose + - name: Upload results + if: success() || failure() + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: device-test-ios-results + path: test_output + + # The integration tests build and run their own app, so they run next to the device tests instead of after them + ios-integration-tests: + runs-on: xcode-27 + env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + NO_ANDROID: true + NO_MACCATALYST: true + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: recursive + + - name: Setup Environment + uses: ./.github/actions/environment + + - name: Get Cocoa SDK cache key + id: cocoa-key + uses: ./.github/actions/cocoa-cache-key + + - name: Restore Cocoa SDK + id: cache-cocoa + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: modules/sentry-cocoa/Carthage + key: ${{ steps.cocoa-key.outputs.key }} + + # ios.Tests.ps1 skips every case unless a simulator is already booted. In the ios-tests job, xharness boots it. + - name: Boot the iOS Simulator + timeout-minutes: 10 + shell: pwsh + run: | + . ./scripts/device-test-utils.ps1 + $udid = Get-IosSimulatorUdid -Verbose + if (-not $udid) { throw 'No iOS simulator found' } + xcrun simctl bootstatus $udid -b + if ($LASTEXITCODE -ne 0) { throw "Couldn't boot simulator $udid" } + - name: Run Integration Tests id: first-integration-test-run continue-on-error: true @@ -123,11 +169,16 @@ jobs: with: path: integration-test/ios.Tests.ps1 + - name: Save Cocoa SDK + if: ${{ !cancelled() && 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: Upload results if: success() || failure() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: - name: device-test-ios-results - path: | - test_output - integration-test/mobile-app/test_output + name: device-test-ios-integration-results + path: integration-test/mobile-app/test_output diff --git a/AGENTS.md b/AGENTS.md index c2a8f45d76..6b185d518e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,9 +38,11 @@ Use solution filters instead of the full `Sentry.slnx`: | Filter | Use when | |--------|----------| | `SentryNoMobile.slnf` | General development (no Android/iOS build toolchain needed for quicker builds) | -| `Sentry-CI-Build-macOS.slnf` | All macOS projects | -| `Sentry-CI-Build-Windows.slnf` | All Windows projects | -| `Sentry-CI-Build-Linux.slnf` | All Linux projects | +| `Sentry-CI-Build-macOS.slnf` | All macOS projects except the app samples | +| `Sentry-CI-Build-Windows.slnf` | All Windows projects except the app samples | +| `Sentry-CI-Build-Linux.slnf` | All Linux projects except the app samples | +| `Sentry-CI-Build-Samples-macOS.slnf` | The MAUI, Android, iOS, Mac Catalyst and macOS sample apps | +| `Sentry-CI-Build-Samples-Linux.slnf` | The MAUI and Android sample apps, built on Linux | > Do **not** edit `*.slnf` files directly. They are generated by `scripts/generate-solution-filters.ps1` from `scripts/generate-solution-filters-config.yaml`. diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index b7a4975621..e502cf89f1 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -4,7 +4,6 @@ "projects": [ "benchmarks\\Sentry.Benchmarks\\Sentry.Benchmarks.csproj", "benchmarks\\Sentry.Extensions.Logging.Benchmarks\\Sentry.Extensions.Logging.Benchmarks.csproj", - "samples\\Sentry.Samples.Android\\Sentry.Samples.Android.csproj", "samples\\Sentry.Samples.AspNetCore.Basic\\Sentry.Samples.AspNetCore.Basic.csproj", "samples\\Sentry.Samples.AspNetCore.Blazor.Server\\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "samples\\Sentry.Samples.AspNetCore.Blazor.Wasm\\Sentry.Samples.AspNetCore.Blazor.Wasm.csproj", @@ -25,7 +24,6 @@ "samples\\Sentry.Samples.GraphQL.Server\\Sentry.Samples.GraphQL.Server.csproj", "samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj", "samples\\Sentry.Samples.Log4Net\\Sentry.Samples.Log4Net.csproj", - "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.AI.AspNetCore\\Sentry.Samples.ME.AI.AspNetCore.csproj", "samples\\Sentry.Samples.ME.AI.Console\\Sentry.Samples.ME.AI.Console.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", diff --git a/Sentry-CI-Build-Samples-Linux.slnf b/Sentry-CI-Build-Samples-Linux.slnf new file mode 100644 index 0000000000..9ad22dc7c0 --- /dev/null +++ b/Sentry-CI-Build-Samples-Linux.slnf @@ -0,0 +1,9 @@ +{ + "solution": { + "path": "Sentry.slnx", + "projects": [ + "samples\\Sentry.Samples.Android\\Sentry.Samples.Android.csproj", + "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj" + ] + } +} diff --git a/Sentry-CI-Build-Samples-macOS.slnf b/Sentry-CI-Build-Samples-macOS.slnf new file mode 100644 index 0000000000..ed4fbddcef --- /dev/null +++ b/Sentry-CI-Build-Samples-macOS.slnf @@ -0,0 +1,12 @@ +{ + "solution": { + "path": "Sentry.slnx", + "projects": [ + "samples\\Sentry.Samples.Android\\Sentry.Samples.Android.csproj", + "samples\\Sentry.Samples.Ios\\Sentry.Samples.Ios.csproj", + "samples\\Sentry.Samples.MacCatalyst\\Sentry.Samples.MacCatalyst.csproj", + "samples\\Sentry.Samples.MacOS\\Sentry.Samples.MacOS.csproj", + "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj" + ] + } +} diff --git a/Sentry-CI-Build-Windows-arm64.slnf b/Sentry-CI-Build-Windows-arm64.slnf index f9b1f924e6..63b09f4ada 100644 --- a/Sentry-CI-Build-Windows-arm64.slnf +++ b/Sentry-CI-Build-Windows-arm64.slnf @@ -26,7 +26,6 @@ "samples\\Sentry.Samples.GraphQL.Server\\Sentry.Samples.GraphQL.Server.csproj", "samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj", "samples\\Sentry.Samples.Log4Net\\Sentry.Samples.Log4Net.csproj", - "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.AI.AspNetCore\\Sentry.Samples.ME.AI.AspNetCore.csproj", "samples\\Sentry.Samples.ME.AI.Console\\Sentry.Samples.ME.AI.Console.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index fccd8ebf8e..33b56f3d83 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -26,7 +26,6 @@ "samples\\Sentry.Samples.GraphQL.Server\\Sentry.Samples.GraphQL.Server.csproj", "samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj", "samples\\Sentry.Samples.Log4Net\\Sentry.Samples.Log4Net.csproj", - "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.AI.AspNetCore\\Sentry.Samples.ME.AI.AspNetCore.csproj", "samples\\Sentry.Samples.ME.AI.Console\\Sentry.Samples.ME.AI.Console.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index ce671ebede..4b1980cdaa 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -6,7 +6,6 @@ "benchmarks\\Sentry.Extensions.Logging.Benchmarks\\Sentry.Extensions.Logging.Benchmarks.csproj", "modules\\perfview\\src\\FastSerialization\\FastSerialization.csproj", "modules\\perfview\\src\\TraceEvent\\TraceEvent.csproj", - "samples\\Sentry.Samples.Android\\Sentry.Samples.Android.csproj", "samples\\Sentry.Samples.AspNetCore.Basic\\Sentry.Samples.AspNetCore.Basic.csproj", "samples\\Sentry.Samples.AspNetCore.Blazor.Server\\Sentry.Samples.AspNetCore.Blazor.Server.csproj", "samples\\Sentry.Samples.AspNetCore.Blazor.Wasm\\Sentry.Samples.AspNetCore.Blazor.Wasm.csproj", @@ -26,11 +25,7 @@ "samples\\Sentry.Samples.GraphQL.Client.Http\\Sentry.Samples.GraphQL.Client.Http.csproj", "samples\\Sentry.Samples.GraphQL.Server\\Sentry.Samples.GraphQL.Server.csproj", "samples\\Sentry.Samples.Hangfire\\Sentry.Samples.Hangfire.csproj", - "samples\\Sentry.Samples.Ios\\Sentry.Samples.Ios.csproj", "samples\\Sentry.Samples.Log4Net\\Sentry.Samples.Log4Net.csproj", - "samples\\Sentry.Samples.MacCatalyst\\Sentry.Samples.MacCatalyst.csproj", - "samples\\Sentry.Samples.MacOS\\Sentry.Samples.MacOS.csproj", - "samples\\Sentry.Samples.Maui\\Sentry.Samples.Maui.csproj", "samples\\Sentry.Samples.ME.AI.AspNetCore\\Sentry.Samples.ME.AI.AspNetCore.csproj", "samples\\Sentry.Samples.ME.AI.Console\\Sentry.Samples.ME.AI.Console.csproj", "samples\\Sentry.Samples.ME.Logging\\Sentry.Samples.ME.Logging.csproj", 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 - } -} diff --git a/integration-test/common.ps1 b/integration-test/common.ps1 index d09721ce15..908a1bda4c 100644 --- a/integration-test/common.ps1 +++ b/integration-test/common.ps1 @@ -124,7 +124,7 @@ BeforeAll { Write-Host "::group::${action}ing $project" try { - dotnet $action $project -flp:logfile=build.log ` + dotnet $action $project ` -c Release ` --nologo ` --framework $TargetFramework ` diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml index ea4233d3dc..55a7ef19c9 100644 --- a/scripts/generate-solution-filters-config.yaml +++ b/scripts/generate-solution-filters-config.yaml @@ -39,6 +39,13 @@ groupConfigs: - "**/*Ios*.csproj" - "**/*MacCatalyst*.csproj" - "**/*Maui*.csproj" + # The build workflow builds these in its own job, off the critical path + appSamples: + - "**/Sentry.Samples.Android.csproj" + - "**/Sentry.Samples.Ios.csproj" + - "**/Sentry.Samples.MacCatalyst.csproj" + - "**/Sentry.Samples.MacOS.csproj" + - "**/Sentry.Samples.Maui.csproj" filterConfigs: @@ -48,6 +55,7 @@ filterConfigs: - "allProjects" exclude: groups: + - "appSamples" - "macOnly" - "windowsOnly" - "artefacts" @@ -85,6 +93,7 @@ filterConfigs: - "modules/perfview/**/FastSerialization.csproj" exclude: groups: + - "appSamples" - "artefacts" - "trimTests" - "playwrightTests" @@ -103,6 +112,7 @@ filterConfigs: - "modules/perfview/**/FastSerialization.csproj" exclude: groups: + - "appSamples" - "macOnly" - "artefacts" - "trimTests" @@ -125,6 +135,7 @@ filterConfigs: - "modules/perfview/**/FastSerialization.csproj" exclude: groups: + - "appSamples" - "macOnly" - "artefacts" - "trimTests" @@ -142,6 +153,18 @@ filterConfigs: - "**/*Sentry.DiagnosticSource.Tests.csproj" - "**/*Sentry.EntityFramework.Tests.csproj" + - outputPath: Sentry-CI-Build-Samples-macOS.slnf + include: + groups: + - "appSamples" + + # The app samples the Linux build leg built before they moved to their own job + - outputPath: Sentry-CI-Build-Samples-Linux.slnf + include: + patterns: + - "**/Sentry.Samples.Android.csproj" + - "**/Sentry.Samples.Maui.csproj" + - outputPath: Sentry-CI-CodeQL.slnf include: groups: