diff --git a/MinVer.Lib/Versioner.cs b/MinVer.Lib/Versioner.cs index 29ca250e..921155a8 100644 --- a/MinVer.Lib/Versioner.cs +++ b/MinVer.Lib/Versioner.cs @@ -10,25 +10,32 @@ public static Version GetVersion(string workDir, string tagPrefix, MajorMinor mi var defaultPreReleaseIdentifiersList = defaultPreReleaseIdentifiers.ToList(); - var (version, height) = GetVersion(workDir, tagPrefix, defaultPreReleaseIdentifiersList, log); + var (version, height, isFromTag) = GetVersion(workDir, tagPrefix, defaultPreReleaseIdentifiersList, log); _ = height.HasValue && ignoreHeight && log.IsDebugEnabled && log.Debug("Ignoring height."); version = !height.HasValue || ignoreHeight ? version : version.WithHeight(height.Value, autoIncrement, defaultPreReleaseIdentifiersList); version = version.AddBuildMetadata(buildMeta); - var calculatedVersion = version.Satisfying(minMajorMinor, defaultPreReleaseIdentifiersList); + var ignoreMinMajorMinor = isFromTag && height is 0; - _ = calculatedVersion != version - ? log.IsInfoEnabled && log.Info($"Bumping version to {calculatedVersion} to satisfy minimum major minor {minMajorMinor}.") - : log.IsDebugEnabled && log.Debug($"The calculated version {calculatedVersion} satisfies the minimum major minor {minMajorMinor}."); + var calculatedVersion = + ignoreMinMajorMinor + ? version.Satisfying(MajorMinor.Default, defaultPreReleaseIdentifiersList) + : version.Satisfying(minMajorMinor, defaultPreReleaseIdentifiersList); + + _ = ignoreMinMajorMinor + ? minMajorMinor != MajorMinor.Default && log.IsDebugEnabled && log.Debug($"Ignoring minimum major minor {minMajorMinor} because the commit is tagged.") + : calculatedVersion != version + ? log.IsInfoEnabled && log.Info($"Bumping version to {calculatedVersion} to satisfy minimum major minor {minMajorMinor}.") + : log.IsDebugEnabled && log.Debug($"The calculated version {calculatedVersion} satisfies the minimum major minor {minMajorMinor}."); _ = log.IsInfoEnabled && log.Info($"Calculated version {calculatedVersion}."); return calculatedVersion; } - private static (Version Version, int? Height) GetVersion(string workDir, string tagPrefix, List defaultPreReleaseIdentifiers, ILogger log) + private static (Version Version, int? Height, bool IsFromTag) GetVersion(string workDir, string tagPrefix, List defaultPreReleaseIdentifiers, ILogger log) { if (!Git.IsWorkingDirectory(workDir, log)) { @@ -36,7 +43,7 @@ private static (Version Version, int? Height) GetVersion(string workDir, string _ = log.IsWarnEnabled && log.Warn(1001, $"'{workDir}' is not a valid Git working directory. Using default version {version}."); - return (version, default); + return (version, default, default); } if (!Git.TryGetHead(workDir, out var head, log)) @@ -45,7 +52,7 @@ private static (Version Version, int? Height) GetVersion(string workDir, string _ = log.IsInfoEnabled && log.Info($"No commits found. Using default version {version}."); - return (version, default); + return (version, default, default); } var tags = Git.GetTags(workDir, log); @@ -71,7 +78,7 @@ private static (Version Version, int? Height) GetVersion(string workDir, string _ = string.IsNullOrEmpty(selectedCandidate.Tag) && log.IsInfoEnabled && log.Info($"No commit found with a valid SemVer 2.0 version{(string.IsNullOrEmpty(tagPrefix) ? "" : $" prefixed with '{tagPrefix}'")}. Using default version {selectedCandidate.Version}."); _ = log.IsInfoEnabled && log.Info($"Using{(log.IsDebugEnabled && orderedCandidates.Count > 1 ? " " : " ")}{selectedCandidate.ToString(tagWidth, versionWidth, heightWidth)}."); - return (selectedCandidate.Version, selectedCandidate.Height); + return (selectedCandidate.Version, selectedCandidate.Height, !string.IsNullOrEmpty(selectedCandidate.Tag)); } private static List GetCandidates(Commit head, IEnumerable<(string Name, string Sha)> tags, string tagPrefix, IReadOnlyCollection defaultPreReleaseIdentifiers, ILogger log) diff --git a/MinVerTests.Lib/LogMessages.cs b/MinVerTests.Lib/LogMessages.cs index b06bdc9f..00036b98 100644 --- a/MinVerTests.Lib/LogMessages.cs +++ b/MinVerTests.Lib/LogMessages.cs @@ -13,7 +13,7 @@ public static class LogMessages [Theory] [InlineData(0, 0)] [InlineData(2, 0)] - public static async Task RepoWithHistory(int minMajor, int minMinor) + public static async Task MinimumMajorMinorAfterTag(int minMajor, int minMinor) { // arrange var minMajorMinor = new MajorMinor(minMajor, minMinor); @@ -67,8 +67,52 @@ git checkout main _ = Versioner.GetVersion(path, "", minMajorMinor, "", default, PreReleaseIdentifiers.Default, false, log); // assert - var logMessages = log.ToString(); + var logMessages = await ReplaceShas(log.ToString(), path); + await AssertFile.Contains($"../../../log.{minMajorMinor}.txt", logMessages); + } + + [Theory] + [InlineData(3, 0)] + public static async Task MinimumMajorMinorOnTag(int minMajor, int minMinor) + { + // arrange + var minMajorMinor = new MajorMinor(minMajor, minMinor); + + var historicalCommands = + @" +git commit --allow-empty -m '.' +git tag not-a-version +git checkout -b foo +git commit --allow-empty -m '.' +git tag 1.0.0-foo.1 +"; + + var path = MethodBase.GetCurrentMethod().GetTestDirectory(minMajorMinor); + + await EnsureEmptyRepository(path); + + foreach (var item in historicalCommands + .ToNonEmptyLines() + .Select((command, index) => new { Command = command, Index = $"{index}", })) + { + var nameAndArgs = item.Command.Split(" ", 2); + _ = await ReadAsync(nameAndArgs[0], nameAndArgs[1], path); + } + + var log = new TestLogger(); + + // act + _ = Versioner.GetVersion(path, "", minMajorMinor, "", default, PreReleaseIdentifiers.Default, false, log); + + // assert + var logMessages = await ReplaceShas(log.ToString(), path); + + await AssertFile.Contains($"../../../log.{minMajorMinor}.txt", logMessages); + } + + private static async Task ReplaceShas(string logMessages, string path) + { var shas = (await ReadAsync("git", "log --pretty=format:\"%H\"", path)) .StandardOutput .ToNonEmptyLines() @@ -85,6 +129,6 @@ git checkout main logMessages = logMessages.Replace(item.ShortSha, $"{item.Index}", StringComparison.Ordinal); } - await AssertFile.Contains($"../../../log.{minMajorMinor}.txt", logMessages); + return logMessages; } } diff --git a/MinVerTests.Lib/MinMajorMinor.cs b/MinVerTests.Lib/MinMajorMinor.cs index cf1ba564..56ccc60b 100644 --- a/MinVerTests.Lib/MinMajorMinor.cs +++ b/MinVerTests.Lib/MinMajorMinor.cs @@ -24,10 +24,10 @@ public static async Task NoCommits() } [Theory] - [InlineData("4.0.0", 3, 2, "4.0.0", true)] - [InlineData("4.3.0", 4, 3, "4.3.0", true)] - [InlineData("4.3.0", 5, 4, "5.4.0-alpha.0", false)] - public static async Task Tagged(string tag, int major, int minor, string expectedVersion, bool isRedundant) + [InlineData("4.0.0", 3, 2, "4.0.0")] + [InlineData("4.3.0", 4, 3, "4.3.0")] + [InlineData("4.3.0", 5, 4, "4.3.0")] + public static async Task Tagged(string tag, int major, int minor, string expectedVersion) { // arrange var path = MethodBase.GetCurrentMethod().GetTestDirectory((tag, major, minor)); @@ -41,10 +41,7 @@ public static async Task Tagged(string tag, int major, int minor, string expecte // assert Assert.Equal(expectedVersion, actualVersion.ToString()); - if (isRedundant) - { - Assert.Contains(logger.Messages, message => message.Text.Contains($"The calculated version {actualVersion} satisfies the minimum major minor {major}.{minor}.", StringComparison.Ordinal)); - } + Assert.Contains(logger.Messages, message => message.Text.Contains($"Ignoring minimum major minor {major}.{minor} because the commit is tagged.", StringComparison.Ordinal)); } [Fact] diff --git a/MinVerTests.Lib/log.3.0.txt b/MinVerTests.Lib/log.3.0.txt new file mode 100644 index 00000000..17a1c6ad --- /dev/null +++ b/MinVerTests.Lib/log.3.0.txt @@ -0,0 +1,32 @@ +Trace: Running Git: git status --short +Trace: Git exit code: 0 +Trace: Git stdout: +?? command-read-00.md +?? command-read-01.md +?? command-read-02.md +?? command-read-03.md + +Trace: Git stderr: + +Trace: Running Git: git log --pretty=format:"%H %P" +Trace: Git exit code: 0 +Trace: Git stdout: +1 0 +0 +Trace: Git stderr: + +Trace: Running Git: git show-ref --tags --dereference +Trace: Git exit code: 0 +Trace: Git stdout: +1 refs/tags/1.0.0-foo.1 +0 refs/tags/not-a-version + +Trace: Git stderr: + +Debug: Ignoring non-version tag { Name: not-a-version, Sha: 0 }. +Trace: Checking commit 1 (height 0)... +Trace: Found version tag { Commit: 1, Tag: '1.0.0-foo.1', Version: 1.0.0-foo.1, Height: 0 }. +Debug: 1 commits checked. +Info: Using { Commit: 1, Tag: '1.0.0-foo.1', Version: 1.0.0-foo.1, Height: 0 }. +Debug: Ignoring minimum major minor 3.0 because the commit is tagged. +Info: Calculated version 1.0.0-foo.1. diff --git a/MinVerTests.Packages/MinimumMajorMinorOnTag.cs b/MinVerTests.Packages/MinimumMajorMinorOnTag.cs index dfdb0989..c92928e1 100644 --- a/MinVerTests.Packages/MinimumMajorMinorOnTag.cs +++ b/MinVerTests.Packages/MinimumMajorMinorOnTag.cs @@ -19,7 +19,7 @@ public static async Task HasMinimumMajorMinor() var envVars = ("MinVerMinimumMajorMinor".ToAltCase(), "3.0"); - var expected = Package.WithVersion(3, 0, 0, ["alpha", "0",]); + var expected = Package.WithVersion(2, 3, 4); // act var (actual, _, _) = await Sdk.BuildProject(path, envVars: envVars); diff --git a/README.md b/README.md index dc32a64d..e32f5e49 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,8 @@ Note that `MinVerMinimumMajorMinor` will be redundant after you create the first Also note that if the latest version tag found in the commit history has a higher `MAJOR.MINOR` than `MinVerMinimumMajorMinor`, then `MinVerMinimumMajorMinor` will be ignored. +Lastly, if the the current commit has a version tag, the tag will be used as-is, even if it has a lower `MAJOR.MINOR` than `MinVerMinimumMajorMinor`. + ### Can I use my own pre-release versioning scheme? Yes! MinVer doesn't care what your pre-release versioning scheme is. The default pre-release identifiers are `alpha.0`, but you can use whatever you like in your tags. If your versioning scheme is valid [SemVer 2.x](https://semver.org/spec/v2.0.0.html), it will work with MinVer.