diff --git a/Directory.Build.props b/Directory.Build.props index 3dcd5dd..adbafb3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -46,7 +46,7 @@ v patch - 2.2 + 2.3 diff --git a/README.md b/README.md index be698fb..77507ee 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ See [docs/VersionCalculation.md](docs/VersionCalculation.md) for further reading | Set the build data to this value. | -b, --build-metadata, VerliteBuildMetadata | | | Part of the version to print. | -s, --show | all | | Automatically fetch commits and a tag for shallow clones. | --auto-fetch | false | -| Create a lightweight tag instead of fetching the remote's. | --enable-lightweight-tags | false | +| Create a lightweight tag instead of fetching the remote's. | --enable-lightweight-tags | false | | Set which version part should be bumped after an RTM release. | -a, --auto-increment, VerliteAutoIncrement | patch | | A command to test whether a tag should be ignored via exit code. | -f, --filter-tags, VerliteFilterTags | | | The remote endpoint to use when fetching tags and commits. | -r, --remote, VerliteRemote | origin | @@ -243,6 +243,8 @@ namespace Verlite public const string Patch; public const string Prerelease; public const string BuildMetadata; + public const string Commit; + public const string Height; } } ``` diff --git a/src/Verlite.CLI/JsonOutput.cs b/src/Verlite.CLI/JsonOutput.cs new file mode 100644 index 0000000..47437e2 --- /dev/null +++ b/src/Verlite.CLI/JsonOutput.cs @@ -0,0 +1,67 @@ +using System.Globalization; +using System.Text; + +namespace Verlite.CLI +{ + internal static class JsonOutput + { + public static string GenerateOutput( + SemVer version, + Commit? commit, + TaggedVersion? lastTag, + int? height) + { + var sb = new StringBuilder(); + sb.AppendLine("{"); + sb.AppendString(1, "commit", commit?.Id); + sb.AppendString(1, "full", version.ToString()); + sb.AppendInteger(1, "major", version.Major); + sb.AppendInteger(1, "minor", version.Minor); + sb.AppendInteger(1, "patch", version.Patch); + sb.AppendString(1, "prerelease", version.Prerelease); + sb.AppendString(1, "meta", version.BuildMetadata); + sb.AppendInteger(1, "height", height); + if (lastTag is null) + { + sb.AppendLine("\t" + @"""lastTag"": null"); + } + else + { + sb.AppendLine("\t" + @"""lastTag"": {"); + sb.AppendString(2, "tag", lastTag.Tag.Name); + sb.AppendString(2, "commit", lastTag.Tag.PointsTo.Id); + sb.AppendString(2, "full", lastTag.Version.ToString()); + sb.AppendInteger(2, "major", lastTag.Version.Major); + sb.AppendInteger(2, "minor", lastTag.Version.Minor); + sb.AppendInteger(2, "patch", lastTag.Version.Patch); + sb.AppendString(2, "prerelease", lastTag.Version.Prerelease); + sb.AppendString(2, "meta", lastTag.Version.BuildMetadata, final: true); + sb.AppendLine("\t}"); + } + sb.AppendLine("}"); + + return sb.ToString(); + } + + private static void AppendString(this StringBuilder sb, int indentation, string key, string? value, bool final = false) + { + value = value is null ? "null" : $@"""{System.Web.HttpUtility.JavaScriptStringEncode(value)}"""; + sb.Append(new string('\t', indentation)); + sb.Append($@"""{key}"": {value}"); + if (final) + sb.AppendLine(""); + else + sb.AppendLine(","); + } + private static void AppendInteger(this StringBuilder sb, int indentation, string key, int? value, bool final = false) + { + var valuestr = value is null ? "null" : value.Value.ToString(CultureInfo.InvariantCulture); + sb.Append(new string('\t', indentation)); + sb.Append($@"""{key}"": ""{valuestr}"""); + if (final) + sb.AppendLine(""); + else + sb.AppendLine(","); + } + } +} diff --git a/src/Verlite.CLI/Program.cs b/src/Verlite.CLI/Program.cs index f6e31bb..a1adc76 100644 --- a/src/Verlite.CLI/Program.cs +++ b/src/Verlite.CLI/Program.cs @@ -141,6 +141,10 @@ private async static Task RootCommandAsync( }; var version = opts.VersionOverride ?? new SemVer(); + Commit? commit = null; + TaggedVersion? lastTag = null; + int? height = null; + if (opts.VersionOverride is null) { using var repo = await GitRepoInspector.FromPath(sourceDirectory, opts.Remote, log, commandRunner); @@ -151,7 +155,8 @@ private async static Task RootCommandAsync( if (!string.IsNullOrWhiteSpace(filterTags)) tagFilter = new CommandTagFilter(commandRunner, log, filterTags, sourceDirectory); - version = await VersionCalculator.FromRepository(repo, opts, log, tagFilter); + commit = await repo.GetHead(); + (version, lastTag, height) = await VersionCalculator.FromRepository2(repo, opts, log, tagFilter); } string toShow = show switch @@ -162,6 +167,8 @@ private async static Task RootCommandAsync( Show.patch => version.Patch.ToString(CultureInfo.InvariantCulture), Show.prerelease => version.Prerelease?.ToString(CultureInfo.InvariantCulture) ?? string.Empty, Show.metadata => version.BuildMetadata?.ToString(CultureInfo.InvariantCulture) ?? string.Empty, + Show.height => height?.ToString(CultureInfo.InvariantCulture) ?? string.Empty, + Show.json => JsonOutput.GenerateOutput(version, commit, lastTag, height), _ => throw new NotImplementedException(), }; diff --git a/src/Verlite.CLI/Show.cs b/src/Verlite.CLI/Show.cs index f3bb412..5b1a09e 100644 --- a/src/Verlite.CLI/Show.cs +++ b/src/Verlite.CLI/Show.cs @@ -11,6 +11,8 @@ internal enum Show patch = 3, prerelease = 4, metadata = 5, + height = 6, + json = 7, } internal static partial class Parsers { @@ -34,6 +36,8 @@ Show invalid() "PATCH" => Show.patch, "PRERELEASE" => Show.prerelease, "METADATA" => Show.metadata, + "HEIGHT" => Show.height, + "JSON" => Show.json, _ => invalid(), }; } diff --git a/src/Verlite.Core/PublicAPI.Unshipped.txt b/src/Verlite.Core/PublicAPI.Unshipped.txt index 7dc5c58..612b820 100644 --- a/src/Verlite.Core/PublicAPI.Unshipped.txt +++ b/src/Verlite.Core/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static Verlite.VersionCalculator.FromRepository2(Verlite.IRepoInspector! repo, Verlite.VersionCalculationOptions! options, Verlite.ILogger? log, Verlite.ITagFilter? tagFilter) -> System.Threading.Tasks.Task<(Verlite.SemVer version, Verlite.TaggedVersion? lastTag, int? height)>! diff --git a/src/Verlite.Core/VersionCalculator.cs b/src/Verlite.Core/VersionCalculator.cs index d1b5629..1a614c8 100644 --- a/src/Verlite.Core/VersionCalculator.cs +++ b/src/Verlite.Core/VersionCalculator.cs @@ -104,6 +104,7 @@ public static SemVer FromTagInfomation(SemVer? lastTag, VersionCalculationOption /// A log for diagnostics. /// A filter to ignore tags. When null no filter is used. /// The version for the state of the repository. + [Obsolete("Use FromRepository2()")] public static async Task FromRepository( IRepoInspector repo, VersionCalculationOptions options, @@ -120,5 +121,34 @@ public static async Task FromRepository( return FromTagInfomation(lastTagVer?.Version, options, height); } + + /// + /// Calculate the next version from a repository. + /// + /// The repo to use. + /// The options. + /// A log for diagnostics. + /// A filter to ignore tags. When null no filter is used. + /// The version for the state of the repository, and the associated tag information. + public static async Task<(SemVer version, TaggedVersion? lastTag, int? height)> FromRepository2( + IRepoInspector repo, + VersionCalculationOptions options, + ILogger? log, + ITagFilter? tagFilter) + { + if (options.VersionOverride.HasValue) + return (options.VersionOverride.Value, null, null); + + var (height, lastTag) = await HeightCalculator.FromRepository( + repo: repo, + tagPrefix: options.TagPrefix, + queryRemoteTags: options.QueryRemoteTags, + fetchTags: options.QueryRemoteTags, + log: log, + tagFilter: tagFilter); + + var version = FromTagInfomation(lastTag?.Version, options, height); + return (version, lastTag, height); + } } } diff --git a/src/Verlite.MsBuild/GetVersionTask.cs b/src/Verlite.MsBuild/GetVersionTask.cs index 413e7dd..20ffabe 100644 --- a/src/Verlite.MsBuild/GetVersionTask.cs +++ b/src/Verlite.MsBuild/GetVersionTask.cs @@ -109,6 +109,7 @@ private async Task ExecuteAsync() var version = opts.VersionOverride ?? new SemVer(); var commit = string.Empty; + var height = string.Empty; if (opts.VersionOverride is null) { using var repo = await GitRepoInspector.FromPath(ProjectDirectory, opts.Remote, log, commandRunner); @@ -117,8 +118,10 @@ private async Task ExecuteAsync() if (!string.IsNullOrWhiteSpace(FilterTags)) tagFilter = new CommandTagFilter(commandRunner, log, FilterTags, ProjectDirectory); - version = await VersionCalculator.FromRepository(repo, opts, log, tagFilter); + int? heightInt; + (version, _, heightInt) = await VersionCalculator.FromRepository2(repo, opts, log, tagFilter); commit = (await repo.GetHead())?.Id ?? string.Empty; + height = heightInt?.ToString(CultureInfo.InvariantCulture) ?? string.Empty; } Version = version.ToString(); @@ -128,6 +131,7 @@ private async Task ExecuteAsync() VersionPrerelease = version.Prerelease ?? string.Empty; VersionBuildMetadata = version.BuildMetadata ?? string.Empty; Commit = commit; + Height = height; return true; } @@ -138,5 +142,6 @@ private async Task ExecuteAsync() [Output] public string VersionPrerelease { get; private set; } = ""; [Output] public string VersionBuildMetadata { get; private set; } = ""; [Output] public string Commit { get; private set; } = ""; + [Output] public string Height { get; private set; } = ""; } } diff --git a/src/Verlite.MsBuild/VersionEmbedGenerator.cs b/src/Verlite.MsBuild/VersionEmbedGenerator.cs index 75c12d4..b62c8ef 100644 --- a/src/Verlite.MsBuild/VersionEmbedGenerator.cs +++ b/src/Verlite.MsBuild/VersionEmbedGenerator.cs @@ -24,6 +24,7 @@ public void Execute(GeneratorExecutionContext context) context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.VerlitePrerelease", out var prerelease); context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.VerliteBuildMetadata", out var meta); context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.VerliteCommit", out var commit); + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.VerliteHeight", out var height); version ??= string.Empty; major ??= string.Empty; @@ -32,6 +33,7 @@ public void Execute(GeneratorExecutionContext context) prerelease ??= string.Empty; meta ??= string.Empty; commit ??= string.Empty; + height ??= string.Empty; var coreVersion = string.IsNullOrEmpty(version) ? string.Empty : $"{major}.{minor}.{patch}"; @@ -54,6 +56,7 @@ internal static class Version public const string Prerelease = ""{prerelease}""; public const string BuildMetadata = ""{meta}""; public const string Commit = ""{commit}""; + public const string Height = ""{height}""; }} }} "; diff --git a/src/Verlite.MsBuild/build/Verlite.MsBuild.targets b/src/Verlite.MsBuild/build/Verlite.MsBuild.targets index 528eb19..8d52a7c 100644 --- a/src/Verlite.MsBuild/build/Verlite.MsBuild.targets +++ b/src/Verlite.MsBuild/build/Verlite.MsBuild.targets @@ -28,6 +28,7 @@ This file has been modified by Ashleigh Adams and adapted for Verlite --> + @@ -77,6 +78,7 @@ This file has been modified by Ashleigh Adams and adapted for Verlite --> + diff --git a/tests/UnitTests/VersionCalculationTests.cs b/tests/UnitTests/VersionCalculationTests.cs index 9e5470e..0b6bab2 100644 --- a/tests/UnitTests/VersionCalculationTests.cs +++ b/tests/UnitTests/VersionCalculationTests.cs @@ -108,9 +108,11 @@ public async Task NoCommitHasMinVersionAlpha1() { var repo = new MockRepoInspector(Array.Empty()); - var semver = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + var (semver, lastTag, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); semver.Should().Be(new SemVer(0, 1, 0, "alpha.1")); + lastTag.Should().BeNull(); + height.Should().Be(1); } [Fact] @@ -121,9 +123,11 @@ public async Task FirstCommitNoTagHasMinVersionAlpha1() new("commit_a"), }); - var semver = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + var (semver, lastTag, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); semver.Should().Be(new SemVer(0, 1, 0, "alpha.1")); + lastTag.Should().BeNull(); + height.Should().Be(1); } [Fact] @@ -134,9 +138,10 @@ public async Task FirstCommitTaggedHasExactVersion() new("commit_a") { Tags = new[] { "v5.4.3-rc.2.1" } }, }); - var semver = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + var (semver, _, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); semver.Should().Be(new SemVer(5, 4, 3, "rc.2.1")); + height.Should().Be(0); } [Fact] @@ -148,9 +153,10 @@ public async Task CommitAfterPrereleaseTagAppendsHeight() new("commit_a") { Tags = new[] { "v4.3.2-rc.1" } }, }); - var semver = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + var (semver, _, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); semver.Should().Be(new SemVer(4, 3, 2, "rc.1.1")); + height?.Should().Be(1); } [Fact] @@ -162,9 +168,11 @@ public async Task CommitAfterRtmTagAppendsHeightAndBumpsMinor() new("commit_a") { Tags = new[] { "v3.2.1" } }, }); - var semver = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + var (semver, lastTag, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); semver.Should().Be(new SemVer(3, 2, 2, "alpha.1")); + lastTag?.Tag.Name.Should().Be("v3.2.1"); + height?.Should().Be(1); } [Fact] @@ -177,7 +185,7 @@ public async Task LatestTagIsFetchedWithQueryRemoteTags() new("commit_a") { Tags = new[] { "v3.2.0" } }, }); - _ = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = true }); + _ = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true }, null, null); repo.LocalTags.Should().Contain(new Tag[] { new Tag("v3.2.1", new Commit("commit_b")) }); } @@ -192,7 +200,7 @@ public async Task LatestTagIsNotFetchedWithoutQueryRemoteTags() new("commit_a") { Tags = new[] { "v3.2.0" } }, }); - _ = await VersionCalculator.FromRepository(repo, new() { QueryRemoteTags = false }); + _ = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = false }, null, null); repo.LocalTags.Should().BeEmpty(); } @@ -205,12 +213,13 @@ public async Task TaggedBuildMetadataIsPreserved() new("commit_a") { Tags = new[] { "v1.2.3+4" } }, }); - var version = await VersionCalculator.FromRepository(repo, new() + var (version, _, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true, - }); + }, null, null); version.BuildMetadata.Should().Be("4"); + height.Should().Be(0); } [Fact] @@ -222,12 +231,13 @@ public async Task TaggedWithHeightBuildMetadataIsNotPreserved() new("commit_a") { Tags = new[] { "v1.2.3+4" } }, }); - var version = await VersionCalculator.FromRepository(repo, new() + var (version, _, height) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true, - }); + }, null, null); version.BuildMetadata.Should().BeNull(); + height.Should().Be(1); } [Fact] @@ -239,11 +249,11 @@ public async Task TaggedWithHeightStillUsesOptionsMetadata() new("commit_a") { Tags = new[] { "v1.2.3+4" } }, }); - var version = await VersionCalculator.FromRepository(repo, new() + var (version, _, _) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true, BuildMetadata = "git.a1b2c3d", - }); + }, null, null); version.BuildMetadata.Should().Be("git.a1b2c3d"); } @@ -256,11 +266,11 @@ public async Task DirectTagWithNoMetaUsesOnlyOptionsMetadata() new("commit_a") { Tags = new[] { "v1.2.3" } }, }); - var version = await VersionCalculator.FromRepository(repo, new() + var (version, _, _) = await VersionCalculator.FromRepository2(repo, new() { QueryRemoteTags = true, BuildMetadata = "git.a1b2c3d", - }); + }, null, null); version.BuildMetadata.Should().Be("git.a1b2c3d"); } @@ -273,11 +283,11 @@ public async Task TaggedBuildMetadataConcatenates() new("commit_a") { Tags = new[] { "v1.2.3+4" } }, }); - var version = await VersionCalculator.FromRepository(repo, new() + var (version, _, _) = await VersionCalculator.FromRepository2(repo, new() { BuildMetadata = "git.a1b2c3d", QueryRemoteTags = true, - }); + }, null, null); version.BuildMetadata.Should().Be("4-git.a1b2c3d"); }