Skip to content

Commit

Permalink
Merge pull request #1153 from dotnet/addPrereleaseIdentifiers
Browse files Browse the repository at this point in the history
Add msbuild-provided prerelease identifiers
  • Loading branch information
AArnott authored Jan 14, 2025
2 parents 6a6124a + 96122d1 commit 2dfd066
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 7 deletions.
9 changes: 9 additions & 0 deletions docfx/docs/build-systems/msbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ Property | Default | Description
`NBGV_ThisAssemblyIncludesPackageVersion` | `false` | When `true`, a `NuGetPackageVersion` property is added to the `ThisAssembly` class.
`NBGV_UseAssemblyVersionInNativeVersion` | `true` | When `false`, uses the `AssemblyFileVersion` as a native `PRODUCTVERSION`.

### Items

The following MSBuild items may be declared in your project to customize the computed version:

Item type | Description
--|--
`BuildMetadata` | Adds `+ItemName` build metadata for each item to the computed version.
`PrereleaseIdentifier` | Adds `-ItemName` build metadata for each item to the computed version.

### Custom `ThisAssembly` static fields and constants

Custom constants may be added to the `ThisAssembly` class through `AdditionalThisAssemblyFields` items defined in your project.
Expand Down
32 changes: 31 additions & 1 deletion src/NerdBank.GitVersioning/VersionOracle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,31 @@ public IEnumerable<string> BuildMetadataWithCommitId
/// <summary>
/// Gets the prerelease version information, including a leading hyphen.
/// </summary>
public string PrereleaseVersion => this.ReplaceMacros(this.VersionOptions?.Version?.Prerelease ?? string.Empty);
/// <value>An empty string for a stable release, or a string like <c>-beta</c>.</value>
public string PrereleaseVersion
{
get
{
string result = this.ReplaceMacros(this.VersionOptions?.Version?.Prerelease ?? string.Empty);

foreach (string identifier in this.ExtraPrereleaseIdentifiers)
{
if (result.Length == 0)
{
result = "-";
}
else
{
// In semver v2, identifiers should be separated by periods.
result += this.VersionOptions?.NuGetPackageVersionOrDefault.SemVerOrDefault >= 2 ? '.' : '-';
}

result += identifier;
}

return result;
}
}

/// <summary>
/// Gets the prerelease version information, omitting the leading hyphen, if any.
Expand Down Expand Up @@ -379,6 +403,12 @@ public IDictionary<string, string> CloudBuildVersionVars
[Ignore]
public List<string> BuildMetadata { get; } = new List<string>();

/// <summary>
/// Gets a list of prerelease identifiers to add to whatever the default prerelease identifiers are.
/// </summary>
[Ignore]
public List<string> ExtraPrereleaseIdentifiers { get; } = new List<string>();

/// <summary>
/// Gets the +buildMetadata fragment for the semantic version.
/// </summary>
Expand Down
15 changes: 10 additions & 5 deletions src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using MSBuildExtensionTask;
Expand All @@ -28,6 +23,11 @@ public GetBuildVersion()
/// </summary>
public string BuildMetadata { get; set; }

/// <summary>
/// Gets or sets an array of prerelease identifiers to append to whatever else may be determined by default.
/// </summary>
public string PrereleaseIdentifiers { get; set; }

/// <summary>
/// Gets or sets the value of the PublicRelease property in MSBuild at the
/// start of this Task.
Expand Down Expand Up @@ -256,6 +256,11 @@ protected override bool ExecuteInner()
oracle.BuildMetadata.AddRange(this.BuildMetadata.Split(';'));
}

if (this.PrereleaseIdentifiers is { Length: > 0 })
{
oracle.ExtraPrereleaseIdentifiers.AddRange(this.PrereleaseIdentifiers.Split(';'));
}

