Decouple .NET package versions from SDK previews - #36599
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36599Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36599" |
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR updates MAUI’s versioning infrastructure so shipping/source projects can stay pinned to the latest published (N-1) .NET preview packages while the current .NET SDK is still prerelease, without forcing MAUI to rebuild and republish for every SDK preview drop. Tests, benchmarks, and manual/device runners are kept on the current SDK-flowed (N) versions, and templates are adjusted to emit floating * during prerelease and exact versions at GA.
Changes:
- Introduces
*N1*version properties ineng/Versions.propsand addsUseN1PackageVersions-driven selection logic ineng/NuGetVersions.targetsto switch shipping projects to N-1 during prerelease. - Removes hardcoded package versions from several test/runner projects so centralized version selection applies consistently, and explicitly opts those projects into current-N behavior via
UseN1PackageVersions=false. - Updates template packing to (a) always refresh version substitutions and (b) validate expected N/N-1/stable behaviors at pack time; updates cgmanifest generation to record N-1 template dependencies during previews.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/TestUtils/src/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj | Removes hardcoded Microsoft.Extensions.Logging.Console version and opts runner into current-N selection. |
| src/Templates/src/Microsoft.Maui.Templates.csproj | Reworks template version substitution to emit * during prerelease, forces refresh on pack, and adds pack-time MSBuild assertions for version-selection scenarios. |
| src/Core/tests/DeviceTests.Shared/Core.DeviceTests.Shared.csproj | Removes hardcoded Microsoft.Extensions.Logging.Console version so centralized selection applies. |
| src/Core/tests/Benchmarks/Core.Benchmarks.csproj | Opts benchmarks into current-N selection with UseN1PackageVersions=false. |
| src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj | Opts unit tests into current-N selection and removes hardcoded System.CodeDom version. |
| src/Controls/tests/ManualTests/Controls.ManualTests.csproj | Opts manual tests into current-N selection and removes hardcoded Microsoft.Extensions.Logging.Debug version. |
| src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj | Opts unit tests into current-N selection with UseN1PackageVersions=false. |
| src/Controls/src/Build.Tasks/Controls.Build.Tasks.csproj | Removes hardcoded System.CodeDom version so centralized selection applies. |
| src/AI/tests/Essentials.AI.DeviceTests/Essentials.AI.DeviceTests.csproj | Opts AI device tests into current-N selection with UseN1PackageVersions=false. |
| eng/Versions.props | Adds N-1 (published) properties for targeted .NET packages and updates some current-N properties to align with the active preview. |
| eng/scripts/update-cgmanifest.ps1 | Adds N-1-aware mapping so template cgmanifest tracks N-1 dependencies during prerelease. |
| eng/NuGetVersions.targets | Implements UseN1PackageVersions-based switching between N-1 (shipping) and N (tests) while current versions are prerelease, and adds missing AspNetCore “previous edition” updates. |
|
Validated the output of Package/nuspec validationDownloaded the complete
Across the eight packages that carry affected dependency groups, I found 162 matching dependency entries:
Representative entries:
So the produced nuspec dependency pins match the intended shipping N-1 model. CI resultCI is not green. Build, pack, Helix unit tests, AOT, samples, and the other integration legs passed, but both Blazor integration legs failed:
The packed template has all seven affected defaults set to literal
The nuspecs are correct, but the unbounded preview template wildcard is currently not viable; it can select the prior stable major and makes the PR not ready until constrained or otherwise corrected. |
|
/azp run maui-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Mark projects importing the shared device-test runner props as MAUI test projects so they consume current N packages, and clarify the cgmanifest script synopsis. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f8808afd-de8d-414a-b271-fae23ee7b596
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj:30
System.CodeDomis now relying on central versioning, but this project does not setIsTestProject/MauiTestProject, soeng/NuGetVersions.targetswill defaultUseN1PackageVersionstotrueand (during SDK previews) pinSystemCodeDomPackageVersionto the published N-1 version. That means this unit test project will unintentionally compile against N-1 packages rather than current N. Consider either marking this as a test project (<MauiTestProject>true</MauiTestProject>) or explicitly binding this reference to current N.
<PackageReference Include="System.CodeDom" />
Final CI artifact validation (build 1522346)Validated the artifacts produced for PR head Binlog: tests use N, shipping projects use N-1Using
Shipping NuGet packagesDownloaded and unpacked all 22 produced packages:
Across their
No Template package contentsUnpacked
There are no exact Preview 6/Preview 7 versions in template content and no unreplaced version placeholders. The package/pack and all template integration lanes passed. Overall CI is red for unrelated file-lock failures: Windows Debug could not access |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
…rsions Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d43af42-9e93-4be0-9754-b362beec3456
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
…rsions Fold the new Avalonia template token into the item-based version replacement. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d43af42-9e93-4be0-9754-b362beec3456
Preview 7 shipped 2026-08-11 as 11.0.0-preview.7.26381.103, and N is now 11.0.0-rc.1.26379.102. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d43af42-9e93-4be0-9754-b362beec3456
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
eng/scripts/update-cgmanifest.ps1:22
- The log message implies this script only reads versions from Versions.props, but it also reads the N-1 pin from eng/NuGetVersions.targets. Updating the message will make troubleshooting cgmanifest generation failures (e.g., missing MicrosoftDotNetN1PackageVersion) more straightforward.
# Read the central package version files
Write-Host "Reading versions from: $versionsPropsPath"
[xml]$versionsProps = Get-Content $versionsPropsPath -Raw
Verification against CI-produced packagesVerified the decoupling end-to-end against the artifacts from CI build PackagesDownloaded
The most direct evidence is the manifest package name: TemplatesIn the packed
CIAll 30 checks pass. Worth highlighting that the Blazor integration tests pass on both Windows and macOS — that is the precise scenario in the root cause, where floating Scope caveatThis validates the prerelease path. The GA path (N stable, so the |
kubaflo
left a comment
There was a problem hiding this comment.
@jonathanpeppers — fresh adversarial review of 7e49d93beceb5eb6b4b82162dea1a50803677973 completed.
NEEDS_CHANGES — one high-confidence release-pack regression survived independent review by Claude Opus 5, Gemini 3.1 Pro, GPT-5.6 Sol, MAUI expert review, and final Terra arbitration. Required maui-pr build 1550274 and CLA pass, but CI exercises the preview lane rather than StabilizePackageVersion=true.
The title accurately describes the main N/N-1 change. The description’s claims that Avalonia behavior is byte-identical and that the stabilized path remained unchanged are inaccurate for the current head.
The rewrite to _TemplatePackageVersion items replaced the upstream
StabilizePackageVersion guard with a Contains('-') check. Those are not
equivalent: a stabilized pack built while a version is still prerelease
would emit a wildcard default instead of the exact pin.
This affected Avalonia.Controls.Maui in every stabilized build, since it
ships on an independent cadence and stays prerelease when MAUI GA/SR
builds are stabilized. A shipped --with-avalonia template could then
restore a later third-party build than the one validated with that MAUI
release. The .NET tokens had the same latent defect for a stabilized SR
built against a prerelease SDK band.
Restore the stabilization guard on all five wildcard conditions and add
an Error that fails the pack if any stabilized token resolves to a
wildcard, so the invariant is enforced rather than assumed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7d43af42-9e93-4be0-9754-b362beec3456
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
eng/NuGetVersions.targets:7
MicrosoftDotNetN1PackageVersionis set unconditionally, which makes it difficult to override for local validation (e.g., testing a different N-1 band) since this assignment will overwrite any value passed via command-line or an earlier import. Consider only assigning the pinned default when the property is empty, mirroring the existingUseN1PackageVersionspattern above.
<MicrosoftDotNetN1PackageVersion>11.0.0-preview.7.26381.103</MicrosoftDotNetN1PackageVersion>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Root Cause
MAUI uses package versions from the in-development .NET SDK for shipping projects, tests, and generated templates. During previews, each SDK update therefore changes shipping dependencies and template defaults even though the previous band's compatible packages are already published.
An unconstrained NuGet
*is also not prerelease-aware: it selected stable .NET 10 ASP.NET Core packages for prerelease MAUI artifacts, causing the Blazor integration builds to fail with assembly version conflicts.Description of Change
eng/Versions.props— currently11.0.0-rc.1.26379.102MicrosoftDotNetN1PackageVersionineng/NuGetVersions.targets, pinned to the published Preview 7 packages (11.0.0-preview.7.26381.103)11.0.0-*template defaults while N is prerelease and exact current versions when N is stableThe N-1 substitution is gated on N containing a prerelease label, so it remained active across the Preview → RC transition and disables itself automatically at GA.
Validation
Verified against the current N / N-1 pair (
11.0.0-rc.1.26379.102/11.0.0-preview.7.26381.103):releases.json→runtime.versionfor11.0.0-preview.7), and all affected packages were confirmed published on nuget.orgeng/Version*, and adds nopreview.6literaldotnet cake --target=dotnetand restored with itCore,Controls.Core, andMicrosoft.AspNetCore.Components.WebView.Mauiresolve only N-1 packages — scanned eachproject.assets.json, zero RC 1 leakage into shipping projectsCore.UnitTestsresolves theMicrosoft.Extensions.*stack at current N (RC 1), confirming the test lane is unaffecteddotnet buildofCoreandMicrosoft.AspNetCore.Components.WebView.Mauisucceeds with 0 warnings and 0 errors — the BlazorWebView surface is where the original assembly version conflict appeared11.0.0-*for the .NET tokens while N is prereleaseeng/scripts/update-cgmanifest.ps1resolvesMicrosoft.Extensions.*to N-1, and missing N-1 data still fails generation explicitlyValidated earlier against the previous band, with the mechanism unchanged since: consecutive preview-then-stable template packs switching all seven generated defaults from
11.0.0-*to exact11.0.0; generating and building Blazor Server and Auto/WebAssembly projects from an installed template.nupkg; and inspecting 22 produced.nupkgnuspecs for dependency-band leakage.Merge Notes
Merging the latest
net11.0conflicted with #35950, which added anAVALONIA_CONTROLS_MAUI_VERSION_VALUEtemplate token to the property-group replacement block that this PR replaces with_TemplatePackageVersionitems. The token was folded into the new item form, and its computed value is byte-identical to the previous behaviour (11.0.0-preview.6.*).Avalonia.Controls.Mauiis third-party, pinned separately ineng/Versions.props, and is intentionally not wired into the N/N-1 mechanism, so its band is preserved rather than floated to11.0.0-*.Alternatives Considered
PR #36598 attempted the same decoupling from
main, using generic previous-published aggregates and private selector properties. This replacement targets the actualnet11.0dependency graph, treats the most recently published band as N-1, and uses an explicitN1-named property as requested.A bare
*was initially used for preview template defaults, but NuGet excludes prerelease packages from that range. Constraining the floating version to the current release (11.0.0-*) keeps preview templates floating while preventing fallback to stable packages from an older .NET release.The initial implementation added duplicate conditional
PackageReferenceitems and a self-invoking MSBuild test harness to the template project. The final implementation instead patches the existing central version properties and keeps test-only targets out of the production project.Issues Fixed
Fixes #36434