Skip to content

Commit 5a82142

Browse files
authored
Updates to account for changes in meaning of FileVersionQuad.Revision LSB (#38)
* Updates to account for changes in meaning of `FileVersionQuad.Reision` LSB * Specs are silent on the point playground indicates that the LSB indicates a CI build (ODD) however, that would place a CI build as a HIGHER sort order than the release form without special understanding. Which won't exist in any OS APIs. So the meaning is inverted and the higher numeric ordering is reserved for release builds allowing for expected comparisons to work. * Docs build updates * Adjusted the build version to use a patch to the previously released RC build. * This is OK for the develop branch as nothing has released from it or any other version and the changes are relevant to the updated release. * Removed unused source file causing build failures --------- Co-authored-by: smaillet <25911635+smaillet@users.noreply.github.com>
1 parent 7d90e84 commit 5a82142

File tree

14 files changed

+241
-120
lines changed

14 files changed

+241
-120
lines changed

BuildVersion.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
This file is updated on a public release to set the next build that pre-releases will increment to
33
-->
44
<BuildVersionData
5-
BuildMajor = "6"
5+
BuildMajor = "5"
66
BuildMinor = "0"
77
BuildPatch = "0"
8-
PreReleaseName = "alpha"
8+
PreReleaseName = "rc"
9+
PreReleaseNumber = "1"
910
/>

New-GeneratedVersionProps.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ class CSemVer
215215

216216
$this.OrderedVersion = [CSemVer]::GetOrderedVersion($this.Major, $this.Minor, $this.Patch, $this.PreReleaseVersion)
217217
$fileVer64 = $this.OrderedVersion -shl 1
218-
if($this.CiBuildIndex -and $this.CiBuildName)
218+
# If this is a release build include that in the file version
219+
if(!$this.CiBuildIndex -and !$this.CiBuildName)
219220
{
220221
$fileVer64 += 1;
221222
}
@@ -366,7 +367,7 @@ try
366367
$propGroupElement.AppendChild($buildKindElement) | Out-Null
367368

368369
# Unit tests need to see the CI build info as it isn't something they can determine on their own.
369-
# The Build index is based on a timestamp and the build name depends on the runtime environment
370+
# The Build index is based on a time stamp and the build name depends on the runtime environment
370371
# to set some env vars etc...
371372
if($buildKind -ne 'ReleaseBuild')
372373
{

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,32 @@ you just built instead of the last one released publicly.
3636
## Documentation
3737
Full documentation on the tasks is available in the project's [docs site](https://ubiquitydotnet.github.io/CSemVer.GitBuild/)
3838

39+
>[!IMPORTANT]
40+
> There is one significant difference from the behavior in the official [CSemVer](https://csemver.org/).
41+
> Sadly the specs are silent on the issue of a File Version, and the [Playground](https://csemver.org/playground/site/#/)
42+
> hints that the LSB of the Revision field of a File Version "Quad" indicates a CI build (ODD).
43+
> ^1^. However, that would lead to FileVersion comparisons where all CI builds are ordered ahead
44+
> of any release builds of the same version number. This is backwards from the expressed intent.
45+
> Therefore, the Ubiquity.NET.Versioning libraries and task DO NOT do this. They implement the
46+
> exact opposite (LSB == RELEASE BUILD). That way any release builds have a sort order that is
47+
> greater than a CI build of the same version number.
48+
3949
## Building the tasks
4050
The build uses a common PowerShell module pattern for Ubiquity.NET projects. To build the sources
4151
use the `Build-All.ps1` script. You can also open the `src/Ubiquity.NET.Versioning.slnx` in any
4252
editor/IDE that has support for the slnx solution format. (Visual Studio 2022 is used but other
43-
options may work, though they are not supported. If you have experience then PRs are welcome for
44-
additional support - but such PRs ***MUST NOT*** break the VS support.)
53+
options may work, though they are not supported. If you have experience with other IDEs, then PRs
54+
are welcome for additional support - but such PRs ***MUST NOT*** break the VS support, and you
55+
must be willing to maintain such support going forward.)
4556

4657
>[!IMPORTANT]
4758
> It is important to note that IDE builds of a clean repo get will FAIL! This is due to the
4859
> mechanisms used to eliminate circular dependencies while still supporting automated versioning
4960
> of the projects themselves. To resolve this, you must run the `.\New-GeneratedVersionProps.ps1`
5061
> at least once to create the imported `generatedversion.props` file. This is also generated by
51-
> the `Build-All.ps1` script, which is the recommended means of generaing the required file. This
62+
> the `Build-All.ps1` script, which is the recommended means of generating the required file. This
5263
> is only needed the first time (or any time the `buildversion.xml` changes).
64+
65+
----
66+
^1^See: [This issue](https://github.com/CK-Build/csemver.org/issues/2) which was reported upon
67+
testing this library and found inconsistencies.

docfx/build-tasks/index.md

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ using a Constrained Semantic Version ([CSemVer](https://csemver.org/)).
44

55
>[!WARNING]
66
> As a [Breaking change in .NET SDK 8](https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/source-link)
7-
> is now setting the build meta data for the `InformationalVersion` property without user
8-
> consent. (A Highly controversial choice that was more easily handled via an OPT-IN pattern)
9-
> Unfortunately, this was set ON by default and made into an 'OPT-OUT' scenario. This library
10-
> will honor such a setting and does not alter/interfere with it in any way. (Though the results
11-
> can, unfortunately, produce surprising behavior).
7+
> is now setting the build meta data for the `InformationalVersion` property automatically and
8+
> without user consent. (A Highly controversial choice that was more easily handled via an
9+
> OPT-IN pattern) Unfortunately, this was set ON by default and made into an 'OPT-OUT' scenario.
10+
> This library will honor such a setting and does not alter/interfere with it in any way.
11+
> (Though the results can, unfortunately, produce surprising behavior as it is not well
12+
> documented).
1213
>
1314
> If you wish to disable this behavior you can set an MSBUILD property to OPT-OUT as follows:
1415
> `<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>`
@@ -17,11 +18,12 @@ using a Constrained Semantic Version ([CSemVer](https://csemver.org/)).
1718
> who are aware of the change and those who use this library to set an explicit build meta data
1819
> string. (Principle of least surprise for what this library can control).
1920
>
20-
> The default .NET build behavior is to use the Repository ID (usually a GIT commit hash).
21-
> This is appended with a leading `+` if one isn't already in the `InformationalVersion`
22-
> property. If build metadata is already included (Like from use of this task) the id is
23-
> appended using a `.` instead. So it is ALWAYS appended unless the project has opted out of
24-
> this behavior by setting the property as previously described.
21+
> The default behavior added in this breaking change is to use the Repository ID (usually a GIT
22+
> commit hash [FULL SHA form!]) as the build metadata. This is appended with a leading `+` if
23+
> one isn't already in the `InformationalVersion` property. If build metadata is already
24+
> included (Like from use of this task) the id is appended using a `.` instead. So it is ALWAYS
25+
> appended unless the project has opted out of this behavior by setting the property as
26+
> previously described.
2527
>
2628
> Thus, it is ***strongly recommended*** that projects using this package OPT-OUT
2729
> of the new behavior.
@@ -36,15 +38,15 @@ allowing for automated CI builds. These new versions are called a [Constrained S
3638
Version](http://csemver.org) (CSemVer).
3739

3840
A CSemVer is unique for each CI build and always increments while still supporting official
39-
releases. In the real world there are often cases where there are additional builds that are distinct from
40-
official releases and CI builds. Including Local developer builds, builds generated from a Pull
41-
Request (a.k.a Automated buddy build). CSemVer doesn't explicitly define any format for these
42-
cases. So this library defines a pattern of versioning that is fully compatible with CSemVer and
43-
allows for the additional build types in a way that retains precedence having the least
44-
surprising consequences. In particular, local build packages have a higher precedence than CI or
45-
release versions if all other components of the version match. This ensures that what you are
46-
building includes the dependent packages you just built instead of the last one released
47-
publicly.
41+
releases. However, in the real world, there are often cases where there are additional builds
42+
that are distinct from official releases and CI builds. These include local developer builds
43+
and builds generated from a Pull Request (a.k.a Automated buddy build). Neither SemVer, nor
44+
CSemVer explicitly define any format for these cases. So this library defines a pattern of
45+
versioning that is fully compatible with CSemVer and allows for the additional build types
46+
in a way that retains precedence having the least surprising consequences. In particular,
47+
local build versions have a higher precedence than CI or release versions if all other
48+
components of the version match. This ensures that what you are building includes the dependent
49+
components you just built instead of the last one released publicly.
4850

4951
The following is a list of the version formats in descending order of precedence:
5052

@@ -56,24 +58,41 @@ The following is a list of the version formats in descending order of precedence
5658
| Official PreRelease | `{BuildMajor}.{BuildMinor}.{BuildPatch}-{PreReleaseName}[.PreReleaseNumber][.PreReleaseFix]+{BuildMeta}` |
5759
| Official Release | `{BuildMajor}.{BuildMinor}.{BuildPatch}+{BuildMeta}` |
5860

61+
That is the, CI BuildName is chosen to ensure the ordering matches the expected behavior for
62+
a build.
63+
5964
This project provides an MSBUILD task to automate the generation of these versions in an easy
6065
to use NuGet Package.
6166

6267
The package creates File and Assembly Versions and defines the appropriate MsBuild properties
6368
so the build will automatically incorporate them.
6469
> **NOTE:**
65-
The automatic use of MsBuild properties requires using the new SDK attribute support for .NET
66-
projects. Where the build auto generates the assembly info. If you are using some other means to
67-
auto generate the assembly level versioning attributes. You can use the properties generated by
68-
this package to generate the attributes.
70+
> The automatic use of MsBuild properties requires using the new SDK attribute support for .NET
71+
> projects. Where the build auto generates the assembly info. If you are using some other means
72+
> to auto generate the assembly level versioning attributes. You can use the properties
73+
> generated by this package to generate the attributes.
6974
7075
File and AssemblyVersions are computed based on the CSemVer "Ordered version", which
71-
is a 64 bit value that maps to a standard windows FILEVERSION Quad with each part
72-
consuming 16 bits. This ensures a strong relationship between the assembly/file versions
76+
is a 64 bit value that maps to a standard windows FILE VERSION Quad with each part
77+
consuming 16 bits. This ensures a strong relationship between the assembly/file versions
7378
and the packages as well as ensures that CI builds can function properly. Furthermore, this
7479
guarantees that each build has a different file and assembly version so that strong name
7580
signing functions properly to enable loading different versions in the same process.
7681

82+
>[!IMPORTANT]
83+
> A file version quad representation of a CSemVer does NOT carry with it the CI information nor
84+
> the build metadata. It only contains a single bit to indicate a Release vs. a CI build. In
85+
> fact, the official CSemVer specs are silent on the use of this bit though the "playground"
86+
> does indicate an ODD numbered revision is a CI build. However, that leads to problems (see
87+
> this: [Spec issue](https://github.com/CK-Build/csemver.org/issues/2) for more details).
88+
> Thus, the `Ubiquity.NET.Versioning.*` libraries do NOT work that way. In these libraries,
89+
> an odd numbered revision indicates a RELEASE build whereas an EVEN numbered form represents
90+
> a CI build. This results in the correct behavior for comparisons of the File version. The
91+
> file version comparisons MUST result in correct ordering or an underlying Windows OS and
92+
> related APIs will NOT behave as expected. (A CI build is ALWAYS ordered less then a release
93+
> build)
94+
95+
7796
The Major, Minor and Patch versions are only updated in the primary branch at the time
7897
of a release. This ensures the concept that SemVer versions define released products. The
7998
version numbers used are stored in the repository in the BuildVersion.xml

docfx/docfx.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@
6767
],
6868
"template": [
6969
"default",
70-
"modern"
70+
"modern",
71+
"templates/Ubiquity"
7172
],
7273
"globalMetadataFiles": [],
7374
"fileMetadataFiles": [],
7475
"postProcessors": [],
7576
"globalMetadata": {
7677
"_appTitle": "Ubiquity.NET",
7778
"_appFooter": "Copyright (C) 2017-2025, Ubiquity.NET Contributors",
78-
"_appLogoPath": "favicon-32x32.png", // TODO: change this to something more generic...
79+
"_appLogoPath": "favicon-32x32.png",
7980
"_disableBreadcrumb": true,
8081
"_gitContribute": {
8182
"repo": "https://github.com/UbiquityDotNET/CSemVer.GitBuild",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@charset "UTF-8";
2+
3+
/*
4+
Disable display of inherited members. It would be nice if there was a build option to not even generate this noise,
5+
but sadly, there is none so this disables the final render of such things.
6+
*/
7+
.typelist.inheritedMembers {
8+
display: none
9+
}

docfx/templates/Ubiquity/readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Ubiquity DOCFX template
2+
This template adds support to the modern template to disable some features like inherited members. It
3+
would be nice if docfx didn't even generate such things, but it has non knobs to control that so it can
4+
only be disabled by a custom CSS to hide the content at page render time on the client...

src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ private void SetFileVersion( int preRelIndex )
170170

171171
Log.LogMessage(MessageImportance.Low, "orderedVersion={0}", orderedVersion);
172172

173-
bool isCiBuild = !string.IsNullOrWhiteSpace(CiBuildIndex) && !string.IsNullOrWhiteSpace(CiBuildName);
174-
UInt64 fileVersion64 = (orderedVersion << 1) + (isCiBuild ? 1ul : 0ul);
173+
bool isReleaseBuild = string.IsNullOrWhiteSpace(CiBuildIndex) && string.IsNullOrWhiteSpace(CiBuildName);
174+
UInt64 fileVersion64 = (orderedVersion << 1) + (isReleaseBuild ? 1ul : 0ul);
175175
FileVersionRevision = (UInt16)(fileVersion64 % 65536);
176176

177177
UInt64 rem = (fileVersion64 - FileVersionRevision.Value) / 65536;

src/Ubiquity.NET.Versioning.UT/CSemVerTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,13 @@ public static void VerifyOrderedVersionPair(
209209
Assert.AreEqual(number, ver.PrereleaseVersion.Number, exp);
210210
Assert.AreEqual(fix, ver.PrereleaseVersion.Fix, exp);
211211
Assert.IsFalse(ver.CiBuildInfo.IsValid, exp);
212-
Assert.IsFalse(ver.IsCIBuild, exp);
212+
Assert.IsTrue(ver.IsCIBuild, exp, $"Conversion from integer should still indicate it IS a CI build for '{exp}'");
213213
Assert.IsNotNull(ver.BuildMetaData, $"non-nullable property should not be null for '{exp}'");
214214
Assert.AreEqual(string.Empty, ver.BuildMetaData, $"non-nullable property should be an empty string if not set '{exp}'");
215215
Assert.AreEqual(orderedVersion, ver.OrderedVersion, exp);
216216
Assert.AreEqual(f64, ver.FileVersion, exp);
217217

218-
// Now test CI variant
218+
// Now test Release variant
219219
var f64CI = FileVersionQuad.From((orderedVersion << 1) + 1);
220220
var verCI = CSemVer.From(f64CI.ToUInt64());
221221
Assert.AreEqual(major, verCI.Major, exp);
@@ -227,7 +227,7 @@ public static void VerifyOrderedVersionPair(
227227
Assert.AreEqual(number, verCI.PrereleaseVersion.Number, exp);
228228
Assert.AreEqual(fix, verCI.PrereleaseVersion.Fix, exp);
229229
Assert.IsFalse(verCI.CiBuildInfo.IsValid, $"Conversion from integer should NOT have any CI build info for '{exp}'");
230-
Assert.IsTrue(verCI.IsCIBuild, $"Conversion from integer should still indicate it IS a CI build for '{exp}'");
230+
Assert.IsFalse(verCI.IsCIBuild, $"Conversion from integer should indicate it IS a release build for '{exp}'");
231231
Assert.IsNotNull(verCI.BuildMetaData, $"non-nullable property should not be null for '{exp}'");
232232
Assert.AreEqual(string.Empty, verCI.BuildMetaData, $"non-nullable property should be an empty string if not set '{exp}'");
233233
Assert.AreEqual(orderedVersion, verCI.OrderedVersion , $"CI builds should have the same ordered version number as a non-CI build for '{exp}'");
@@ -250,13 +250,13 @@ public static void VerifyOrderedVersionPair(
250250
Assert.IsFalse(ver.PrereleaseVersion.IsValid, exp);
251251
Assert.IsFalse(ver.IsPrerelease, exp);
252252
Assert.IsFalse(ver.CiBuildInfo.IsValid, exp);
253-
Assert.IsFalse(ver.IsCIBuild, exp);
253+
Assert.IsTrue(ver.IsCIBuild, exp, $"Conversion from integer should still indicate it IS a CI build for '{exp}'");
254254
Assert.IsNotNull(ver.BuildMetaData, $"non-nullable property should not be null for '{exp}'");
255255
Assert.AreEqual(string.Empty, ver.BuildMetaData, $"non-nullable property should be an empty string if not set for '{exp}'");
256256
Assert.AreEqual(orderedVersion, ver.OrderedVersion, exp);
257257
Assert.AreEqual(f64, ver.FileVersion, exp);
258258

259-
// Now test CI variant
259+
// Now test Release variant
260260
var f64CI = FileVersionQuad.From((orderedVersion << 1) + 1);
261261
var verCI = CSemVer.From(f64CI.ToUInt64());
262262
Assert.AreEqual(major, verCI.Major, exp);
@@ -265,7 +265,7 @@ public static void VerifyOrderedVersionPair(
265265
Assert.IsFalse(verCI.PrereleaseVersion.IsValid, exp);
266266
Assert.IsFalse(verCI.IsPrerelease, exp);
267267
Assert.IsFalse(verCI.CiBuildInfo.IsValid, $"Conversion from integer should NOT have any CI build info for '{exp}'");
268-
Assert.IsTrue(verCI.IsCIBuild, $"Conversion from integer should indicate it IS a CI build for '{exp}'");
268+
Assert.IsFalse(verCI.IsCIBuild, $"Conversion from integer should indicate it IS a release build for '{exp}'");
269269
Assert.IsNotNull(verCI.BuildMetaData, $"non-nullable property should not be null for '{exp}'");
270270
Assert.AreEqual(string.Empty, verCI.BuildMetaData, $"non-nullable property should be an empty string if not set for '{exp}'");
271271
Assert.AreEqual(orderedVersion, verCI.OrderedVersion , $"CI builds should have the same ordered version number as a non-CI build for '{exp}'");

src/Ubiquity.NET.Versioning.UT/FileVersionQuadTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public void CompareToTest( )
4141
var val = new FileVersionQuad(0x1234, 0x5678, 0x9ABC, 0xDEF0 );
4242
var valp1 = new FileVersionQuad(0x1234, 0x5678, 0x9ABC, 0xDEF1 );
4343

44+
Assert.IsFalse(valm1.IsCiBuild);
45+
Assert.IsTrue(val.IsCiBuild);
46+
Assert.IsFalse(valp1.IsCiBuild);
47+
4448
Assert.AreEqual(-1, valm1.CompareTo(val), "(val-1) < val");
4549
Assert.AreEqual(-1, valm1.CompareTo(valp1), "(val-1) < (val+1)");
4650
Assert.AreEqual(1, val.CompareTo(valm1), "val > (val-1)");

0 commit comments

Comments
 (0)