From 23ee2df02a72809f00eb5356757b13f4c09a26b7 Mon Sep 17 00:00:00 2001 From: Riccardo De Agostini Date: Tue, 2 Jun 2026 12:02:28 +0200 Subject: [PATCH] Add Buildvana.Core.Versioning shared library (Versioning Phase 4) Move VersionSpec and VersionSpecChange from Buildvana.Tool into a new host-agnostic Buildvana.Core.Versioning library (depending only on NuGet.Versioning and CommunityToolkit.Diagnostics). The Tool now references the library; no behavior change. Add VersionFileData, an immutable model of current-version.json with a strict parser, and VersionCalculator, which computes the semantic version from version-file data, git height, branch name, public-release branch patterns, and a prerelease tag. CalculatedVersion carries the result. The library is unit-tested but not yet wired into the live tool path: bv still computes versions via nbgv until Phase 5. Co-Authored-By: Claude Opus 4.8 --- Buildvana.slnx | 2 + .../Buildvana.Core.Versioning.csproj | 14 +++ .../CalculatedVersion.cs | 20 ++++ .../VersionCalculator.cs | 68 ++++++++++++++ .../VersionFileData.cs | 93 +++++++++++++++++++ .../VersionSpec.cs | 4 +- .../VersionSpecChange.cs | 4 +- src/Buildvana.Tool/Buildvana.Tool.csproj | 1 + .../Services/Versioning/VersionFile.cs | 1 + .../Services/Versioning/VersionService.cs | 1 + .../Subcommands/ReleaseCommand.cs | 1 + .../Subcommands/ReleaseSettings.cs | 2 +- .../Buildvana.Core.Versioning.Tests.csproj | 18 ++++ .../VersionCalculatorTests.cs | 81 ++++++++++++++++ .../VersionFileDataTests.cs | 77 +++++++++++++++ .../VersionSpecTests.cs | 58 ++++++++++++ .../ReleaseSettingsTests.cs | 2 +- 17 files changed, 441 insertions(+), 6 deletions(-) create mode 100644 src/Buildvana.Core.Versioning/Buildvana.Core.Versioning.csproj create mode 100644 src/Buildvana.Core.Versioning/CalculatedVersion.cs create mode 100644 src/Buildvana.Core.Versioning/VersionCalculator.cs create mode 100644 src/Buildvana.Core.Versioning/VersionFileData.cs rename src/{Buildvana.Tool/Services/Versioning => Buildvana.Core.Versioning}/VersionSpec.cs (98%) rename src/{Buildvana.Tool/Services/Versioning => Buildvana.Core.Versioning}/VersionSpecChange.cs (92%) create mode 100644 tests/Buildvana.Core.Versioning.Tests/Buildvana.Core.Versioning.Tests.csproj create mode 100644 tests/Buildvana.Core.Versioning.Tests/VersionCalculatorTests.cs create mode 100644 tests/Buildvana.Core.Versioning.Tests/VersionFileDataTests.cs create mode 100644 tests/Buildvana.Core.Versioning.Tests/VersionSpecTests.cs diff --git a/Buildvana.slnx b/Buildvana.slnx index 2cd2ea2..d18f4b5 100644 --- a/Buildvana.slnx +++ b/Buildvana.slnx @@ -44,6 +44,7 @@ + @@ -53,6 +54,7 @@ + diff --git a/src/Buildvana.Core.Versioning/Buildvana.Core.Versioning.csproj b/src/Buildvana.Core.Versioning/Buildvana.Core.Versioning.csproj new file mode 100644 index 0000000..7d7c28b --- /dev/null +++ b/src/Buildvana.Core.Versioning/Buildvana.Core.Versioning.csproj @@ -0,0 +1,14 @@ + + + + Buildvana versioning + Host-agnostic version computation: the current-version.json model, semantic-version calculation from git height, and public-release branch matching. Reports malformed input via standard exceptions. + $(StandardTfm) + + + + + + + + diff --git a/src/Buildvana.Core.Versioning/CalculatedVersion.cs b/src/Buildvana.Core.Versioning/CalculatedVersion.cs new file mode 100644 index 0000000..6fabdc4 --- /dev/null +++ b/src/Buildvana.Core.Versioning/CalculatedVersion.cs @@ -0,0 +1,20 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using NuGet.Versioning; + +namespace Buildvana.Core.Versioning; + +/// +/// Represents the result of a version computation performed by . +/// +/// The computed semantic version. +/// The normalized SemVer 2.0 string form of . +/// A value indicating whether the build is a public release, i.e. the current branch +/// matches one of the configured public-release patterns. +/// A value indicating whether the computed version is a prerelease. +public sealed record CalculatedVersion( + SemanticVersion SemanticVersion, + string CurrentStr, + bool IsPublicRelease, + bool IsPrerelease); diff --git a/src/Buildvana.Core.Versioning/VersionCalculator.cs b/src/Buildvana.Core.Versioning/VersionCalculator.cs new file mode 100644 index 0000000..46912b5 --- /dev/null +++ b/src/Buildvana.Core.Versioning/VersionCalculator.cs @@ -0,0 +1,68 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using CommunityToolkit.Diagnostics; +using NuGet.Versioning; + +namespace Buildvana.Core.Versioning; + +/// +/// Computes a semantic version from version-file data, the git height, the current branch, and release policy. +/// +public static class VersionCalculator +{ + /// + /// Computes the version to build. + /// + /// The data parsed from current-version.json. + /// The git height (number of commits since the version was last changed); must be non-negative. + /// The short name of the current branch, or the empty string when HEAD is detached. + /// The patterns identifying public-release branches, matched against + /// . Callers should compile these with + /// and a match timeout; the patterns are expected to carry their own anchors. + /// The prerelease tag to apply when denotes a prerelease; + /// required (non-empty) in that case, ignored otherwise. + /// A describing the computed version. + /// A reference argument is . + /// is negative. + /// denotes a prerelease but + /// is or empty. + /// A pattern in exceeded its match timeout. + public static CalculatedVersion Compute( + VersionFileData fileData, + int height, + string branchName, + IReadOnlyList publicReleaseBranches, + string? prereleaseTag) + { + Guard.IsNotNull(fileData); + Guard.IsGreaterThanOrEqualTo(height, 0); + Guard.IsNotNull(branchName); + Guard.IsNotNull(publicReleaseBranches); + + SemanticVersion semanticVersion; + if (fileData.Prerelease) + { + if (string.IsNullOrEmpty(prereleaseTag)) + { + throw new ArgumentException( + "A prerelease tag is required to compute a prerelease version, but none was provided.", + nameof(prereleaseTag)); + } + + semanticVersion = new SemanticVersion(fileData.Major, fileData.Minor, height, prereleaseTag); + } + else + { + semanticVersion = new SemanticVersion(fileData.Major, fileData.Minor, height); + } + + var currentStr = semanticVersion.ToNormalizedString(); + var isPublicRelease = publicReleaseBranches.Any(pattern => pattern.IsMatch(branchName)); + return new CalculatedVersion(semanticVersion, currentStr, isPublicRelease, semanticVersion.IsPrerelease); + } +} diff --git a/src/Buildvana.Core.Versioning/VersionFileData.cs b/src/Buildvana.Core.Versioning/VersionFileData.cs new file mode 100644 index 0000000..33b7038 --- /dev/null +++ b/src/Buildvana.Core.Versioning/VersionFileData.cs @@ -0,0 +1,93 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Text.Json; +using CommunityToolkit.Diagnostics; + +namespace Buildvana.Core.Versioning; + +/// +/// Represents the immutable data parsed from a current-version.json file +/// ({ "major": <int>, "minor": <int>, "prerelease": <bool> }). +/// +/// The major version component. +/// The minor version component. +/// A value indicating whether the version is a prerelease. +/// +/// The current-version.json file holds the version value; release policy +/// (public-release branches, prerelease tag, assembly-version precision) lives in buildvana.json. +/// This type does not read the file from disk. Callers are responsible for reading the file content and +/// for failing loudly when the file is absent: a missing current-version.json must be +/// treated as an error, never silently defaulted. +/// +public sealed record VersionFileData(int Major, int Minor, bool Prerelease) +{ + /// + /// Parses a from the textual content of a current-version.json file. + /// + /// The JSON content to parse. + /// The parsed . + /// is . + /// + /// is not a JSON object, is missing a required property, or has a property of the wrong + /// type or value (major and minor must be non-negative integers, prerelease a boolean). + /// + public static VersionFileData Parse(string json) + { + Guard.IsNotNull(json); + + JsonDocument document; + try + { + document = JsonDocument.Parse(json); + } + catch (JsonException exception) + { + throw new FormatException("current-version.json is not valid JSON.", exception); + } + + using (document) + { + var root = document.RootElement; + if (root.ValueKind != JsonValueKind.Object) + { + throw new FormatException("current-version.json must contain a JSON object."); + } + + var major = GetNonNegativeInt32(root, "major"); + var minor = GetNonNegativeInt32(root, "minor"); + var prerelease = GetBoolean(root, "prerelease"); + return new VersionFileData(major, minor, prerelease); + } + } + + private static int GetNonNegativeInt32(JsonElement root, string propertyName) + { + if (!root.TryGetProperty(propertyName, out var element)) + { + throw new FormatException($"current-version.json is missing the required '{propertyName}' property."); + } + + if (element.ValueKind == JsonValueKind.Number && element.TryGetInt32(out var value) && value >= 0) + { + return value; + } + + throw new FormatException($"current-version.json property '{propertyName}' must be a non-negative integer."); + } + + private static bool GetBoolean(JsonElement root, string propertyName) + { + if (!root.TryGetProperty(propertyName, out var element)) + { + throw new FormatException($"current-version.json is missing the required '{propertyName}' property."); + } + + return element.ValueKind switch { + JsonValueKind.True => true, + JsonValueKind.False => false, + _ => throw new FormatException($"current-version.json property '{propertyName}' must be a boolean."), + }; + } +} diff --git a/src/Buildvana.Tool/Services/Versioning/VersionSpec.cs b/src/Buildvana.Core.Versioning/VersionSpec.cs similarity index 98% rename from src/Buildvana.Tool/Services/Versioning/VersionSpec.cs rename to src/Buildvana.Core.Versioning/VersionSpec.cs index 31ff2e4..c674bc9 100644 --- a/src/Buildvana.Tool/Services/Versioning/VersionSpec.cs +++ b/src/Buildvana.Core.Versioning/VersionSpec.cs @@ -6,12 +6,12 @@ using System.Globalization; using System.Text.RegularExpressions; -namespace Buildvana.Tool.Services.Versioning; +namespace Buildvana.Core.Versioning; /// /// Represents a Major.Minor[-Tag] version as found in version.json. /// -internal sealed partial record VersionSpec +public sealed partial record VersionSpec { private static readonly Regex VersionSpecRegex = GetVersionSpecRegex(); diff --git a/src/Buildvana.Tool/Services/Versioning/VersionSpecChange.cs b/src/Buildvana.Core.Versioning/VersionSpecChange.cs similarity index 92% rename from src/Buildvana.Tool/Services/Versioning/VersionSpecChange.cs rename to src/Buildvana.Core.Versioning/VersionSpecChange.cs index 8c3eeb3..e651f8a 100644 --- a/src/Buildvana.Tool/Services/Versioning/VersionSpecChange.cs +++ b/src/Buildvana.Core.Versioning/VersionSpecChange.cs @@ -1,12 +1,12 @@ // Copyright (C) Tenacom and Contributors. Licensed under the MIT license. // See the LICENSE file in the project root for full license information. -namespace Buildvana.Tool.Services.Versioning; +namespace Buildvana.Core.Versioning; /// /// Specifies how to modify the version specification upon publishing a release. /// -internal enum VersionSpecChange +public enum VersionSpecChange { /// /// Do not force a version increment; do not modify the unstable tag. diff --git a/src/Buildvana.Tool/Buildvana.Tool.csproj b/src/Buildvana.Tool/Buildvana.Tool.csproj index 459d866..c2ec24b 100644 --- a/src/Buildvana.Tool/Buildvana.Tool.csproj +++ b/src/Buildvana.Tool/Buildvana.Tool.csproj @@ -29,6 +29,7 @@ + diff --git a/src/Buildvana.Tool/Services/Versioning/VersionFile.cs b/src/Buildvana.Tool/Services/Versioning/VersionFile.cs index 9a9a4af..924224e 100644 --- a/src/Buildvana.Tool/Services/Versioning/VersionFile.cs +++ b/src/Buildvana.Tool/Services/Versioning/VersionFile.cs @@ -5,6 +5,7 @@ using Buildvana.Core; using Buildvana.Core.HomeDirectory; using Buildvana.Core.Json; +using Buildvana.Core.Versioning; using CommunityToolkit.Diagnostics; namespace Buildvana.Tool.Services.Versioning; diff --git a/src/Buildvana.Tool/Services/Versioning/VersionService.cs b/src/Buildvana.Tool/Services/Versioning/VersionService.cs index 4228b6f..4ac7921 100644 --- a/src/Buildvana.Tool/Services/Versioning/VersionService.cs +++ b/src/Buildvana.Tool/Services/Versioning/VersionService.cs @@ -5,6 +5,7 @@ using Buildvana.Core.ConsoleOutput; using Buildvana.Core.Json; using Buildvana.Core.Process; +using Buildvana.Core.Versioning; using Buildvana.Tool.Services.Git; using Buildvana.Tool.Services.PublicApiFiles; using CommunityToolkit.Diagnostics; diff --git a/src/Buildvana.Tool/Subcommands/ReleaseCommand.cs b/src/Buildvana.Tool/Subcommands/ReleaseCommand.cs index 184d939..af54b09 100644 --- a/src/Buildvana.Tool/Subcommands/ReleaseCommand.cs +++ b/src/Buildvana.Tool/Subcommands/ReleaseCommand.cs @@ -13,6 +13,7 @@ using Buildvana.Core.ConsoleOutput; using Buildvana.Core.HomeDirectory; using Buildvana.Core.Json; +using Buildvana.Core.Versioning; using Buildvana.Tool.Build; using Buildvana.Tool.Infrastructure; using Buildvana.Tool.Infrastructure.Execution; diff --git a/src/Buildvana.Tool/Subcommands/ReleaseSettings.cs b/src/Buildvana.Tool/Subcommands/ReleaseSettings.cs index 64cc686..fff058d 100644 --- a/src/Buildvana.Tool/Subcommands/ReleaseSettings.cs +++ b/src/Buildvana.Tool/Subcommands/ReleaseSettings.cs @@ -7,9 +7,9 @@ using System.Text.RegularExpressions; using Buildvana.Core; using Buildvana.Core.Configuration; +using Buildvana.Core.Versioning; using Buildvana.Tool.CommandLine; using Buildvana.Tool.Services; -using Buildvana.Tool.Services.Versioning; using CommunityToolkit.Diagnostics; namespace Buildvana.Tool.Subcommands; diff --git a/tests/Buildvana.Core.Versioning.Tests/Buildvana.Core.Versioning.Tests.csproj b/tests/Buildvana.Core.Versioning.Tests/Buildvana.Core.Versioning.Tests.csproj new file mode 100644 index 0000000..fae4448 --- /dev/null +++ b/tests/Buildvana.Core.Versioning.Tests/Buildvana.Core.Versioning.Tests.csproj @@ -0,0 +1,18 @@ + + + + Exe + $(StandardTfm) + false + true + + + + + + + + + + + diff --git a/tests/Buildvana.Core.Versioning.Tests/VersionCalculatorTests.cs b/tests/Buildvana.Core.Versioning.Tests/VersionCalculatorTests.cs new file mode 100644 index 0000000..2293f42 --- /dev/null +++ b/tests/Buildvana.Core.Versioning.Tests/VersionCalculatorTests.cs @@ -0,0 +1,81 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Text.RegularExpressions; +using Buildvana.Core.Versioning; +using NuGet.Versioning; + +internal sealed class VersionCalculatorTests +{ + [Test] + public async Task Compute_StableVersion_BuildsMajorMinorHeight() + { + var result = Compute(new VersionFileData(2, 1, false), height: 7, branchName: "main"); + await Assert.That(result.CurrentStr).IsEqualTo("2.1.7"); + await Assert.That(result.SemanticVersion).IsEqualTo(new SemanticVersion(2, 1, 7)); + await Assert.That(result.IsPrerelease).IsFalse(); + } + + [Test] + public async Task Compute_PrereleaseVersion_AppendsTagToHeight() + { + var result = Compute(new VersionFileData(2, 0, true), height: 3, prereleaseTag: "preview"); + await Assert.That(result.CurrentStr).IsEqualTo("2.0.3-preview"); + await Assert.That(result.IsPrerelease).IsTrue(); + } + + [Test] + public async Task Compute_PrereleaseWithoutTag_Throws() + { + await Assert.That(() => Compute(new VersionFileData(1, 0, true))).Throws(); + } + + [Test] + public async Task Compute_NegativeHeight_Throws() + { + await Assert.That(() => Compute(new VersionFileData(1, 0, false), height: -1)) + .Throws(); + } + + [Test] + public async Task Compute_BranchMatchesAnyPattern_IsPublicReleaseTrue() + { + IReadOnlyList branches = [new Regex("^main$"), new Regex(@"^v\d+\.\d+$")]; + var result = Compute(new VersionFileData(2, 0, false), branchName: "v2.0", publicReleaseBranches: branches); + await Assert.That(result.IsPublicRelease).IsTrue(); + } + + [Test] + public async Task Compute_BranchMatchesNoPattern_IsPublicReleaseFalse() + { + IReadOnlyList branches = [new Regex("^main$")]; + var result = Compute(new VersionFileData(2, 0, false), branchName: "topic", publicReleaseBranches: branches); + await Assert.That(result.IsPublicRelease).IsFalse(); + } + + [Test] + public async Task Compute_EmptyBranchList_IsPublicReleaseFalse() + { + var result = Compute(new VersionFileData(2, 0, false), branchName: "main"); + await Assert.That(result.IsPublicRelease).IsFalse(); + } + + [Test] + public async Task Compute_DetachedHead_IsPublicReleaseFalse() + { + IReadOnlyList branches = [new Regex("^main$")]; + var result = Compute( + new VersionFileData(2, 0, false), + branchName: string.Empty, + publicReleaseBranches: branches); + await Assert.That(result.IsPublicRelease).IsFalse(); + } + + private static CalculatedVersion Compute( + VersionFileData fileData, + int height = 0, + string branchName = "", + IReadOnlyList? publicReleaseBranches = null, + string? prereleaseTag = null) + => VersionCalculator.Compute(fileData, height, branchName, publicReleaseBranches ?? [], prereleaseTag); +} diff --git a/tests/Buildvana.Core.Versioning.Tests/VersionFileDataTests.cs b/tests/Buildvana.Core.Versioning.Tests/VersionFileDataTests.cs new file mode 100644 index 0000000..9f4b46e --- /dev/null +++ b/tests/Buildvana.Core.Versioning.Tests/VersionFileDataTests.cs @@ -0,0 +1,77 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using Buildvana.Core.Versioning; + +internal sealed class VersionFileDataTests +{ + [Test] + public async Task Parse_ValidStableVersion_ParsesAllFields() + { + var data = VersionFileData.Parse("""{ "major": 2, "minor": 1, "prerelease": false }"""); + await Assert.That(data.Major).IsEqualTo(2); + await Assert.That(data.Minor).IsEqualTo(1); + await Assert.That(data.Prerelease).IsFalse(); + } + + [Test] + public async Task Parse_ValidPrerelease_SetsPrereleaseTrue() + { + var data = VersionFileData.Parse("""{ "major": 0, "minor": 0, "prerelease": true }"""); + await Assert.That(data.Prerelease).IsTrue(); + } + + [Test] + public async Task Parse_NotValidJson_Throws() + { + await Assert.That(() => VersionFileData.Parse("{ not json")).Throws(); + } + + [Test] + public async Task Parse_NotAnObject_Throws() + { + await Assert.That(() => VersionFileData.Parse("42")).Throws(); + } + + [Test] + public async Task Parse_MissingMajor_Throws() + { + const string json = """{ "minor": 0, "prerelease": false }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } + + [Test] + public async Task Parse_MissingPrerelease_Throws() + { + const string json = """{ "major": 0, "minor": 0 }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } + + [Test] + public async Task Parse_MajorWrongType_Throws() + { + const string json = """{ "major": "2", "minor": 0, "prerelease": false }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } + + [Test] + public async Task Parse_NegativeMajor_Throws() + { + const string json = """{ "major": -1, "minor": 0, "prerelease": false }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } + + [Test] + public async Task Parse_NonIntegerMajor_Throws() + { + const string json = """{ "major": 2.5, "minor": 0, "prerelease": false }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } + + [Test] + public async Task Parse_PrereleaseWrongType_Throws() + { + const string json = """{ "major": 0, "minor": 0, "prerelease": "yes" }"""; + await Assert.That(() => VersionFileData.Parse(json)).Throws(); + } +} diff --git a/tests/Buildvana.Core.Versioning.Tests/VersionSpecTests.cs b/tests/Buildvana.Core.Versioning.Tests/VersionSpecTests.cs new file mode 100644 index 0000000..741c813 --- /dev/null +++ b/tests/Buildvana.Core.Versioning.Tests/VersionSpecTests.cs @@ -0,0 +1,58 @@ +// Copyright (C) Tenacom and Contributors. Licensed under the MIT license. +// See the LICENSE file in the project root for full license information. + +using Buildvana.Core.Versioning; + +internal sealed class VersionSpecTests +{ + [Test] + public async Task TryParse_ValidStable_ParsesComponents() + { + var parsed = VersionSpec.TryParse("2.0", out var spec); + await Assert.That(parsed).IsTrue(); + await Assert.That(spec!.Major).IsEqualTo(2); + await Assert.That(spec.Minor).IsEqualTo(0); + await Assert.That(spec.HasTag).IsFalse(); + } + + [Test] + public async Task TryParse_WithVPrefixAndTag_ParsesComponents() + { + var parsed = VersionSpec.TryParse("v1.5-preview", out var spec); + await Assert.That(parsed).IsTrue(); + await Assert.That(spec!.Major).IsEqualTo(1); + await Assert.That(spec.Minor).IsEqualTo(5); + await Assert.That(spec.Tag).IsEqualTo("preview"); + } + + [Test] + public async Task TryParse_InvalidString_ReturnsFalse() + { + await Assert.That(VersionSpec.TryParse("not-a-version", out _)).IsFalse(); + } + + [Test] + public async Task ToString_RoundTripsTag() + { + _ = VersionSpec.TryParse("3.4-beta", out var spec); + await Assert.That(spec!.ToString()).IsEqualTo("3.4-beta"); + } + + [Test] + public async Task ApplyChange_Major_BumpsMajorResetsMinorAddsTag() + { + _ = VersionSpec.TryParse("2.3", out var spec); + var (result, changed) = spec!.ApplyChange(VersionSpecChange.Major, "preview"); + await Assert.That(changed).IsTrue(); + await Assert.That(result.ToString()).IsEqualTo("3.0-preview"); + } + + [Test] + public async Task ApplyChange_StableWhenAlreadyStable_ReportsNoChange() + { + _ = VersionSpec.TryParse("2.3", out var spec); + var (result, changed) = spec!.ApplyChange(VersionSpecChange.Stable, "preview"); + await Assert.That(changed).IsFalse(); + await Assert.That(result).IsEqualTo(spec); + } +} diff --git a/tests/Buildvana.Tool.Tests/ReleaseSettingsTests.cs b/tests/Buildvana.Tool.Tests/ReleaseSettingsTests.cs index 84df245..aa7fd60 100644 --- a/tests/Buildvana.Tool.Tests/ReleaseSettingsTests.cs +++ b/tests/Buildvana.Tool.Tests/ReleaseSettingsTests.cs @@ -3,8 +3,8 @@ using Buildvana.Core; using Buildvana.Core.Configuration; +using Buildvana.Core.Versioning; using Buildvana.Tool.Services; -using Buildvana.Tool.Services.Versioning; using Buildvana.Tool.Subcommands; internal sealed class ReleaseSettingsTests