From 676a2142444fc17fb1f8711358eb5698f880d341 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Sun, 25 Oct 2026 16:04:12 +0100 Subject: [PATCH 1/3] ci: build the MSBuild job on all cores msbuild.exe builds on one node unless it gets -m, while the dotnet CLI passes -maxcpucount on its own. The MSBuild job's binlog shows 21.4 minutes on one node, 12.4 of them in Build. The win-x64 leg builds the same solution filter with dotnet build in 3 to 4.5 minutes. -nr:false keeps the worker nodes from staying around after the build, since nothing later in the job uses them. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 492828b395..d3f7039c6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,7 @@ jobs: - name: Run MSBuild id: msbuild - run: msbuild Sentry-CI-Build-Windows.slnf -t:Restore,Build,Pack -p:Configuration=Release --nologo -v:minimal -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 - name: Test MSBuild uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1 From 455b3624a7249181b0144eeb953e6bc019b2df16 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Mon, 26 Oct 2026 20:09:37 +0100 Subject: [PATCH 2/3] ci: install .NET and NuGet packages on a faster drive on Windows The Windows .NET legs spent about 12 minutes in Setup Environment and Restore, against about 4.5 on Linux. setup-dotnet installed into C:\Program Files\dotnet, where the SDK installs workloads as MSI packages, and NuGet extracted packages into the user profile on C:. On Windows, Setup Environment now starts by installing .NET and the NuGet cache next to the workspace, where workloads install as plain files. On the x64 runners that's on D:. The arm64 runners have no D:, so there the NuGet packages and TEMP go on a Dev Drive, and .NET stays next to the workspace on C:. Trusting the new drive is retried. If setting it up fails anyway, the step warns and keeps the NuGet packages next to the workspace, because the drive is a speedup. Every Windows job that uses Setup Environment gets it. There ~/.nuget/packages isn't the NuGet cache anymore, so the integration tests now clear the Sentry packages they push from NUGET_PACKAGES when it's set. In two probes of the win-arm64 leg, the job took 33.8 and 32.4 minutes with the Dev Drive, against 37.2 and 35.8 without it, and 36.9 with .NET on the drive too. On its own, the step took the MSBuild job's Run MSBuild from 23-24 to 14.7 minutes. Co-Authored-By: Claude Opus 5.5 --- .github/actions/environment/action.yml | 46 ++++++++++++++++++++++++++ integration-test/common.ps1 | 3 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml index e5cba99e50..26e40ff605 100644 --- a/.github/actions/environment/action.yml +++ b/.github/actions/environment/action.yml @@ -9,6 +9,52 @@ runs: 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 + if: runner.os == 'Windows' + shell: pwsh + run: | + $root = Split-Path $env:GITHUB_WORKSPACE + "DOTNET_INSTALL_DIR=$root\.dotnet" >> $env:GITHUB_ENV + if ($root.StartsWith('C:')) + { + try + { + # 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 + New-Item -ItemType Directory 'X:\tmp' | Out-Null + 'TEMP=X:\tmp' >> $env:GITHUB_ENV + 'TMP=X:\tmp' >> $env:GITHUB_ENV + $root = 'X:' + } + catch + { + # The Dev Drive is a speedup, so the job goes on without it + Write-Warning "Couldn't set up the Dev Drive, so the NuGet packages stay next to the workspace: $_" + } + } + "NUGET_PACKAGES=$root\.nuget\packages" >> $env:GITHUB_ENV + $global:LASTEXITCODE = 0 + - name: Set Global Environment Variables shell: bash run: | diff --git a/integration-test/common.ps1 b/integration-test/common.ps1 index 908a1bda4c..df880b08e9 100644 --- a/integration-test/common.ps1 +++ b/integration-test/common.ps1 @@ -105,7 +105,8 @@ BeforeAll { } # We need to remove the package from cache or it won't re resolved properly - Remove-Item -Path ~/.nuget/packages/$($name.ToLower())/$packageVersion -Recurse -Force -ErrorAction SilentlyContinue + $packagesDir = if ($env:NUGET_PACKAGES) { $env:NUGET_PACKAGES } else { "$HOME/.nuget/packages" } + Remove-Item -Path "$packagesDir/$($name.ToLower())/$packageVersion" -Recurse -Force -ErrorAction SilentlyContinue } function ResetLocalPackages() From 2f76defe71e9c94aa4fc843bd3b55d60a121ad11 Mon Sep 17 00:00:00 2001 From: Ricardo Colombo Oliveira Date: Tue, 27 Oct 2026 09:51:17 +0100 Subject: [PATCH 3/3] ci: restore in its own pass in the MSBuild job The MSBuild job ran msbuild -t:Restore,Build,Pack. NuGet's docs say restoring and building in one invocation is unpredictable and often wrong: restore can bring in props and targets from packages, so the build needs its own evaluation after it. -restore runs the restore as a separate pass first, which is what the dotnet CLI does. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3f7039c6b..6b51c3588b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,7 @@ jobs: - name: Run MSBuild id: msbuild - 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 + run: msbuild Sentry-CI-Build-Windows.slnf -restore -t:Build,Pack -p:Configuration=Release -m -nr:false --nologo -v:minimal -p:CopyLocalLockFileAssemblies=true -bl:msbuild.binlog - name: Test MSBuild uses: getsentry/github-workflows/sentry-cli/integration-test@959162c159b866489d3fabe93327f48f9b8cb7ee # 3.4.1