From b65022aa3e615ba3cd2c85b0a5dc8e953b7bb743 Mon Sep 17 00:00:00 2001
From: smaillet <25911635+smaillet@users.noreply.github.com>
Date: Sun, 1 Jun 2025 08:48:50 -0700
Subject: [PATCH 1/5] Added Testing of assembly attributes * Updated build
scripts to force a rebuild of all projects - The tests use nuget pkg
resolution and would otherwise use whatever package was built last. * Added
targets to PSSPROJ files to allow rebuilds to work * Marked IDE runs as in
conclusive for packaged assembly validation. - Since tests resolve NUGET
packages it isn't deterministic if the package was created via the IDE or a
command line build. - IDE builds are determined by the presence of the
environment variable "BuildTime". - This value is set by the build
scripts for ALL of the builds so that all binaries produce the same version
number for CI builds. (It is ignored for a release build)
---
Build-All.ps1 | 2 +-
PsModules/CommonBuild/CommonBuild.pssproj | 3 +-
.../Public/Initialize-BuildEnvironment.ps1 | 10 ---
PsModules/RepoBuild/RepoBuild.pssproj | 4 +-
.../BuildTaskTests.cs | 61 +++++++++++++++++++
.../TestModuleFixtures.cs | 9 +++
.../Ubiquity.Versioning.Build.Tasks.UT.csproj | 1 +
7 files changed, 76 insertions(+), 14 deletions(-)
diff --git a/Build-All.ps1 b/Build-All.ps1
index 65b1dc4..d94587f 100644
--- a/Build-All.ps1
+++ b/Build-All.ps1
@@ -50,7 +50,7 @@ try
mkdir $buildInfo['NuGetOutputPath'] -ErrorAction SilentlyContinue | Out-Null
- dotnet build -c $Configuration 'src/Ubiquity.NET.Versioning.slnx'
+ dotnet build -c $Configuration --no-incremental 'src/Ubiquity.NET.Versioning.slnx'
}
catch
{
diff --git a/PsModules/CommonBuild/CommonBuild.pssproj b/PsModules/CommonBuild/CommonBuild.pssproj
index 7e28cda..07afd0a 100644
--- a/PsModules/CommonBuild/CommonBuild.pssproj
+++ b/PsModules/CommonBuild/CommonBuild.pssproj
@@ -59,6 +59,7 @@
+
@@ -68,4 +69,4 @@
-
\ No newline at end of file
+
diff --git a/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1 b/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
index 6335237..61bef94 100644
--- a/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
+++ b/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
@@ -350,16 +350,6 @@ function Initialize-BuildEnvironment
}
- Write-Information 'Deleting common build versioning env vars'
- # override the build version related values set from CommonBuild
- # This repo is unique in that it CREATES the package that uses these.
- # The actual build of these packages use `GeneratedVersion.props`
- $env:IsAutomatedBuild = $null
- $env:IsPullRequestBuild =$null
- $env:IsReleaseBuild =$null
- $env:CiBuildName =$null
- $env:BuildTime =$null
-
return $buildInfo
}
catch
diff --git a/PsModules/RepoBuild/RepoBuild.pssproj b/PsModules/RepoBuild/RepoBuild.pssproj
index 9c74d88..f5ae29c 100644
--- a/PsModules/RepoBuild/RepoBuild.pssproj
+++ b/PsModules/RepoBuild/RepoBuild.pssproj
@@ -53,8 +53,6 @@
-
-
@@ -62,7 +60,9 @@
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
+
+
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
index 1d962b5..37432df 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Runtime.Loader;
using System.Xml.Linq;
@@ -48,18 +49,29 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
["BuildVersionXml"] = Path.Combine(RepoRoot, "BuildVersion.xml"),
};
+ string envBuildTime = TestModuleFixtures.EnvBuildTime;
+ bool isIDEBuild = true;
+ if( !string.IsNullOrWhiteSpace(envBuildTime))
+ {
+ globalProperties["BuildTime"] = envBuildTime;
+ isIDEBuild = false;
+ }
+
using var collection = new ProjectCollection(globalProperties);
var (testProject, props) = CreateTestProjectAndInvokeTestedPackage(
targetFramework,
projectCollection: collection
);
+
string? taskAssembly = testProject.ProjectInstance.GetOptionalProperty("_Ubiquity_NET_Versioning_Build_Tasks");
Assert.IsNotNull( taskAssembly, "Task assembly property should contain full path to the task DLL (Not NULL)" );
Context.WriteLine( $"Task Assembly: '{taskAssembly}'" );
+
Assert.IsFalse( string.IsNullOrWhiteSpace( taskAssembly ), "Task assembly property should contain full path to the task DLL (Not Whitespace)" );
Assert.IsNotNull( props.FileVersion, "Generated properties should have a 'FileVersion'" );
Context.WriteLine( $"Generated FileVersion: {props.FileVersion}" );
+
var alc = new AssemblyLoadContext("TestALC", isCollectible: true);
try
{
@@ -70,6 +82,7 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
Assert.IsNotNull( asmVer, "Task assembly should have a version" );
Context.WriteLine( $"TaskAssemblyVersion: {asmVer}" );
Context.WriteLine( $"AssemblyName: {asmName}" );
+
Assert.IsNotNull( props.FileVersionMajor, "Property value for Major should exist" );
Assert.AreEqual( (int)props.FileVersionMajor, asmVer.Major, "Major value of assembly version should match" );
@@ -81,6 +94,54 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
Assert.IsNotNull( props.FileVersionRevision, "Property value for Revision should exist" );
Assert.AreEqual( (int)props.FileVersionRevision, asmVer.Revision, "Revision value of assembly version should match" );
+
+ // Test that AssemblyFileVersion attribute matches expected value
+ string fileVersion = ( from attr in asm.CustomAttributes
+ where attr.AttributeType.FullName == "System.Reflection.AssemblyFileVersionAttribute"
+ let val = attr.ConstructorArguments.Single().Value as string
+ where val is not null
+ select val
+ ).Single();
+
+ Assert.AreEqual(props.FileVersion, fileVersion);
+
+ // Test that AssemblyInformationalVersion attribute matches expected value
+ string informationalVersion = ( from attr in asm.CustomAttributes
+ where attr.AttributeType.FullName == "System.Reflection.AssemblyInformationalVersionAttribute"
+ let val = attr.ConstructorArguments.Single().Value as string
+ where val is not null
+ select val
+ ).Single();
+
+ // Check for limited version information in an IDE run as the "BuildTime" environment,
+ // which is translated to CIBuildIndex, is hard coded to the max value. Additionally,
+ // in a release build there is NO CI information to extract so it is inconclusive.
+ // Full testing is formally inconclusive but at least validate what is plausible and
+ // treat the rest as inconclusive
+ if (isIDEBuild)
+ {
+ Context.WriteLine($"AssemblyInformationalVersion {informationalVersion}");
+
+ Assert.IsNotNull(props.InformationalVersion);
+
+ // release builds won't have ANY CI information.
+ int cipos = props.InformationalVersion.IndexOf(".ci", StringComparison.Ordinal);
+ if(cipos > 0)
+ {
+ string propsVersion = props.InformationalVersion[ ..cipos];
+
+ int ideCiBuildPos = informationalVersion.IndexOf(".ci");
+ informationalVersion = informationalVersion[ ..ideCiBuildPos];
+
+ Assert.AreEqual(propsVersion, informationalVersion);
+ }
+
+ Assert.Inconclusive("IDE build verification is inconclusive; but at least matches expectations");
+ }
+ else
+ {
+ Assert.AreEqual(props.InformationalVersion, informationalVersion);
+ }
}
finally
{
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
index 12e530e..2aa1766 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
@@ -48,6 +48,13 @@ public static void AssemblyInitialize( TestContext ctx )
string? value = Environment.GetEnvironmentVariable(envVar);
if(value is not null)
{
+ // Save the environment build time for use with task assembly builds as that
+ // value is needed to determine the build index used for non-IDE builds
+ if(envVar == "BuildTime")
+ {
+ EnvBuildTime = value;
+ }
+
Environment.SetEnvironmentVariable(envVar, null);
}
}
@@ -67,6 +74,8 @@ public static void AssemblyCleanup( )
PackageRepo?.Dispose();
}
+ internal static string EnvBuildTime { get; private set; } = string.Empty;
+
private static void CopyFile(string srcDir, string fileName, string dstDir)
{
File.Copy(Path.Combine(srcDir, fileName), Path.Combine(dstDir, fileName), overwrite: true);
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/Ubiquity.Versioning.Build.Tasks.UT.csproj b/src/Ubiquity.Versioning.Build.Tasks.UT/Ubiquity.Versioning.Build.Tasks.UT.csproj
index 6d064cb..55c1a88 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/Ubiquity.Versioning.Build.Tasks.UT.csproj
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/Ubiquity.Versioning.Build.Tasks.UT.csproj
@@ -10,4 +10,5 @@
+
From 1f969bc3b91ca1e4196f47606221eca41f215da9 Mon Sep 17 00:00:00 2001
From: smaillet <25911635+smaillet@users.noreply.github.com>
Date: Sun, 1 Jun 2025 09:01:24 -0700
Subject: [PATCH 2/5] Updated GH Action to upload test results even if failed
as follow up action that updates a PR comment needs the results.
---
.github/workflows/pr-build.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml
index c6fdafc..d8b291c 100644
--- a/.github/workflows/pr-build.yml
+++ b/.github/workflows/pr-build.yml
@@ -49,7 +49,9 @@ jobs:
- name: Run Tests
run: ./Invoke-Tests.ps1
+ # Upload test results even if failed; unless cancelled
- name: Upload Test Results
+ if: (!cancelled())
uses: actions/upload-artifact@v4
with:
name: Test Results
From 8d42688f920d9d0a61393c5cf1e9f25dae25be55 Mon Sep 17 00:00:00 2001
From: smaillet <25911635+smaillet@users.noreply.github.com>
Date: Sun, 1 Jun 2025 12:10:08 -0700
Subject: [PATCH 3/5] Removed IDE build detection * It was downright confusing
and, obviously now, broke the automated builds - Now requires existence
of a generated props file * Moved creation of the generated props file to a
distinct script to make it easy to generate once for IDE work. -
Directory.Build.Targets contains a check for it and will create an error if
not found with a message indicating what to do to resolve the problem.
---
.editorconfig | 5 +-
Build-All.ps1 | 2 +-
Directory.Build.props | 31 +-
Directory.Build.targets | 5 +-
New-GeneratedVersionProps.ps1 | 371 ++++++++++++++++++
.../Private/ConvertTo-BuildIndex.ps1 | 23 --
.../Public/Initialize-BuildEnvironment.ps1 | 278 -------------
PsModules/RepoBuild/RepoBuild.pssproj | 19 +-
...biquity.NET.Versioning.Build.Tasks.targets | 8 +-
src/Ubiquity.NET.Versioning.slnx | 1 +
.../BuildTaskTests.cs | 52 +--
.../TestModuleFixtures.cs | 98 ++++-
12 files changed, 493 insertions(+), 400 deletions(-)
create mode 100644 New-GeneratedVersionProps.ps1
delete mode 100644 PsModules/RepoBuild/Private/ConvertTo-BuildIndex.ps1
diff --git a/.editorconfig b/.editorconfig
index 5e92d1e..f5d0214 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -94,6 +94,9 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
dotnet_diagnostic.SA1005.severity = none
+# see: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3920
+dotnet_diagnostic.SA1202.severity = none
+
# Analysis and refactoring rules for Ubiquity.NET
# Description: Code analysis rules for Ubiquity.NET projects
@@ -1408,8 +1411,6 @@ dotnet_diagnostic.SA1200.severity = error
dotnet_diagnostic.SA1201.severity = error
-dotnet_diagnostic.SA1202.severity = error
-
dotnet_diagnostic.SA1203.severity = none
dotnet_diagnostic.SA1204.severity = silent
diff --git a/Build-All.ps1 b/Build-All.ps1
index d94587f..2b64a8d 100644
--- a/Build-All.ps1
+++ b/Build-All.ps1
@@ -49,7 +49,7 @@ try
}
mkdir $buildInfo['NuGetOutputPath'] -ErrorAction SilentlyContinue | Out-Null
-
+ .\New-GeneratedVersionProps.ps1 $buildInfo
dotnet build -c $Configuration --no-incremental 'src/Ubiquity.NET.Versioning.slnx'
}
catch
diff --git a/Directory.Build.props b/Directory.Build.props
index c03a411..3b1bc9d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,30 +1,15 @@
-
-
-
-
-
- 1.27597.27630.61955
- 5.0.0-a.ci-4294967295.IDE
- 5.0.0-alpha.ci-4294967295.IDE
- $(FileVersion)
- $(ProductVersion)
+
+ $(MSBuildThisFileDirectory)GeneratedVersion.props
-
-
+
-
+
-
+
+
diff --git a/New-GeneratedVersionProps.ps1 b/New-GeneratedVersionProps.ps1
new file mode 100644
index 0000000..0f94dfd
--- /dev/null
+++ b/New-GeneratedVersionProps.ps1
@@ -0,0 +1,371 @@
+using module "PSModules/CommonBuild/CommonBuild.psd1"
+using module "PSModules/RepoBuild/RepoBuild.psd1"
+
+<#
+.SYNOPSIS
+ Script to Create the generateVersion.props file used in this repo build
+
+.PARAMETER Configuration
+ This sets the build configuration to use, default is "Release" though for inner loop development this
+ may be set to "Debug".
+
+.PARAMETER ForceClean
+ Forces a complete clean (Recursive delete of the build output)
+
+.DESCRIPTION
+ This script is used by the local and automated builds to create the versioning information for the assemblies
+ in this repository. The assembly build CANNOT reference itself to get the versioning (only consumers of the
+ OUTPUT of this repo can). Thus, this will generate the information using the same algorithms, at least in theory.
+ Unit tests will VERIFY that the actual task assembly matches the versioning it would produce itself. So, the
+ "duality" of code paths is still verified.
+#>
+[cmdletbinding()]
+Param(
+ [hashtable]$buildInfo,
+ [string]$Configuration="Release",
+ [switch]$ForceClean
+)
+
+function ConvertTo-BuildIndex
+{
+<#
+.SYNOPSIS
+ Converts a TimeStamp into a build index
+
+.DESCRIPTION
+ The algorithm used is the same as the package published. The resulting index is a 32bit value that
+ is a combination of the number of days since a fixed point (Upper 16 bits) and the number of seconds since
+ midnight (on the day of the input time stamp) divided by 2 {Lower 16 bits)
+#>
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeLine)]
+ [DateTime]$timeStamp
+ )
+
+ $timeStamp = $timeStamp.ToUniversalTime()
+ $midnightTodayUtc = [DateTime]::new($timeStamp.Year, $timeStamp.Month, $timeStamp.Day, 0, 0, 0, [DateTimeKind]::Utc)
+ $baseDate = [DateTime]::new(2000, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)
+ $buildNumber = ([Uint32]($timeStamp - $baseDate).Days) -shl 16
+ $buildNUmber += [UInt16](($timeStamp - $midnightTodayUtc).TotalSeconds / 2)
+ return $buildNumber
+}
+
+class PreReleaseVersion
+{
+ [ValidateSet("alpha", "beta", "delta", "epsilon", "gamma", "kappa", "prerelease", "rc")]
+ [string] $Name;
+
+ [ValidateSet("a", "b", "d", "e", "g", "k", "p", "r")]
+ [string] $ShortName;
+
+ [ValidateRange(-1,7)]
+ [int] $Index;
+
+ [ValidateRange(0,99)]
+ [int] $Number;
+
+ [ValidateRange(0,99)]
+ [int] $Fix;
+
+ PreReleaseVersion([hashtable]$buildVersionXmlData)
+ {
+ $preRelName = $buildVersionXmlData['PreReleaseName']
+
+ if( ![string]::IsNullOrWhiteSpace( $preRelName ) )
+ {
+ $this.Index = [PreReleaseVersion]::GetPrerelIndex($preRelName)
+ if($this.Index -ge 0)
+ {
+ $this.Name = [PreReleaseVersion]::PreReleaseNames[$this.Index]
+ $this.ShortName = [PreReleaseVersion]::PreReleaseShortNames[$this.Index]
+ }
+
+ $this.Number = $buildVersionXmlData['PreReleaseNumber'];
+ $this.Fix = $buildVersionXmlData['PreReleaseFix'];
+ }
+ else
+ {
+ $this.Index = -1;
+ }
+ }
+
+ [string] ToString([bool] $useShortForm = $false)
+ {
+ $hasPreRel = $this.Index -ge 0
+
+ $bldr = [System.Text.StringBuilder]::new()
+ if($hasPreRel)
+ {
+ $bldr.Append('-').Append($useShortForm ? $this.ShortName : $this.Name)
+ $delimFormat = $useShortForm ? '-{0:D02}' : '.{0}'
+ if(($this.Number -gt 0))
+ {
+ $bldr.AppendFormat($delimFormat, $this.Number)
+ if(($this.Fix -gt 0))
+ {
+ $bldr.AppendFormat($delimFormat, $this.Fix)
+ }
+ }
+ }
+
+ return $bldr.ToString()
+ }
+
+ hidden static [string[]] $PreReleaseNames = @("alpha", "beta", "delta", "epsilon", "gamma", "kappa", "prerelease", "rc" );
+ hidden static [string[]] $PreReleaseShortNames = @("a", "b", "d", "e", "g", "k", "p", "r");
+
+ hidden static [int] GetPrerelIndex([string] $preRelName)
+ {
+ $preRelIndex = -1
+ if(![string]::IsNullOrWhiteSpace($preRelName))
+ {
+ $preRelIndex = [PreReleaseVersion]::PreReleaseNames |
+ ForEach-Object {$index=0} {@{Name = $_; Index = $index++}} |
+ Where-Object {$_["Name"] -ieq $preRelName} |
+ ForEach-Object {$_["Index"]} |
+ Select-Object -First 1
+
+ # if not found in long names, test against the short names
+ if($preRelIndex -lt 0)
+ {
+ $preRelIndex = [PreReleaseVersion]::PreReleaseShortNames |
+ ForEach-Object {$index=0} {@{Name = $_; Index = $index++}} |
+ Where-Object {$_["Name"] -ieq $preRelName} |
+ ForEach-Object {$_["Index"]} |
+ Select-Object -First 1
+ }
+ }
+ return $preRelIndex
+ }
+
+}
+
+class CSemVer
+{
+ [ValidateRange(0,99999)]
+ [int] $Major;
+
+ [ValidateRange(0,49999)]
+ [int] $Minor;
+
+ [ValidateRange(0,9999)]
+ [int] $Patch;
+
+ [ValidateLength(0,20)]
+ [string] $BuildMetadata;
+
+ [ValidatePattern('\A[a-z0-9-]+\Z')]
+ [string] $CiBuildIndex;
+
+ [ValidatePattern('\A[a-z0-9-]+\Z')]
+ [string] $CiBuildName;
+
+ [ulong] $OrderedVersion;
+
+ [Version] $FileVersion;
+
+ [PreReleaseVersion] $PreReleaseVersion;
+
+ CSemVer([hashtable]$buildVersionData)
+ {
+ $this.Major = $buildVersionData["BuildMajor"]
+ $this.Minor = $buildVersionData["BuildMinor"]
+ $this.Patch = $buildVersionData["BuildPatch"]
+ if($buildVersionData["PreReleaseName"])
+ {
+ $this.PreReleaseVersion = [PreReleaseVersion]::new($buildVersionData)
+ if(!$this.PreReleaseVersion)
+ {
+ throw "Internal ERROR: PreReleaseVersion version is NULL!"
+ }
+ }
+
+ $this.BuildMetadata = $buildVersionData["BuildMetadata"]
+
+ $this.CiBuildName = $buildVersionData["CiBuildName"];
+ $this.CiBuildIndex = $buildVersionData["CiBuildIndex"];
+
+ if( (![string]::IsNullOrEmpty( $this.CiBuildName )) -and [string]::IsNullOrEmpty( $this.CiBuildIndex ) )
+ {
+ throw "CiBuildIndex is required if CiBuildName is provided";
+ }
+
+ if( (![string]::IsNullOrEmpty( $this.CiBuildIndex )) -and [string]::IsNullOrEmpty( $this.CiBuildName ) )
+ {
+ throw "CiBuildName is required if CiBuildIndex is provided";
+ }
+
+ $this.OrderedVersion = [CSemVer]::GetOrderedVersion($this.Major, $this.Minor, $this.Patch, $this.PreReleaseVersion)
+ $fileVer64 = $this.OrderedVersion -shl 1
+ if($this.CiBuildIndex -and $this.CiBuildName)
+ {
+ $fileVer64 += 1;
+ }
+
+ $this.FileVersion = [CSemVer]::ConvertToVersion($fileVer64)
+ }
+
+ [string] ToString([bool] $includeMetadata, [bool]$useShortForm)
+ {
+ $bldr = [System.Text.StringBuilder]::new()
+ $bldr.AppendFormat('{0}.{1}.{2}', $this.Major, $this.Minor, $this.Patch)
+ if($this.PreReleaseVersion)
+ {
+ $bldr.Append($this.PreReleaseVersion.ToString($useShortForm))
+ }
+
+ $hasPreRel = $this.PreReleaseVersion -and $this.PreReleaseVersion.Index -ge 0
+ if($this.CiBuildIndex -and $this.CiBuildName)
+ {
+ $bldr.Append($hasPreRel ? '.' : '--')
+ $bldr.AppendFormat('ci.{0}.{1}', $this.CiBuildIndex, $this.CiBuildName)
+ }
+
+ if(![string]::IsNullOrWhitespace($this.BuildMetadata) -and $includeMetadata)
+ {
+ $bldr.AppendFormat( '+{0}', $this.BuildMetadata )
+ }
+
+ return $bldr.ToString();
+ }
+
+ [string] ToString()
+ {
+ return $this.ToString($true, $false);
+ }
+
+ hidden static [ulong] GetOrderedVersion($Major, $Minor, $Patch, [PreReleaseVersion] $PreReleaseVersion)
+ {
+ [ulong] $MulNum = 100;
+ [ulong] $MulName = $MulNum * 100;
+ [ulong] $MulPatch = ($MulName * 8) + 1;
+ [ulong] $MulMinor = $MulPatch * 10000;
+ [ulong] $MulMajor = $MulMinor * 50000;
+
+ [ulong] $retVal = (([ulong]$Major) * $MulMajor) + (([ulong]$Minor) * $MulMinor) + ((([ulong]$Patch) + 1) * $MulPatch);
+ if( $PreReleaseVersion -and $PreReleaseVersion.Index -ge 0 )
+ {
+ $retVal -= $MulPatch - 1;
+ $retVal += [ulong]($PreReleaseVersion.Index) * $MulName;
+ $retVal += [ulong]($PreReleaseVersion.Number) * $MulNum;
+ $retVal += [ulong]($PreReleaseVersion.Fix);
+ }
+ return $retVal;
+ }
+
+ hidden static [Version] ConvertToVersion([ulong]$value)
+ {
+ $revision = [ushort]($value % 65536);
+ $rem = [ulong](($value - $revision) / 65536);
+
+ $build = [ushort]($rem % 65536);
+ $rem = ($rem - $build) / 65536;
+
+ $minorNum = [ushort]($rem % 65536);
+ $rem = ($rem - $minorNum) / 65536;
+
+ $majorNum = [ushort]($rem % 65536);
+
+ return [Version]::new( $majorNum, $minorNum, $build, $revision );
+ }
+}
+
+Set-StrictMode -Version 3.0
+
+Push-Location $PSScriptRoot
+$oldPath = $env:Path
+try
+{
+ # Pull in the repo specific support and force a full initialization of all the environment
+ # if current build information is not provided.
+ if(!$buildInfo)
+ {
+ $buildInfo = Initialize-BuildEnvironment -FullInit
+ if(!$buildInfo -or $buildInfo -isnot [hashtable])
+ {
+ throw "build scripts BUSTED; Got null buildinfo hashtable..."
+ }
+ }
+
+ # PowerShell doesn't export enums from a script module, so the type of this return is
+ # "unpronounceable" [In C++ terminology]. So, convert it to a string so it is usable in this
+ # script.
+ [string] $buildKind = Get-CurrentBuildKind
+
+ $verInfo = Get-ParsedBuildVersionXML -BuildInfo $buildInfo
+ if($buildKind -ne "ReleaseBuild")
+ {
+ $verInfo['CiBuildIndex'] = ConvertTo-BuildIndex $env:BuildTime
+ }
+
+ switch($buildKind)
+ {
+ "LocalBuild" { $verInfo['CiBuildName'] = "ZZZ" }
+ "PullRequestBuild" { $verInfo['CiBuildName'] = "PRQ" }
+ "CiBuild" { $verInfo['CiBuildName'] = "BLD" }
+ "ReleaseBuild" { }
+ default {throw "unknown build kind" }
+ }
+
+ # Generate props file with the version information for this build.
+ # While it is plausible to use ENV vars to overload or set properties
+ # that leads to dangling values during development, which makes for
+ # a LOT of wasted time chasing down why a change didn't work...
+ # [Been there, done that, worn out the bloody T-Shirt...]
+ $csemVer = [CSemVer]::New($verInfo)
+ $xmlDoc = [System.Xml.XmlDocument]::new()
+ $projectElement = $xmlDoc.CreateElement("Project")
+ $xmlDoc.AppendChild($projectElement) | Out-Null
+
+ $propGroupElement = $xmlDoc.CreateElement("PropertyGroup")
+ $projectElement.AppendChild($propGroupElement) | Out-Null
+
+ $fileVersionElement = $xmlDoc.CreateElement("FileVersion")
+ $fileVersionElement.InnerText = $csemVer.FileVersion.ToString()
+ $propGroupElement.AppendChild($fileVersionElement) | Out-Null
+
+ $packageVersionElement = $xmlDoc.CreateElement("PackageVersion")
+ $packageVersionElement.InnerText = $csemVer.ToString($false,$true) # short form of version
+ $propGroupElement.AppendChild($packageVersionElement) | Out-Null
+
+ $productVersionElement = $xmlDoc.CreateElement("ProductVersion")
+ $productVersionElement.InnerText = $csemVer.ToString($true, $false) # long form of version
+ $propGroupElement.AppendChild($productVersionElement) | Out-Null
+
+ $assemblyVersionElement = $xmlDoc.CreateElement("AssemblyVersion")
+ $assemblyVersionElement.InnerText = $csemVer.FileVersion.ToString()
+ $propGroupElement.AppendChild($assemblyVersionElement) | Out-Null
+
+ $informationalVersionElement = $xmlDoc.CreateElement("InformationalVersion")
+ $informationalVersionElement.InnerText = $csemVer.ToString($true, $false) # long form of version
+ $propGroupElement.AppendChild($informationalVersionElement) | Out-Null
+
+ # Unit tests need to see the CI build info as it isn't something they can determine on their own.
+ # The Build index is based on a timestamp and the build name depends on the runtime environment
+ # to set some env vars etc...
+ if($buildKind -ne "ReleaseBuild")
+ {
+ $buildTimeElement = $xmlDoc.CreateElement("BuildTime")
+ $buildTimeElement.InnerText = $env:BuildTime
+ $propGroupElement.AppendChild($buildTimeElement) | Out-Null
+ }
+
+ $buildGeneratedPropsPath = Join-Path $buildInfo["RepoRootPath"] "GeneratedVersion.props"
+ $xmlDoc.Save($buildGeneratedPropsPath)
+}
+catch
+{
+ # everything from the official docs to the various articles in the blog-sphere says this isn't needed
+ # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location
+ # information is retained and the error reported will include the correct source file and line number
+ # data for the error. Without this, only the error message is retained and the location information is
+ # Line 1, Column 1, of the outer most script file, which is, of course, completely useless.
+ throw
+}
+finally
+{
+ Pop-Location
+ $env:Path = $oldPath
+}
+
+Write-Information "Done build"
diff --git a/PsModules/RepoBuild/Private/ConvertTo-BuildIndex.ps1 b/PsModules/RepoBuild/Private/ConvertTo-BuildIndex.ps1
deleted file mode 100644
index 0b8bc0e..0000000
--- a/PsModules/RepoBuild/Private/ConvertTo-BuildIndex.ps1
+++ /dev/null
@@ -1,23 +0,0 @@
-function ConvertTo-BuildIndex
-{
-<#
-.SYNOPSIS
- Converts a TimeStamp into a build index
-
-.DESCRIPTION
- The algorithm used is the same as the package published. The resulting index is a 32bit value that
- is a combination of the number of days since a fixed point (Upper 16 bits) and the number of seconds since
- midnight (on the day of the input time stamp) divided by 2 {Lower 16 bits)
-#>
- param(
- [Parameter(Mandatory=$true, ValueFromPipeLine)]
- [DateTime]$timeStamp
- )
-
- $timeStamp = $timeStamp.ToUniversalTime()
- $midnightTodayUtc = [DateTime]::new($timeStamp.Year, $timeStamp.Month, $timeStamp.Day, 0, 0, 0, [DateTimeKind]::Utc)
- $baseDate = [DateTime]::new(2000, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)
- $buildNumber = ([Uint32]($timeStamp - $baseDate).Days) -shl 16
- $buildNUmber += [UInt16](($timeStamp - $midnightTodayUtc).TotalSeconds / 2)
- return $buildNumber
-}
diff --git a/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1 b/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
index 61bef94..43dd2b5 100644
--- a/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
+++ b/PsModules/RepoBuild/Public/Initialize-BuildEnvironment.ps1
@@ -1,222 +1,3 @@
-class PreReleaseVersion
-{
- [ValidateSet("alpha", "beta", "delta", "epsilon", "gamma", "kappa", "prerelease", "rc")]
- [string] $Name;
-
- [ValidateSet("a", "b", "d", "e", "g", "k", "p", "r")]
- [string] $ShortName;
-
- [ValidateRange(-1,7)]
- [int] $Index;
-
- [ValidateRange(0,99)]
- [int] $Number;
-
- [ValidateRange(0,99)]
- [int] $Fix;
-
- PreReleaseVersion([hashtable]$buildVersionXmlData)
- {
- $preRelName = $buildVersionXmlData['PreReleaseName']
-
- if( ![string]::IsNullOrWhiteSpace( $preRelName ) )
- {
- $this.Index = [PreReleaseVersion]::GetPrerelIndex($preRelName)
- if($this.Index -ge 0)
- {
- $this.Name = [PreReleaseVersion]::PreReleaseNames[$this.Index]
- $this.ShortName = [PreReleaseVersion]::PreReleaseShortNames[$this.Index]
- }
-
- $this.Number = $buildVersionXmlData['PreReleaseNumber'];
- $this.Fix = $buildVersionXmlData['PreReleaseFix'];
- }
- else
- {
- $this.Index = -1;
- }
- }
-
- [string] ToString([bool] $useShortForm = $false)
- {
- $hasPreRel = $this.Index -ge 0
-
- $bldr = [System.Text.StringBuilder]::new()
- if($hasPreRel)
- {
- $bldr.Append('-').Append($useShortForm ? $this.ShortName : $this.Name)
- $delimFormat = $useShortForm ? '-{0:D02}' : '.{0}'
- if(($this.Number -gt 0))
- {
- $bldr.AppendFormat($delimFormat, $this.Number)
- if(($this.Fix -gt 0))
- {
- $bldr.AppendFormat($delimFormat, $this.Fix)
- }
- }
- }
-
- return $bldr.ToString()
- }
-
- hidden static [int] GetPrerelIndex([string] $preRelName)
- {
- $preRelIndex = -1
- if(![string]::IsNullOrWhiteSpace($preRelName))
- {
- $preRelIndex = [PreReleaseVersion]::PreRleaseNames |
- ForEach-Object {$index=0} {@{Name = $_; Index = $index++}} |
- Where-Object {$_["Name"] -ieq $preRelName} |
- ForEach-Object {$_["Index"]} |
- Select-Object -First 1
-
- # if not found in long names, test against the short names
- if($preRelIndex -lt 0)
- {
- $preRelIndex = [PreReleaseVersion]::PreReleaseShortNames |
- ForEach-Object {$index=0} {@{Name = $_; Index = $index++}} |
- Where-Object {$_["Name"] -ieq $preRelName} |
- ForEach-Object {$_["Index"]} |
- Select-Object -First 1
- }
- }
- return $preRelIndex
- }
-
- hidden static [string[]] $PreReleaseNames = @("alpha", "beta", "delta", "epsilon", "gamma", "kappa", "prerelease", "rc" );
- hidden static [string[]] $PreReleaseShortNames = @("a", "b", "d", "e", "g", "k", "p", "r");
-}
-
-class CSemVer
-{
- [ValidateRange(0,99999)]
- [int] $Major;
-
- [ValidateRange(0,49999)]
- [int] $Minor;
-
- [ValidateRange(0,9999)]
- [int] $Patch;
-
- [ValidateLength(0,20)]
- [string] $BuildMetadata;
-
- [ValidatePattern('\A[a-z0-9-]+\Z')]
- [string] $CiBuildIndex;
-
- [ValidatePattern('\A[a-z0-9-]+\Z')]
- [string] $CiBuildName;
-
- [ulong] $OrderedVersion;
-
- [Version] $FileVersion;
-
- [PreReleaseVersion] $PreReleaseVersion;
-
- CSemVer([hashtable]$buildVersionXmlData)
- {
- $this.Major = $buildVersionXmlData["BuildMajor"]
- $this.Minor = $buildVersionXmlData["BuildMinor"]
- $this.Patch = $buildVersionXmlData["BuildPatch"]
- if($buildVersionXmlData["PreReleaseName"])
- {
- $this.PreReleaseVersion = [PreReleaseVersion]::new($buildVersionXmlData)
- if(!$this.PreReleaseVersion)
- {
- throw "Internal ERROR: PreReleaseVersion version is NULL!"
- }
- }
-
- $this.BuildMetadata = $buildVersionXmlData["BuildMetadata"]
-
- $this.CiBuildName = $buildVersionXmlData["CiBuildName"];
- $this.CiBuildIndex = $buildVersionXmlData["CiBuildIndex"];
-
- if( (![string]::IsNullOrEmpty( $this.CiBuildName )) -and [string]::IsNullOrEmpty( $this.CiBuildIndex ) )
- {
- throw "CiBuildIndex is required if CiBuildName is provided";
- }
-
- if( (![string]::IsNullOrEmpty( $this.CiBuildIndex )) -and [string]::IsNullOrEmpty( $this.CiBuildName ) )
- {
- throw "CiBuildName is required if CiBuildIndex is provided";
- }
-
- $this.OrderedVersion = [CSemVer]::GetOrderedVersion($this.Major, $this.Minor, $this.Patch, $this.PreReleaseVersion)
- $fileVer64 = $this.OrderedVersion -shl 1
- if($this.CiBuildIndex -and $this.CiBuildName)
- {
- $fileVer64 += 1;
- }
-
- $this.FileVersion = [CSemVer]::ConvertToVersion($fileVer64)
- }
-
- [string] ToString([bool] $includeMetadata, [bool]$useShortForm)
- {
- $bldr = [System.Text.StringBuilder]::new()
- $bldr.AppendFormat('{0}.{1}.{2}', $this.Major, $this.Minor, $this.Patch)
- if($this.PreReleaseVersion)
- {
- $bldr.Append($this.PreReleaseVersion.ToString($useShortForm))
- }
-
- $hasPreRel = $this.PreReleaseVersion -and $this.PreReleaseVersion.Index -ge 0
- if($this.CiBuildIndex -and $this.CiBuildName)
- {
- $bldr.Append($hasPreRel ? '.' : '--')
- $bldr.AppendFormat('ci.{0}.{1}', $this.CiBuildIndex, $this.CiBuildName)
- }
-
- if(![string]::IsNullOrWhitespace($this.BuildMetadata) -and $includeMetadata)
- {
- $bldr.AppendFormat( '+{0}', $this.BuildMetadata )
- }
-
- return $bldr.ToString();
- }
-
- [string] ToString()
- {
- return $this.ToString($true, $false);
- }
-
- hidden static [ulong] GetOrderedVersion($Major, $Minor, $Patch, [PreReleaseVersion] $PreReleaseVersion)
- {
- [ulong] $MulNum = 100;
- [ulong] $MulName = $MulNum * 100;
- [ulong] $MulPatch = ($MulName * 8) + 1;
- [ulong] $MulMinor = $MulPatch * 10000;
- [ulong] $MulMajor = $MulMinor * 50000;
-
- [ulong] $retVal = (([ulong]$Major) * $MulMajor) + (([ulong]$Minor) * $MulMinor) + ((([ulong]$Patch) + 1) * $MulPatch);
- if( $PreReleaseVersion -and $PreReleaseVersion.Index -ge 0 )
- {
- $retVal -= $MulPatch - 1;
- $retVal += [ulong]($PreReleaseVersion.Index) * $MulName;
- $retVal += [ulong]($PreReleaseVersion.Number) * $MulNum;
- $retVal += [ulong]($PreReleaseVersion.Fix);
- }
- return $retVal;
- }
-
- hidden static [Version] ConvertToVersion([ulong]$value)
- {
- $revision = [ushort]($value % 65536);
- $rem = [ulong](($value - $revision) / 65536);
-
- $build = [ushort]($rem % 65536);
- $rem = ($rem - $build) / 65536;
-
- $minorNum = [ushort]($rem % 65536);
- $rem = ($rem - $minorNum) / 65536;
-
- $majorNum = [ushort]($rem % 65536);
-
- return [Version]::new( $majorNum, $minorNum, $build, $revision );
- }
-}
-
function Initialize-BuildEnvironment
{
<#
@@ -291,65 +72,6 @@ function Initialize-BuildEnvironment
# information and generally makes it HARDER to see what's going on, not easier as it claims.
$env:MSBUILDTERMINALLOGGER='off'
- if($FullInit)
- {
- # PowerShell doesn't export enums from a script module, so the type of this return is
- # "unpronounceable" [In C++ terminology]. So, convert it to a string so it is usable in this
- # script.
- [string] $buildKind = Get-CurrentBuildKind
-
- $verInfo = Get-ParsedBuildVersionXML -BuildInfo $buildInfo
- if($buildKind -ne "ReleaseBuild")
- {
- $verInfo['CiBuildIndex'] = ConvertTo-BuildIndex $env:BuildTime
- }
-
- switch($buildKind)
- {
- "LocalBuild" { $verInfo['CiBuildName'] = "ZZZ" }
- "PullRequestBuild" { $verInfo['CiBuildName'] = "PRQ" }
- "CiBuild" { $verInfo['CiBuildName'] = "BLD" }
- "ReleaseBuild" { }
- default {throw "unknown build kind" }
- }
-
- # Generate props file with the version information for this build
- # While it is plausible to use ENV vars to overload or set properties
- # that leads to dangling values during development, which makes for
- # a LOT of wasted time chasing down why a change didn't work...
- $csemVer = [CSemVer]::New($verInfo)
- $xmlDoc = [System.Xml.XmlDocument]::new()
- $projectElement = $xmlDoc.CreateElement("Project")
- $xmlDoc.AppendChild($projectElement) | Out-Null
-
- $propGroupElement = $xmlDoc.CreateElement("PropertyGroup")
- $projectElement.AppendChild($propGroupElement) | Out-Null
-
- $fileVersionElement = $xmlDoc.CreateElement("FileVersion")
- $fileVersionElement.InnerText = $csemVer.FileVersion.ToString()
- $propGroupElement.AppendChild($fileVersionElement) | Out-Null
-
- $packageVersionElement = $xmlDoc.CreateElement("PackageVersion")
- $packageVersionElement.InnerText = $csemVer.ToString($false,$true) # short form of version
- $propGroupElement.AppendChild($packageVersionElement) | Out-Null
-
- $productVersionElement = $xmlDoc.CreateElement("ProductVersion")
- $productVersionElement.InnerText = $csemVer.ToString($true, $false) # long form of version
- $propGroupElement.AppendChild($productVersionElement) | Out-Null
-
- $assemblyVersionElement = $xmlDoc.CreateElement("AssemblyVersion")
- $assemblyVersionElement.InnerText = $csemVer.FileVersion.ToString()
- $propGroupElement.AppendChild($assemblyVersionElement) | Out-Null
-
- $informationalVersionElement = $xmlDoc.CreateElement("InformationalVersion")
- $informationalVersionElement.InnerText = $csemVer.ToString($true, $false) # long form of version
- $propGroupElement.AppendChild($informationalVersionElement) | Out-Null
-
- $buildGeneratedPropsPath = Join-Path $buildInfo["RepoRootPath"] "GeneratedVersion.props"
- $xmlDoc.Save($buildGeneratedPropsPath)
-
- }
-
return $buildInfo
}
catch
diff --git a/PsModules/RepoBuild/RepoBuild.pssproj b/PsModules/RepoBuild/RepoBuild.pssproj
index f5ae29c..9287af3 100644
--- a/PsModules/RepoBuild/RepoBuild.pssproj
+++ b/PsModules/RepoBuild/RepoBuild.pssproj
@@ -7,7 +7,8 @@
MyApplication
MyApplication
RepoBuild
- LlvmCore
+
+
true
@@ -46,23 +47,19 @@
within it.
-->
-
-
-
-
+
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
-
-
-
-
+
+
+
+
-
+
\ No newline at end of file
diff --git a/src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.targets b/src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.targets
index 6b18ae2..9536cc5 100644
--- a/src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.targets
+++ b/src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.targets
@@ -72,10 +72,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/Ubiquity.NET.Versioning.slnx b/src/Ubiquity.NET.Versioning.slnx
index 738fb5f..2d070b0 100644
--- a/src/Ubiquity.NET.Versioning.slnx
+++ b/src/Ubiquity.NET.Versioning.slnx
@@ -12,6 +12,7 @@
+
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
index 37432df..0ad2410 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
@@ -11,6 +11,7 @@
using System.Runtime.Loader;
using System.Xml.Linq;
+using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Utilities.ProjectCreation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -47,16 +48,9 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
var globalProperties = new Dictionary
{
["BuildVersionXml"] = Path.Combine(RepoRoot, "BuildVersion.xml"),
+ ["BuildTime"] = GetBuildTime()
};
- string envBuildTime = TestModuleFixtures.EnvBuildTime;
- bool isIDEBuild = true;
- if( !string.IsNullOrWhiteSpace(envBuildTime))
- {
- globalProperties["BuildTime"] = envBuildTime;
- isIDEBuild = false;
- }
-
using var collection = new ProjectCollection(globalProperties);
var (testProject, props) = CreateTestProjectAndInvokeTestedPackage(
@@ -113,35 +107,7 @@ where val is not null
select val
).Single();
- // Check for limited version information in an IDE run as the "BuildTime" environment,
- // which is translated to CIBuildIndex, is hard coded to the max value. Additionally,
- // in a release build there is NO CI information to extract so it is inconclusive.
- // Full testing is formally inconclusive but at least validate what is plausible and
- // treat the rest as inconclusive
- if (isIDEBuild)
- {
- Context.WriteLine($"AssemblyInformationalVersion {informationalVersion}");
-
- Assert.IsNotNull(props.InformationalVersion);
-
- // release builds won't have ANY CI information.
- int cipos = props.InformationalVersion.IndexOf(".ci", StringComparison.Ordinal);
- if(cipos > 0)
- {
- string propsVersion = props.InformationalVersion[ ..cipos];
-
- int ideCiBuildPos = informationalVersion.IndexOf(".ci");
- informationalVersion = informationalVersion[ ..ideCiBuildPos];
-
- Assert.AreEqual(propsVersion, informationalVersion);
- }
-
- Assert.Inconclusive("IDE build verification is inconclusive; but at least matches expectations");
- }
- else
- {
- Assert.AreEqual(props.InformationalVersion, informationalVersion);
- }
+ Assert.AreEqual(props.InformationalVersion, informationalVersion);
}
finally
{
@@ -394,5 +360,17 @@ private string CreateBuildVersionXml(
Context.WriteLine( $"BuildVersionXML written to: '{retVal}'" );
return retVal;
}
+
+ private static string GetBuildTime( )
+ {
+ using var dummyCollection = new ProjectCollection();
+ var options = new ProjectOptions()
+ {
+ ProjectCollection = dummyCollection
+ };
+
+ var project = Project.FromFile(Path.Combine(TestModuleFixtures.RepoRoot, "GeneratedVersion.props"), options);
+ return project.GetPropertyValue("BuildTime");
+ }
}
}
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
index 2aa1766..9b975d5 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
@@ -5,7 +5,9 @@
// -----------------------------------------------------------------------
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Globalization;
using System.IO;
using Microsoft.Build.Utilities.ProjectCreation;
@@ -13,6 +15,14 @@
namespace Ubiquity.Versioning.Build.Tasks.UT
{
+ internal enum BuildKind
+ {
+ LocalBuild,
+ PullRequestBuild,
+ CiBuild,
+ ReleaseBuild,
+ }
+
// Provides common location for one time initialization for all tests in this assembly
// Doing the package repo construction here, allows tests to run in parallel without
// hitting access denied errors due to the use of the location in some other test.
@@ -22,39 +32,35 @@ public static class TestModuleFixtures
[AssemblyInitialize]
public static void AssemblyInitialize( TestContext ctx )
{
- ArgumentNullException.ThrowIfNull( ctx );
- ArgumentException.ThrowIfNullOrWhiteSpace( ctx.TestRunDirectory );
+ ArgumentNullException.ThrowIfNull(ctx);
+ ArgumentException.ThrowIfNullOrWhiteSpace(ctx.TestRunDirectory);
// This assumes the solutions '.runsettings' file has re-directed the run output
// to a well known location. There's no way to "pass" in the location to the build
// of this test.
- string buildOutputPath = Path.GetFullPath( Path.Combine(ctx.TestRunDirectory, "..", ".."));
- string buildRoot = Path.GetFullPath( Path.Combine(buildOutputPath, ".."));
+ BuildOutputPath = Path.GetFullPath(Path.Combine(ctx.TestRunDirectory, "..", ".."));
+ RepoRoot = Path.GetFullPath(Path.Combine(BuildOutputPath, ".."));
// Generate fake directory.build.[props|targets] in the test run directory to prevent MSBUILD
// from searching beyond these to find the REPO one in the root (It contains things that will
// interfere with deterministic testing).
ProjectCreator.Create()
- .Save( Path.Combine( ctx.TestRunDirectory, "Directory.Build.props" ) );
+ .Save(Path.Combine(ctx.TestRunDirectory, "Directory.Build.props"));
ProjectCreator.Create()
- .Save( Path.Combine( ctx.TestRunDirectory, "Directory.Build.targets" ) );
+ .Save(Path.Combine(ctx.TestRunDirectory, "Directory.Build.targets"));
// Ensure environment is clear of any overrides to ensure tests are validating the correct behavior
// Individual tests MAY set these again. To prevent interference with other tests, any such test
// needing to set these must restore them (even on exceptional exit) via a try/finally or using pattern.
- foreach(string envVar in TaskInputPropertyNames)
+ foreach (string envVar in TaskInputPropertyNames)
{
string? value = Environment.GetEnvironmentVariable(envVar);
- if(value is not null)
+ if (value is not null)
{
- // Save the environment build time for use with task assembly builds as that
- // value is needed to determine the build index used for non-IDE builds
- if(envVar == "BuildTime")
- {
- EnvBuildTime = value;
- }
-
+ // Save the environment var value for use with task assembly build verification
+ // as these are needed to verify the assembly build details
+ OriginalInputVars.Add(envVar, value);
Environment.SetEnvironmentVariable(envVar, null);
}
}
@@ -63,8 +69,8 @@ public static void AssemblyInitialize( TestContext ctx )
// test project files or the settings will NOT apply!
PackageRepo = PackageRepository.Create(
ctx.TestRunDirectory, // '.nuget/packages' repo folder goes here
- new Uri( Path.Combine( buildOutputPath, "NuGet" ) ), // Local feed (Contains location of the build of the package under test)
- new Uri( "https://api.nuget.org/v3/index.json" ) // standard NuGet Feed
+ new Uri(Path.Combine(BuildOutputPath, "NuGet")), // Local feed (Contains location of the build of the package under test)
+ new Uri("https://api.nuget.org/v3/index.json") // standard NuGet Feed
);
}
@@ -74,9 +80,63 @@ public static void AssemblyCleanup( )
PackageRepo?.Dispose();
}
- internal static string EnvBuildTime { get; private set; } = string.Empty;
+ internal static string BuildOutputPath { get; private set; } = string.Empty;
+
+ internal static string RepoRoot { get; private set; } = string.Empty;
+
+ // This mirrors the implementation in CommonBuild/GetCurrentBuildKind.ps1
+ // This is very build environment Back-end specific and currently only supports
+ // APPVEYOR (unused or tested in a LONG time!) and GitHub Actions (Currently active)
+ internal static BuildKind BuildKind
+ {
+ get
+ {
+ var retVal = BuildKind.LocalBuild;
+ bool appveyorBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("APPVEYOR") ?? "false", CultureInfo.InvariantCulture);
+ bool gitHubActionsBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS") ?? "false", CultureInfo.InvariantCulture);
+
+ // isAutomatedBuild is the top level gate (e.g. if it is false, all the others must be false)
+ bool isAutomatedBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false", CultureInfo.InvariantCulture)
+ || appveyorBuild
+ || gitHubActionsBuild;
+ if (isAutomatedBuild)
+ {
+ // PR and release builds have externally detected indicators that are tested
+ // below, so default to a CiBuild (e.g. not a PR, And not a RELEASE)
+ retVal = BuildKind.CiBuild;
+ bool isPullRequestBuild = Environment.GetEnvironmentVariable("GITHUB_BASE_REF") != null
+ || Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER") != null;
+ if (isPullRequestBuild)
+ {
+ retVal = BuildKind.PullRequestBuild;
+ }
+ else
+ {
+ bool isReleaseBuild = false;
+ if (appveyorBuild)
+ {
+ isReleaseBuild = Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG") != null;
+ }
+ else if (gitHubActionsBuild)
+ {
+ string commitRef = Environment.GetEnvironmentVariable("GITHUB_REF") ?? string.Empty;
+ isReleaseBuild = commitRef.StartsWith("refs/tags");
+ }
+
+ if (isReleaseBuild)
+ {
+ retVal = BuildKind.ReleaseBuild;
+ }
+ }
+ }
+
+ return retVal;
+ }
+ }
+
+ internal static readonly Dictionary OriginalInputVars = [];
- private static void CopyFile(string srcDir, string fileName, string dstDir)
+ private static void CopyFile( string srcDir, string fileName, string dstDir )
{
File.Copy(Path.Combine(srcDir, fileName), Path.Combine(dstDir, fileName), overwrite: true);
}
From 93c3c278b302204b96f43ed7cfc24b7b9a7e78c2 Mon Sep 17 00:00:00 2001
From: smaillet <25911635+smaillet@users.noreply.github.com>
Date: Sun, 1 Jun 2025 14:54:47 -0700
Subject: [PATCH 4/5] Added support to extract the CiBuildName from the build
environment * Added additional logging to build tasks to aid with diagnostics
- These are now low priority messages so that don't normally appear in
output * Removed determination of build kind from tests as that's a build
script only thing not needed in the tests now.
---
New-GeneratedVersionProps.ps1 | 13 ++++-
.../CreateVersionInfo.cs | 17 ++++--
.../GetBuildIndexFromTime.cs | 8 +++
.../ParseBuildVersionXml.cs | 13 ++++-
.../BuildTaskTests.cs | 25 ++++++--
.../ProjectInstanceExtensions.cs | 28 +++++----
.../TestModuleFixtures.cs | 58 -------------------
7 files changed, 79 insertions(+), 83 deletions(-)
diff --git a/New-GeneratedVersionProps.ps1 b/New-GeneratedVersionProps.ps1
index 0f94dfd..8739abe 100644
--- a/New-GeneratedVersionProps.ps1
+++ b/New-GeneratedVersionProps.ps1
@@ -35,18 +35,21 @@ function ConvertTo-BuildIndex
.DESCRIPTION
The algorithm used is the same as the package published. The resulting index is a 32bit value that
is a combination of the number of days since a fixed point (Upper 16 bits) and the number of seconds since
- midnight (on the day of the input time stamp) divided by 2 {Lower 16 bits)
+ midnight (on the day of the input time stamp) divided by 2 (Lower 16 bits)
#>
param(
[Parameter(Mandatory=$true, ValueFromPipeLine)]
[DateTime]$timeStamp
)
+ $commonBaseDate = [DateTime]::new(2000, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)
+
$timeStamp = $timeStamp.ToUniversalTime()
$midnightTodayUtc = [DateTime]::new($timeStamp.Year, $timeStamp.Month, $timeStamp.Day, 0, 0, 0, [DateTimeKind]::Utc)
- $baseDate = [DateTime]::new(2000, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)
- $buildNumber = ([Uint32]($timeStamp - $baseDate).Days) -shl 16
+
+ $buildNumber = ([Uint32]($timeStamp - $commonBaseDate).Days) -shl 16
$buildNUmber += [UInt16](($timeStamp - $midnightTodayUtc).TotalSeconds / 2)
+
return $buildNumber
}
@@ -348,6 +351,10 @@ try
$buildTimeElement = $xmlDoc.CreateElement("BuildTime")
$buildTimeElement.InnerText = $env:BuildTime
$propGroupElement.AppendChild($buildTimeElement) | Out-Null
+
+ $ciBuildNameElement = $xmlDoc.CreateElement("CiBuildName")
+ $ciBuildNameElement.InnerText = $verInfo['CiBuildName']
+ $propGroupElement.AppendChild($ciBuildNameElement) | Out-Null
}
$buildGeneratedPropsPath = Join-Path $buildInfo["RepoRootPath"] "GeneratedVersion.props"
diff --git a/src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs b/src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs
index 95d760d..34fc216 100644
--- a/src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs
+++ b/src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs
@@ -71,7 +71,7 @@ public override bool Execute( )
{
try
{
- Log.LogMessage(MessageImportance.High, $"+{nameof(CreateVersionInfo)} Task");
+ Log.LogMessage(MessageImportance.Low, $"+{nameof(CreateVersionInfo)} Task");
if(!ValidateInput())
{
@@ -79,14 +79,21 @@ public override bool Execute( )
return false;
}
+ Log.LogMessage(MessageImportance.Low, "CiBuildIndex={0}", CiBuildIndex ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "CiBuildName={0}", CiBuildName ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "BuildMeta={0}", BuildMeta ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "PreReleaseName={0}", PreReleaseName ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "PreReleaseNumber={0}", PreReleaseNumber);
+ Log.LogMessage(MessageImportance.Low, "PreReleaseFix={0}", PreReleaseFix);
+
int preRelIndex = ComputePreReleaseIndex( PreReleaseName!);
- Log.LogMessage(MessageImportance.High, "PreRelIndex={0}", preRelIndex);
+ Log.LogMessage(MessageImportance.Low, "PreRelIndex={0}", preRelIndex);
CSemVer = CreateSemVerString( preRelIndex );
- Log.LogMessage(MessageImportance.High, "CSemVer={0}", CSemVer ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "CSemVer={0}", CSemVer ?? string.Empty);
ShortCSemVer = CreateSemVerString( preRelIndex, useShortForm: true );
- Log.LogMessage(MessageImportance.High, "ShortCSemVer={0}", ShortCSemVer ?? string.Empty);
+ Log.LogMessage(MessageImportance.Low, "ShortCSemVer={0}", ShortCSemVer ?? string.Empty);
SetFileVersion( preRelIndex );
return true;
@@ -98,7 +105,7 @@ public override bool Execute( )
}
finally
{
- Log.LogMessage(MessageImportance.High, $"-{nameof(CreateVersionInfo)} Task");
+ Log.LogMessage(MessageImportance.Low, $"-{nameof(CreateVersionInfo)} Task");
}
}
diff --git a/src/Ubiquity.NET.Versioning.Build.Tasks/GetBuildIndexFromTime.cs b/src/Ubiquity.NET.Versioning.Build.Tasks/GetBuildIndexFromTime.cs
index 774086b..4869505 100644
--- a/src/Ubiquity.NET.Versioning.Build.Tasks/GetBuildIndexFromTime.cs
+++ b/src/Ubiquity.NET.Versioning.Build.Tasks/GetBuildIndexFromTime.cs
@@ -23,16 +23,24 @@ public class GetBuildIndexFromTime
public override bool Execute( )
{
+ Log.LogMessage(MessageImportance.Low, $"+{nameof(GetBuildIndexFromTime)} Task");
+
// establish an increasing build index based on the number of seconds from a common UTC date
var timeStamp = TimeStamp.ToUniversalTime( );
+ Log.LogMessage(MessageImportance.Low, $"Time Stamp(UTC; ISO-8601): {timeStamp:o}");
// Upper 16 bits of the build number is the number of days since the common base value
uint buildNumber = ((uint)(timeStamp - CommonBaseDate).Days) << 16;
+ Log.LogMessage(MessageImportance.Low, $"BuildNumber (upper): 0x{buildNumber:X04}");
// Lower 16 bits is the number of seconds (divided by 2) since midnight (on the date of the time stamp)
var midnightTodayUtc = new DateTime( timeStamp.Year, timeStamp.Month, timeStamp.Day, 0, 0, 0, DateTimeKind.Utc );
buildNumber += (ushort)((timeStamp - midnightTodayUtc).TotalSeconds / 2);
+ Log.LogMessage(MessageImportance.Low, $"BuildNumber (full): 0x{buildNumber:X04}");
+
BuildIndex = buildNumber.ToString( CultureInfo.InvariantCulture );
+ Log.LogMessage(MessageImportance.Low, $"BuildIndex set to: {BuildIndex}");
+ Log.LogMessage(MessageImportance.Low, $"-{nameof(GetBuildIndexFromTime)} Task");
return true;
}
diff --git a/src/Ubiquity.NET.Versioning.Build.Tasks/ParseBuildVersionXml.cs b/src/Ubiquity.NET.Versioning.Build.Tasks/ParseBuildVersionXml.cs
index f3ff39a..85a1b91 100644
--- a/src/Ubiquity.NET.Versioning.Build.Tasks/ParseBuildVersionXml.cs
+++ b/src/Ubiquity.NET.Versioning.Build.Tasks/ParseBuildVersionXml.cs
@@ -41,6 +41,8 @@ public override bool Execute( )
{
try
{
+ Log.LogMessage(MessageImportance.Low, $"+{nameof(ParseBuildVersionXml)} Task");
+
using var stream = File.OpenText( BuildVersionXml );
var xdoc = System.Xml.Linq.XDocument.Load( stream, System.Xml.Linq.LoadOptions.None );
var data = xdoc.Element( "BuildVersionData" );
@@ -50,26 +52,32 @@ public override bool Execute( )
switch( attrib.Name.LocalName )
{
case "BuildMajor":
+ Log.LogMessage(MessageImportance.Low, $"BuildMajor: {attrib.Value}");
BuildMajor = attrib.Value;
break;
case "BuildMinor":
+ Log.LogMessage(MessageImportance.Low, $"BuildMinor: {attrib.Value}");
BuildMinor = attrib.Value;
break;
case "BuildPatch":
+ Log.LogMessage(MessageImportance.Low, $"BuildPatch: {attrib.Value}");
BuildPatch = attrib.Value;
break;
case "PreReleaseName":
+ Log.LogMessage(MessageImportance.Low, $"PreReleaseName: {attrib.Value}");
PreReleaseName = attrib.Value;
break;
case "PreReleaseNumber":
+ Log.LogMessage(MessageImportance.Low, $"PreReleaseNumber: {attrib.Value}");
PreReleaseNumber = attrib.Value;
break;
case "PreReleaseFix":
+ Log.LogMessage(MessageImportance.Low, $"PreReleaseFix: {attrib.Value}");
PreReleaseFix = attrib.Value;
break;
@@ -82,15 +90,18 @@ public override bool Execute( )
// correct malformed values
if( string.IsNullOrWhiteSpace( PreReleaseName ) )
{
+ Log.LogMessage(MessageImportance.Low, "PreReleaseName not provided, forcing PreReleaseNumber and PreReleaseFix == 0");
PreReleaseNumber = "0";
PreReleaseFix = "0";
}
- if( PreReleaseNumber == "0" )
+ if( PreReleaseNumber == "0" && PreReleaseFix != "0")
{
+ Log.LogMessage(MessageImportance.Low, "PreReleaseNumber is 0; forcing PreReleaseFix == 0");
PreReleaseFix = "0";
}
+ Log.LogMessage(MessageImportance.Low, $"-{nameof(ParseBuildVersionXml)} Task");
return true;
}
catch(Exception ex)
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
index 0ad2410..828189e 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
@@ -48,9 +48,26 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
var globalProperties = new Dictionary
{
["BuildVersionXml"] = Path.Combine(RepoRoot, "BuildVersion.xml"),
- ["BuildTime"] = GetBuildTime()
};
+ // For a CI build load the build time and ciBuild name from the generatedversion.props file
+ // so the test knows what to expect. This verifies that the task creation of versions agrees
+ // with how the build scripts created the version information. (Not the task in general)
+ // That is, this test case is NOT free of the build environment and MUST take it into consideration
+ // to properly verify the build scripts are doing things correctly. The CI Index is created from
+ // the time stamp and the name is either provided directly as a build property or computed in the
+ // Ubiquity.NET.Versioning.Build.Tasks.props file.
+ var (buildTime, ciBuildName) = GetGeneratedCiBuildInfo();
+ if(!string.IsNullOrWhiteSpace(ciBuildName))
+ {
+ globalProperties["CiBuildName"] = ciBuildName;
+ }
+
+ if(!string.IsNullOrWhiteSpace(buildTime))
+ {
+ globalProperties["BuildTime"] = buildTime;
+ }
+
using var collection = new ProjectCollection(globalProperties);
var (testProject, props) = CreateTestProjectAndInvokeTestedPackage(
@@ -272,7 +289,7 @@ public void BuildVersionXmlIsUsed( string targetFramework)
Assert.IsTrue( resolveResult );
// Since this project uses an imported target, it won't even exist until AFTER ResolvePackageDependencies[DesignTime|ForBuild] comes along.
- var result = testProject.ProjectInstance.Build("PrepareVersioningForBuild");
+ var (result, output) = testProject.ProjectInstance.Build("PrepareVersioningForBuild");
Assert.IsNotNull( result );
Assert.IsNotNull( result.ProjectStateAfterBuild );
@@ -361,7 +378,7 @@ private string CreateBuildVersionXml(
return retVal;
}
- private static string GetBuildTime( )
+ private static (string BuildTime, string CiBuildName) GetGeneratedCiBuildInfo( )
{
using var dummyCollection = new ProjectCollection();
var options = new ProjectOptions()
@@ -370,7 +387,7 @@ private static string GetBuildTime( )
};
var project = Project.FromFile(Path.Combine(TestModuleFixtures.RepoRoot, "GeneratedVersion.props"), options);
- return project.GetPropertyValue("BuildTime");
+ return (project.GetPropertyValue("BuildTime"), project.GetPropertyValue("CiBuildName"));
}
}
}
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/ProjectInstanceExtensions.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/ProjectInstanceExtensions.cs
index 575a644..e2d4271 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/ProjectInstanceExtensions.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/ProjectInstanceExtensions.cs
@@ -8,19 +8,20 @@
using System.Globalization;
using Microsoft.Build.Execution;
+using Microsoft.Build.Utilities.ProjectCreation;
namespace Ubiquity.Versioning.Build.Tasks.UT
{
internal static class ProjectInstanceExtensions
{
- public static T? GetPropertyAs(this ProjectInstance self, string name, T? defaultValue = null)
+ public static T? GetPropertyAs( this ProjectInstance self, string name, T? defaultValue = null )
where T : struct
{
var prop = self.GetProperty(name);
return prop is null ? defaultValue : (T?)Convert.ChangeType(prop.EvaluatedValue, typeof(T), CultureInfo.InvariantCulture);
}
- public static string? GetOptionalProperty(this ProjectInstance self, string name, string? defaultValue = null)
+ public static string? GetOptionalProperty( this ProjectInstance self, string name, string? defaultValue = null )
{
var prop = self.GetProperty(name);
return prop is null ? defaultValue : prop.EvaluatedValue;
@@ -28,17 +29,20 @@ internal static class ProjectInstanceExtensions
// Sadly, project creator library doesn't have support for after build state retrieval...
// Fortunately, it is fairly easy to create an extension to handle that scenario.
- public static BuildResult Build(this ProjectInstance self, params string[] targetsToBuild)
+ public static (BuildResult BuildResult, BuildOutput Output) Build( this ProjectInstance self, params string[] targetsToBuild )
{
- return BuildManager.DefaultBuildManager.Build(
- new BuildParameters(),
- new BuildRequestData(
- self,
- targetsToBuild,
- new HostServices(),
- BuildRequestDataFlags.ProvideProjectStateAfterBuild
- )
- );
+ // !@#$ project creator hides new and uses a dumb wrapper for create. [Sigh...]
+ var buildOutput = BuildOutput.Create();
+ var result = BuildManager.DefaultBuildManager.Build(
+ new BuildParameters() { Loggers = [buildOutput] },
+ new BuildRequestData(
+ self,
+ targetsToBuild,
+ new HostServices(),
+ BuildRequestDataFlags.ProvideProjectStateAfterBuild
+ )
+ );
+ return (result, buildOutput);
}
}
}
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
index 9b975d5..84aad9c 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/TestModuleFixtures.cs
@@ -15,14 +15,6 @@
namespace Ubiquity.Versioning.Build.Tasks.UT
{
- internal enum BuildKind
- {
- LocalBuild,
- PullRequestBuild,
- CiBuild,
- ReleaseBuild,
- }
-
// Provides common location for one time initialization for all tests in this assembly
// Doing the package repo construction here, allows tests to run in parallel without
// hitting access denied errors due to the use of the location in some other test.
@@ -84,56 +76,6 @@ public static void AssemblyCleanup( )
internal static string RepoRoot { get; private set; } = string.Empty;
- // This mirrors the implementation in CommonBuild/GetCurrentBuildKind.ps1
- // This is very build environment Back-end specific and currently only supports
- // APPVEYOR (unused or tested in a LONG time!) and GitHub Actions (Currently active)
- internal static BuildKind BuildKind
- {
- get
- {
- var retVal = BuildKind.LocalBuild;
- bool appveyorBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("APPVEYOR") ?? "false", CultureInfo.InvariantCulture);
- bool gitHubActionsBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS") ?? "false", CultureInfo.InvariantCulture);
-
- // isAutomatedBuild is the top level gate (e.g. if it is false, all the others must be false)
- bool isAutomatedBuild = Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false", CultureInfo.InvariantCulture)
- || appveyorBuild
- || gitHubActionsBuild;
- if (isAutomatedBuild)
- {
- // PR and release builds have externally detected indicators that are tested
- // below, so default to a CiBuild (e.g. not a PR, And not a RELEASE)
- retVal = BuildKind.CiBuild;
- bool isPullRequestBuild = Environment.GetEnvironmentVariable("GITHUB_BASE_REF") != null
- || Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER") != null;
- if (isPullRequestBuild)
- {
- retVal = BuildKind.PullRequestBuild;
- }
- else
- {
- bool isReleaseBuild = false;
- if (appveyorBuild)
- {
- isReleaseBuild = Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG") != null;
- }
- else if (gitHubActionsBuild)
- {
- string commitRef = Environment.GetEnvironmentVariable("GITHUB_REF") ?? string.Empty;
- isReleaseBuild = commitRef.StartsWith("refs/tags");
- }
-
- if (isReleaseBuild)
- {
- retVal = BuildKind.ReleaseBuild;
- }
- }
- }
-
- return retVal;
- }
- }
-
internal static readonly Dictionary OriginalInputVars = [];
private static void CopyFile( string srcDir, string fileName, string dstDir )
From 9ecd69fbe5044f818f909dcdd899a9371b8da396 Mon Sep 17 00:00:00 2001
From: smaillet <25911635+smaillet@users.noreply.github.com>
Date: Sun, 1 Jun 2025 16:35:18 -0700
Subject: [PATCH 5/5] Adjusted tests to use the build index from the
generatedversion.props * Sadly the buildIndex from a time stamp is
non-deterministic making it impossible to create tests with an exact match.
- Tests would need to adjust to acount for "close enough" - or, the
better answer, I think, is that a new deterministic computation method is
needed.
---
New-GeneratedVersionProps.ps1 | 6 ++---
.../BuildTaskTests.cs | 26 ++++++++++---------
2 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/New-GeneratedVersionProps.ps1 b/New-GeneratedVersionProps.ps1
index 8739abe..383d51e 100644
--- a/New-GeneratedVersionProps.ps1
+++ b/New-GeneratedVersionProps.ps1
@@ -348,9 +348,9 @@ try
# to set some env vars etc...
if($buildKind -ne "ReleaseBuild")
{
- $buildTimeElement = $xmlDoc.CreateElement("BuildTime")
- $buildTimeElement.InnerText = $env:BuildTime
- $propGroupElement.AppendChild($buildTimeElement) | Out-Null
+ $ciBuildIndexElement = $xmlDoc.CreateElement("CiBuildIndex")
+ $ciBuildIndexElement.InnerText = $verInfo['CiBuildIndex']
+ $propGroupElement.AppendChild($ciBuildIndexElement) | Out-Null
$ciBuildNameElement = $xmlDoc.CreateElement("CiBuildName")
$ciBuildNameElement.InnerText = $verInfo['CiBuildName']
diff --git a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
index 828189e..e73b7ee 100644
--- a/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
+++ b/src/Ubiquity.Versioning.Build.Tasks.UT/BuildTaskTests.cs
@@ -50,22 +50,24 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
["BuildVersionXml"] = Path.Combine(RepoRoot, "BuildVersion.xml"),
};
- // For a CI build load the build time and ciBuild name from the generatedversion.props file
- // so the test knows what to expect. This verifies that the task creation of versions agrees
- // with how the build scripts created the version information. (Not the task in general)
- // That is, this test case is NOT free of the build environment and MUST take it into consideration
- // to properly verify the build scripts are doing things correctly. The CI Index is created from
- // the time stamp and the name is either provided directly as a build property or computed in the
- // Ubiquity.NET.Versioning.Build.Tasks.props file.
- var (buildTime, ciBuildName) = GetGeneratedCiBuildInfo();
+ // For a CI build load the ciBuildIndex d time and ciBuildName from the generatedversion.props file
+ // so the test knows what to expect. This does NOT verify the behavior of the tasks exactly unfortunately.
+ // There is a non-determinism in computing the index based on a time stamp in particular a single date/time
+ // string is converted based on seconds since midnight today (in UTC) so if two different implementations
+ // compute a value at a different time that varies by as much as 2 seconds, then they will produce different
+ // results even if behaving correctly. The use of seconds since midnight today makes it non-deterministic...
+ // Unfortunately that's the algorithm chosen, though since this is a major release (and a full rename that
+ // is something to re-visit) Until, that is deterministic, use the generated CI info all up. Other tests will
+ // need to validate the behavior of the task.
+ var (ciBuildIndex, ciBuildName) = GetGeneratedCiBuildInfo();
if(!string.IsNullOrWhiteSpace(ciBuildName))
{
globalProperties["CiBuildName"] = ciBuildName;
}
- if(!string.IsNullOrWhiteSpace(buildTime))
+ if(!string.IsNullOrWhiteSpace(ciBuildIndex))
{
- globalProperties["BuildTime"] = buildTime;
+ globalProperties["CiBuildIndex"] = ciBuildIndex;
}
using var collection = new ProjectCollection(globalProperties);
@@ -378,7 +380,7 @@ private string CreateBuildVersionXml(
return retVal;
}
- private static (string BuildTime, string CiBuildName) GetGeneratedCiBuildInfo( )
+ private static (string CiBuildIndex, string CiBuildName) GetGeneratedCiBuildInfo( )
{
using var dummyCollection = new ProjectCollection();
var options = new ProjectOptions()
@@ -387,7 +389,7 @@ private static (string BuildTime, string CiBuildName) GetGeneratedCiBuildInfo( )
};
var project = Project.FromFile(Path.Combine(TestModuleFixtures.RepoRoot, "GeneratedVersion.props"), options);
- return (project.GetPropertyValue("BuildTime"), project.GetPropertyValue("CiBuildName"));
+ return (project.GetPropertyValue("CiBuildIndex"), project.GetPropertyValue("CiBuildName"));
}
}
}