Skip to content

Commit

Permalink
feat: add static FromVersion method
Browse files Browse the repository at this point in the history
Adds a method to convert a .NET Version to a SemanticVersionObject.
Also adds a static GetCurrentAppVersion method.
  • Loading branch information
Kampfmoehre committed Mar 18, 2024
1 parent 9ab14e5 commit 89dd5f9
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ There is also a static `FromString` Method which will return you a `SemanticVers
var version = SemanticVersionObject.FromString("v1.0.0-beta.1+298a915a985daeb426a0fe7543917874d7fa2995");
```

### FromVersion

There is a static method to take the version from a .NET `Version` instance called `FromVersion`. It takes the `Version` instance as a parameter.

The second parameter controls how build and revision are used. [The Version docs](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-version) state, that revision should be used for assemblies that fix security holes but are otherwise the same. This would match the sense of the `Patch` part of semantic version, so this is the default, setting the `Revision` property to the patch version.

However when using the `Version` build property in `.csproj` the generated version in `AssemblyInfo.cs` ends up in a way where patch version lands in the build property instead of revision. So when using the method with an Assembly version, you can pass `true` to the method and the patch version will be read from the `Build` property and `Revision` is used for build version.

## GetCurrentAppVersion

This retrieves the version from the current running application and returns a `SemanticVersionObject` for it. For this `Assembly.GetEntryAssembly()` is used.

### ToString

You can generate a version string with the `ToString` method:
Expand Down
61 changes: 61 additions & 0 deletions src/DroidSolutions.Oss.SemanticVersion/SemanticVersionObject.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;

namespace DroidSolutions.Oss.SemanticVersion;
Expand Down Expand Up @@ -204,6 +205,66 @@ public static SemanticVersionObject FromString(string version)
return result;
}

/// <summary>
/// Creates a <see cref="SemanticVersion"/> instance by parsing the given <see cref="Version"/>.
/// </summary>
/// <remarks>
/// <para>The given <see cref="Version"/> object's properties are used to create an instance of the
/// <see cref="SemanticVersion"/> class. Major and minor properties are used accordingly.
/// </para>
/// <para>
/// <seealso href="https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-version">The docs
/// </seealso> state that revision should be used as patch version, however .NET AssemblyInfo.cs files are generated
/// with patch set to the build property. To control this, the <paramref name="useBuildAsPatch"/> parameter can be
/// used.
/// </para>
/// <para>
/// Note that Version objects don't support prereleases or build metadata as the build and revision properties are
/// ints.
/// </para>
/// </remarks>
/// <param name="version">The version to parse.</param>
/// <param name="useBuildAsPatch">If <see langword="true"/>, the <see cref="Version.Build"/> property will be
/// used as the patch version. Otherwise, the <see cref="Version.Revision"/> property will be used.</param>
/// <returns>A <see cref="SemanticVersionObject"/> instance with the parsed version numbers. </returns>
public static SemanticVersionObject FromVersion(Version version, bool useBuildAsPatch = false)
{
ArgumentNullException.ThrowIfNull(version);
int patch = useBuildAsPatch ? version.Build : version.Revision;
string? build = null;
if (useBuildAsPatch && version.Revision >= 1)
{
build = version.Revision.ToString();
}
else if (!useBuildAsPatch && version.Build >= 1)
{
build = version.Build.ToString();
}

return build is null
? new SemanticVersionObject(version.Major, version.Minor, patch)
: new SemanticVersionObject(version.Major, version.Minor, patch, null, build);
}

/// <summary>
/// Gets the current application version.
/// </summary>
/// <returns>The current application version as <see cref="SemanticVersionObject"/>.</returns>
public static SemanticVersionObject GetCurrentAppVersion()
{
Assembly entryAssembly = Assembly.GetEntryAssembly()
?? throw new InvalidOperationException("Unable to get entry assembly.");
AssemblyName assemblyName = entryAssembly.GetName()
?? throw new InvalidOperationException("Unable to get assembly name.");

if (assemblyName.Version is null)
{
throw new InvalidOperationException("Unable to get assembly version.");
}

return FromVersion(assemblyName.Version, true);
}

/// <summary>
/// Returns a comparer that sorts a list of version descending by the newest first and the oldest last.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;

using DroidSolutions.Oss.SemanticVersion;

Expand All @@ -9,9 +10,9 @@ namespace DroidSolutions.Oss.SemanticVersionTest;

public class SemanticVersionObjectTest
{
public static IEnumerable<object[]> FromStringTestData() => new[]
{
new object[] { "1.0.0", new SemanticVersionObject(1, 0, 0) },
public static IEnumerable<object[]> FromStringTestData() =>
[
["1.0.0", new SemanticVersionObject(1, 0, 0)],
["v1.0.0", new SemanticVersionObject(1, 0, 0)],
["1.1.1", new SemanticVersionObject(1, 1, 1)],
["11.111.1111", new SemanticVersionObject(11, 111, 1111)],
Expand All @@ -21,7 +22,15 @@ public static IEnumerable<object[]> FromStringTestData() => new[]
"1.20.4+dd08e98289531187cb240db94b188c58938eb214",
new SemanticVersionObject(1, 20, 4, null, "dd08e98289531187cb240db94b188c58938eb214")
],
};
];

public static IEnumerable<object[]> FromVersionTestData() =>
[
[new Version(1, 0, 0, 0), false, "v1.0.0"],
[new Version(2, 3, 0, 4), false, "v2.3.4"],
[new Version(2, 3, 5, 4), false, "v2.3.4+5"],
[new Version("3.43.35.0"), true, "v3.43.35"],
];

[Theory]
[MemberData(nameof(FromStringTestData))]
Expand Down Expand Up @@ -52,6 +61,37 @@ public void FromString_ThrowsArgumentException_IfInvalidVersionGiven(string vers
Assert.Throws<ArgumentException>(() => SemanticVersionObject.FromString(version));
}

[Theory]
[MemberData(nameof(FromVersionTestData))]
public void FromVersion_ShouldParseCorrect(Version version, bool useRevisionAsPatch, string expected)
{
Assert.Equal(expected, SemanticVersionObject.FromVersion(version, useRevisionAsPatch).ToString());
}

[Fact]
public void FromVersion_ThrowsArgumentNullException_IfGivenNull()
{
Assert.Throws<ArgumentNullException>(() => SemanticVersionObject.FromVersion(null!));
}

[Fact]
public void GetCurrentAppVersion_ShouldWork()
{
SemanticVersionObject actual = SemanticVersionObject.GetCurrentAppVersion();
Version? expected = Assembly.GetEntryAssembly()?.GetName()?.Version;
Assert.Equal(expected?.Major, actual.Major);
Assert.Equal(expected?.Minor, actual.Minor);
Assert.Equal(expected?.Build, actual.Patch);
if (expected?.Revision > 0)
{
Assert.Equal(expected?.Revision.ToString(), actual.Build);
}
else
{
Assert.Null(actual.Build);
}
}

[Fact]
public void ListOf_SemanticVersionObject_ShouldBeSortedDescending()
{
Expand Down

0 comments on commit 89dd5f9

Please sign in to comment.