Give the assemblies a coherent, binding-stable version - #351
Merged
Conversation
Three problems, all in how versions reached MSBuild. Compile passed no version properties at all, so every DLL under build/$(Configuration)/ - including the ones PackBinaries copies into bin/net for the distribution and the examples - was stamped 1.0.0.0 from the SDK default, while the same assemblies inside the packages were stamped correctly by Pack. The two halves of the build disagreed. Pack set -p:Version, which is a global override of MSBuild's VersionPrefix-plus-VersionSuffix composition, so --version-suffix was inert. Untagged CI and local packs therefore produced a bare 3.1.0 rather than 3.1.0-preview-20260809-0945, indistinguishable from a real release in a local feed. AssemblyVersion tracked the full package version, so it moved on every patch: 3.0.1.0, 3.0.2.0, 3.0.3.0. The assemblies are strong-named, which makes AssemblyVersion part of their binding identity, so consumers on .NET Framework needed a new binding redirect for every fix release. DetermineVersion() now derives three separate facets from the tag and applies them to both Compile and Pack: AssemblyVersion pinned to major.minor.0.0 so patch releases are drop-in replacements, FileVersion carrying the exact numeric version, and InformationalVersion left to MSBuild so SourceLink can append +<sha>. Prerelease tags are split, since only the numeric part is a valid AssemblyVersion. Verified against a throwaway v9.9.0-rc.1 tag: package 9.9.0-rc.1, AssemblyVersion 9.9.0.0, FileVersion 9.9.0, InformationalVersion 9.9.0-rc.1+<sha>, PublicKeyToken unchanged, and build/Release assemblies carrying the same versions as the packaged ones. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three problems in how versions reached MSBuild, all fixed by deriving the facets once and applying them to both
CompileandPack.What was wrong
1. The two halves of the build disagreed.
Compilepassed no version properties at all, soVersionPrefixfell back to the SDK default and every DLL underbuild/$(Configuration)/was stamped1.0.0.0— including the onesPackBinariescopies intobin/netfor the distribution and the examples. The same assemblies inside the packages were stamped correctly, because onlyPackset anything.2. The preview suffix was inert.
Packused.SetVersion(...), which emits-p:Version=. That is a global override of MSBuild'sVersionPrefix-plus-VersionSuffixcomposition, so the--version-suffixargument next to it did nothing. Untagged CI and local packs produced a bare3.1.0instead of3.1.0-preview-20260809-0945— indistinguishable from a real release once it lands in a local feed.3.
AssemblyVersionmoved on every patch —3.0.1.0,3.0.2.0,3.0.3.0. The assemblies are strong-named, soAssemblyVersionis part of their binding identity, and consumers on .NET Framework needed a fresh binding redirect for every fix release.What it does now
DetermineVersion()derives three distinct facets from the tag:AssemblyVersionmajor.minor.0.03.1.0 → 3.1.1 → 3.1.2are drop-in replacements.FileVersionInformationalVersion+<sha>.Prerelease tags are split (
v3.1.0-rc.1→ prefix3.1.0, suffixrc.1), since only the numeric part is a validAssemblyVersion.major.minor.0.0rather than the more commonmajor.0.0.0: the latter would mean3.0.0.0, which is lower than the already-shipped3.0.3.0and would force a backward redirect at that hop. This choice is monotonic against what is on nuget.org today.Verification
Against a throwaway
v9.9.0-rc.1tag, deliberately mismatched with theProjectVersionplaceholder:build/Release/**assemblies carry the same versions as the packaged ones — 60 DLLs checked, no1.0.0.0left. Untagged on this branch,Packproduces3.1.0-preview-20260809-0945withAssemblyVersion 3.1.0.0. Build project compiles clean. The throwaway tag was deleted and never pushed.Note
TagVersionstill usesSingleOrDefault(x => x.StartsWith("v")), which throws if HEAD ever carries twov*tags. Left alone deliberately — picking one arbitrarily would be worse than failing loudly — but worth a separate issue.