if (IsMisconfiguredPrereleaseAndSemVer1(oracle))
{
this.Log.LogWarning("The 'nugetPackageVersion' is explicitly set to 'semVer': 1 but the prerelease version '{0}' is not SemVer1 compliant. Change the 'nugetPackageVersion'.'semVer' value to 2 or change the 'version' member to follow SemVer1 rules (e.g.: '{1}').", oracle.PrereleaseVersion, GetSemVer1WithoutPaddingOrBuildMetadata(oracle));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
CallTarget invoked targets do not see properties set by the calling target. -->
<PropertyGroup>
<BuildMetadata>@(BuildMetadata, ',')</BuildMetadata>
<PrereleaseIdentifiers>@(PrereleaseIdentifier, ',')</PrereleaseIdentifiers>
</PropertyGroup>
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
<NBGV_GlobalPropertiesToRemove Include="RuntimeIdentifier" />

<_BuildMetadataSnapped Include="@(BuildMetadata)" />
<_PrereleaseIdentifierSnapped Include="@(PrereleaseIdentifier)" />
</ItemGroup>

<ItemGroup>
<NBGV_CachingProjectReference Include="$(NBGV_CachingProjectReference)">
<Targets>GetBuildVersion_Properties;GetBuildVersion_CloudBuildVersionVars</Targets>
<Properties>$(NBGV_InnerGlobalProperties)BuildMetadata=@(BuildMetadata, ',');</Properties>
<Properties>$(NBGV_InnerGlobalProperties)BuildMetadata=@(BuildMetadata, ',');PrereleaseIdentifiers=@(PrereleaseIdentifier, ',')</Properties>
<SetConfiguration>Configuration=Release</SetConfiguration>
<SetPlatform>Platform=AnyCPU</SetPlatform>
<GlobalPropertiesToRemove>@(NBGV_GlobalPropertiesToRemove)</GlobalPropertiesToRemove>
Expand All @@ -44,6 +45,7 @@

<Target Name="InvokeGetBuildVersionTask">
<Error Text="BuildMetadata items changed after a copy was made. Add all BuildMetadata items before importing this file." Condition=" '@(BuildMetadata)' != '@(_BuildMetadataSnapped)' " />
<Error Text="PrereleaseIdentifier items changed after a copy was made. Add all PrereleaseIdentifier items before importing this file." Condition=" '@(PrereleaseIdentifier)' != '@(_PrereleaseIdentifierSnapped)' " />

<!-- Calculate version by invoking another "project" with global properties that will serve as a key
into an msbuild cache to ensure we only invoke the GetBuildVersion task as many times as will produce a unique value. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Nerdbank.GitVersioning.Tasks.GetBuildVersion
BuildingRef="$(_NBGV_BuildingRef)"
BuildMetadata="$(BuildMetadata.Replace(',',';'))"
PrereleaseIdentifiers="$(PrereleaseIdentifiers.Replace(',',';'))"
DefaultPublicRelease="$(PublicRelease)"
ProjectDirectory="$(GitVersionBaseDirectory)"
GitRepoRoot="$(GitRepoRoot)"
Expand Down
15 changes: 15 additions & 0 deletions test/Nerdbank.GitVersioning.Tests/BuildIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ public async Task GetBuildVersion_Without_Git_HighPrecisionAssemblyVersion()
Assert.Equal("3.4.0", buildResult.AssemblyInformationalVersion);
}

[Fact]
public async Task WithExtraPrereleaseIdentifiers()
{
this.WriteVersionFile(new VersionOptions
{
Version = SemanticVersion.Parse("3.4"),
});
this.InitializeSourceControl();
this.testProject.AddItem("PrereleaseIdentifier", "i1");
this.testProject.AddItem("PrereleaseIdentifier", "i2");
this.globalProperties["PublicRelease"] = "true";
BuildResults buildResult = await this.BuildAsync();
Assert.Matches(@"^3\.4\.[01]-i1-i2$", buildResult.NuGetPackageVersion);
}

// TODO: add key container test.
[Theory]
[InlineData("keypair.snk", false)]
Expand Down
105 changes: 105 additions & 0 deletions test/Nerdbank.GitVersioning.Tests/VersionOracleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,111 @@ public void SemVerStableNonPublicVersionShortened()
Assert.Matches(@"^2.3.1-g[a-f0-9]{7}$", oracle.ChocolateyPackageVersion);
}

[Fact]
public void ExtraPrereleaseIdentifiers_StableBase_V1()
{
var workingCopyVersion = new VersionOptions
{
Version = SemanticVersion.Parse("2.3"),
NuGetPackageVersion = new VersionOptions.NuGetPackageVersionOptions
{
SemVer = 1,
},
};
this.WriteVersionFile(workingCopyVersion);
this.InitializeSourceControl();
VersionOracle oracle = new(this.Context)
{
ExtraPrereleaseIdentifiers = { "i1", "i2" },
PublicRelease = true,
};
Assert.Equal(@"2.3.1-i1-i2", oracle.NuGetPackageVersion);
}

[Fact]
public void ExtraPrereleaseIdentifiers_StableBase_V2()
{
var workingCopyVersion = new VersionOptions
{
Version = SemanticVersion.Parse("2.3"),
NuGetPackageVersion = new VersionOptions.NuGetPackageVersionOptions
{
SemVer = 2,
},
};
this.WriteVersionFile(workingCopyVersion);
this.InitializeSourceControl();
VersionOracle oracle = new(this.Context)
{
ExtraPrereleaseIdentifiers = { "i1", "i2" },
PublicRelease = true,
};
Assert.Equal(@"2.3.1-i1.i2", oracle.NuGetPackageVersion);
}

[Fact]
public void ExtraPrereleaseIdentifiers_StableBase_V2_NonPublic()
{
var workingCopyVersion = new VersionOptions
{
Version = SemanticVersion.Parse("2.3"),
NuGetPackageVersion = new VersionOptions.NuGetPackageVersionOptions
{
SemVer = 2,
},
};
this.WriteVersionFile(workingCopyVersion);
this.InitializeSourceControl();
VersionOracle oracle = new(this.Context)
{
ExtraPrereleaseIdentifiers = { "i1", "i2" },
PublicRelease = false,
};
Assert.Matches(@"^2\.3\.1-i1\.i2\.g", oracle.NuGetPackageVersion);
}

[Fact]
public void ExtraPrereleaseIdentifiers_UnstableBase_V1()
{
var workingCopyVersion = new VersionOptions
{
Version = SemanticVersion.Parse("2.3-abc"),
NuGetPackageVersion = new VersionOptions.NuGetPackageVersionOptions
{
SemVer = 1,
},
};
this.WriteVersionFile(workingCopyVersion);
this.InitializeSourceControl();
VersionOracle oracle = new(this.Context)
{
ExtraPrereleaseIdentifiers = { "i1", "i2" },
PublicRelease = true,
};
Assert.Equal(@"2.3.1-abc-i1-i2", oracle.NuGetPackageVersion);
}

[Fact]
public void ExtraPrereleaseIdentifiers_UnstableBase_V2()
{
var workingCopyVersion = new VersionOptions
{
Version = SemanticVersion.Parse("2.3-abc"),
NuGetPackageVersion = new VersionOptions.NuGetPackageVersionOptions
{
SemVer = 2,
},
};
this.WriteVersionFile(workingCopyVersion);
this.InitializeSourceControl();
VersionOracle oracle = new(this.Context)
{
ExtraPrereleaseIdentifiers = { "i1", "i2" },
PublicRelease = true,
};
Assert.Equal(@"2.3.1-abc.i1.i2", oracle.NuGetPackageVersion);
}

[Theory]
[InlineData("1.2.0.0", null, null)]
[InlineData("1.0.0.0", null, VersionOptions.VersionPrecision.Major)]
Expand Down

0 comments on commit 2dfd066

Please sign in to comment.