From f3947a957a9278dd4b16ef0c49865b717cf7592d Mon Sep 17 00:00:00 2001 From: Thomas Gillen Date: Fri, 10 Jul 2015 11:15:46 +0100 Subject: [PATCH] Add support for indicating change severity in commit messages While active, the version increment is determined by reading commit/merge commit messages for markers describing the change severity. --- ...riteOutEffectiveConfiguration.approved.txt | 4 + .../TestEffectiveConfiguration.cs | 10 +- .../Configuration/BranchConfig.cs | 4 + src/GitVersionCore/Configuration/Config.cs | 12 ++ .../Configuration/ConfigurationProvider.cs | 4 + .../Configuration/IncrementStrategy.cs | 24 +++- src/GitVersionCore/EffectiveConfiguration.cs | 18 ++- src/GitVersionCore/FodyWeavers.xml | 2 +- src/GitVersionCore/GitVersionContext.cs | 14 ++- src/GitVersionCore/GitVersionCore.csproj | 1 + src/GitVersionCore/IncrementStrategyFinder.cs | 107 ++++++++++++++++++ src/GitVersionCore/SemanticVersion.cs | 18 ++- .../BaseVersionCalculator.cs | 8 +- .../NextVersionCalculator.cs | 5 +- src/GitVersionCore/packages.config | 2 +- src/GitVersionExe/FodyWeavers.xml | 2 +- src/GitVersionExe/packages.config | 2 +- 17 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 src/GitVersionCore/IncrementStrategyFinder.cs diff --git a/src/GitVersionCore.Tests/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt b/src/GitVersionCore.Tests/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt index 2460062693..21e3799605 100644 --- a/src/GitVersionCore.Tests/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt +++ b/src/GitVersionCore.Tests/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt @@ -2,6 +2,10 @@ mode: ContinuousDelivery tag-prefix: '[vV]' continuous-delivery-fallback-tag: ci +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +commit-message-incrementing: Enabled branches: master: mode: ContinuousDelivery diff --git a/src/GitVersionCore.Tests/TestEffectiveConfiguration.cs b/src/GitVersionCore.Tests/TestEffectiveConfiguration.cs index 3bd2932dc7..791bdd3436 100644 --- a/src/GitVersionCore.Tests/TestEffectiveConfiguration.cs +++ b/src/GitVersionCore.Tests/TestEffectiveConfiguration.cs @@ -14,10 +14,16 @@ public TestEffectiveConfiguration( bool preventIncrementForMergedBranchVersion = false, string tagNumberPattern = null, string continuousDeploymentFallbackTag = "ci", - bool trackMergeTarget = false) : + bool trackMergeTarget = false, + string majorMessage = null, + string minorMessage = null, + string patchMessage = null, + CommitMessageIncrementMode commitMessageMode = CommitMessageIncrementMode.Enabled) : base(assemblyVersioningScheme, versioningMode, gitTagPrefix, tag, nextVersion, IncrementStrategy.Patch, branchPrefixToTrim, preventIncrementForMergedBranchVersion, tagNumberPattern, continuousDeploymentFallbackTag, - trackMergeTarget) + trackMergeTarget, + majorMessage, minorMessage, patchMessage, + commitMessageMode) { } } diff --git a/src/GitVersionCore/Configuration/BranchConfig.cs b/src/GitVersionCore/Configuration/BranchConfig.cs index f20836adaa..821a413811 100644 --- a/src/GitVersionCore/Configuration/BranchConfig.cs +++ b/src/GitVersionCore/Configuration/BranchConfig.cs @@ -16,6 +16,7 @@ public BranchConfig(BranchConfig branchConfiguration) PreventIncrementOfMergedBranchVersion = branchConfiguration.PreventIncrementOfMergedBranchVersion; TagNumberPattern = branchConfiguration.TagNumberPattern; TrackMergeTarget = branchConfiguration.TrackMergeTarget; + CommitMessageIncrementing = branchConfiguration.CommitMessageIncrementing; } [YamlMember(Alias = "mode")] @@ -38,5 +39,8 @@ public BranchConfig(BranchConfig branchConfiguration) [YamlMember(Alias = "track-merge-target")] public bool? TrackMergeTarget { get; set; } + + [YamlMember(Alias = "commit-message-incrementing")] + public CommitMessageIncrementMode? CommitMessageIncrementing { get; set; } } } diff --git a/src/GitVersionCore/Configuration/Config.cs b/src/GitVersionCore/Configuration/Config.cs index 1b8865e2ae..b630e93ba8 100644 --- a/src/GitVersionCore/Configuration/Config.cs +++ b/src/GitVersionCore/Configuration/Config.cs @@ -23,6 +23,18 @@ public class Config [YamlMember(Alias = "next-version")] public string NextVersion { get; set; } + [YamlMember(Alias = "major-version-bump-message")] + public string MajorVersionBumpMessage { get; set; } + + [YamlMember(Alias = "minor-version-bump-message")] + public string MinorVersionBumpMessage { get; set; } + + [YamlMember(Alias = "patch-version-bump-message")] + public string PatchVersionBumpMessage { get; set; } + + [YamlMember(Alias = "commit-message-incrementing")] + public CommitMessageIncrementMode? CommitMessageIncrementing { get; set; } + [YamlMember(Alias = "branches")] public Dictionary Branches { diff --git a/src/GitVersionCore/Configuration/ConfigurationProvider.cs b/src/GitVersionCore/Configuration/ConfigurationProvider.cs index 914f32875b..8ab8e006b6 100644 --- a/src/GitVersionCore/Configuration/ConfigurationProvider.cs +++ b/src/GitVersionCore/Configuration/ConfigurationProvider.cs @@ -24,6 +24,10 @@ public static void ApplyDefaultsTo(Config config) config.TagPrefix = config.TagPrefix ?? DefaultTagPrefix; config.VersioningMode = config.VersioningMode ?? VersioningMode.ContinuousDelivery; config.ContinuousDeploymentFallbackTag = config.ContinuousDeploymentFallbackTag ?? "ci"; + config.MajorVersionBumpMessage = config.MajorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMajorPattern; + config.MinorVersionBumpMessage = config.MinorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMinorPattern; + config.PatchVersionBumpMessage = config.PatchVersionBumpMessage ?? IncrementStrategyFinder.DefaultPatchPattern; + config.CommitMessageIncrementing = config.CommitMessageIncrementing ?? CommitMessageIncrementMode.Enabled; var configBranches = config.Branches.ToList(); ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "master"), defaultTag: string.Empty, defaultPreventIncrement: true); diff --git a/src/GitVersionCore/Configuration/IncrementStrategy.cs b/src/GitVersionCore/Configuration/IncrementStrategy.cs index cd3b8de0b7..17a420899c 100644 --- a/src/GitVersionCore/Configuration/IncrementStrategy.cs +++ b/src/GitVersionCore/Configuration/IncrementStrategy.cs @@ -1,5 +1,7 @@ namespace GitVersion { + using System; + public enum IncrementStrategy { None, @@ -9,6 +11,26 @@ public enum IncrementStrategy /// /// Uses the increment strategy from the branch the current branch was branched from /// - Inherit + Inherit + } + + public static class IncrementStrategyExtensions + { + public static VersionField ToVersionField(this IncrementStrategy strategy) + { + switch (strategy) + { + case IncrementStrategy.None: + return VersionField.None; + case IncrementStrategy.Major: + return VersionField.Major; + case IncrementStrategy.Minor: + return VersionField.Minor; + case IncrementStrategy.Patch: + return VersionField.Patch; + default: + throw new ArgumentOutOfRangeException("strategy", strategy, null); + } + } } } \ No newline at end of file diff --git a/src/GitVersionCore/EffectiveConfiguration.cs b/src/GitVersionCore/EffectiveConfiguration.cs index 6945a856d0..f635be7db1 100644 --- a/src/GitVersionCore/EffectiveConfiguration.cs +++ b/src/GitVersionCore/EffectiveConfiguration.cs @@ -13,7 +13,11 @@ public EffectiveConfiguration( bool preventIncrementForMergedBranchVersion, string tagNumberPattern, string continuousDeploymentFallbackTag, - bool trackMergeTarget) + bool trackMergeTarget, + string majorVersionBumpMessage, + string minorVersionBumpMessage, + string patchVersionBumpMessage, + CommitMessageIncrementMode commitMessageIncrementing) { AssemblyVersioningScheme = assemblyVersioningScheme; VersioningMode = versioningMode; @@ -26,6 +30,10 @@ public EffectiveConfiguration( TagNumberPattern = tagNumberPattern; ContinuousDeploymentFallbackTag = continuousDeploymentFallbackTag; TrackMergeTarget = trackMergeTarget; + MajorVersionBumpMessage = majorVersionBumpMessage; + MinorVersionBumpMessage = minorVersionBumpMessage; + PatchVersionBumpMessage = patchVersionBumpMessage; + CommitMessageIncrementing = commitMessageIncrementing; } public VersioningMode VersioningMode { get; private set; } @@ -55,5 +63,13 @@ public EffectiveConfiguration( public string ContinuousDeploymentFallbackTag { get; private set; } public bool TrackMergeTarget { get; private set; } + + public string MajorVersionBumpMessage { get; private set; } + + public string MinorVersionBumpMessage { get; private set; } + + public string PatchVersionBumpMessage { get; private set; } + + public CommitMessageIncrementMode CommitMessageIncrementing { get; private set; } } } \ No newline at end of file diff --git a/src/GitVersionCore/FodyWeavers.xml b/src/GitVersionCore/FodyWeavers.xml index 371130fa3e..b1f1923b49 100644 --- a/src/GitVersionCore/FodyWeavers.xml +++ b/src/GitVersionCore/FodyWeavers.xml @@ -1,4 +1,4 @@ - + diff --git a/src/GitVersionCore/GitVersionContext.cs b/src/GitVersionCore/GitVersionContext.cs index ee8d38416f..6cf7f76bf5 100644 --- a/src/GitVersionCore/GitVersionContext.cs +++ b/src/GitVersionCore/GitVersionContext.cs @@ -91,6 +91,8 @@ void CalculateEffectiveConfiguration() throw new Exception(string.Format("Configuration value for 'TrackMergeTarget' for branch {0} has no value. (this should not happen, please report an issue)", currentBranchConfig.Key)); if (!configuration.AssemblyVersioningScheme.HasValue) throw new Exception("Configuration value for 'AssemblyVersioningScheme' has no value. (this should not happen, please report an issue)"); + if (!configuration.CommitMessageIncrementing.HasValue) + throw new Exception("Configuration value for 'CommitMessageIncrementing' has no value. (this should not happen, please report an issue)"); var versioningMode = currentBranchConfig.Value.VersioningMode.Value; var tag = currentBranchConfig.Value.Tag; @@ -98,16 +100,24 @@ void CalculateEffectiveConfiguration() var incrementStrategy = currentBranchConfig.Value.Increment.Value; var preventIncrementForMergedBranchVersion = currentBranchConfig.Value.PreventIncrementOfMergedBranchVersion.Value; var trackMergeTarget = currentBranchConfig.Value.TrackMergeTarget.Value; - + var nextVersion = configuration.NextVersion; var assemblyVersioningScheme = configuration.AssemblyVersioningScheme.Value; var gitTagPrefix = configuration.TagPrefix; + var majorMessage = configuration.MajorVersionBumpMessage; + var minorMessage = configuration.MinorVersionBumpMessage; + var patchMessage = configuration.MinorVersionBumpMessage; + + var commitMessageVersionBump = currentBranchConfig.Value.CommitMessageIncrementing ?? configuration.CommitMessageIncrementing.Value; + Configuration = new EffectiveConfiguration( assemblyVersioningScheme, versioningMode, gitTagPrefix, tag, nextVersion, incrementStrategy, currentBranchConfig.Key, preventIncrementForMergedBranchVersion, tagNumberPattern, configuration.ContinuousDeploymentFallbackTag, - trackMergeTarget); + trackMergeTarget, + majorMessage, minorMessage, patchMessage, + commitMessageVersionBump); } } } \ No newline at end of file diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index 3e2ee4ad07..12e10600c1 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -113,6 +113,7 @@ + diff --git a/src/GitVersionCore/IncrementStrategyFinder.cs b/src/GitVersionCore/IncrementStrategyFinder.cs new file mode 100644 index 0000000000..1813ece0cd --- /dev/null +++ b/src/GitVersionCore/IncrementStrategyFinder.cs @@ -0,0 +1,107 @@ +namespace GitVersion +{ + using System.Collections.Generic; + using System.Linq; + using System.Text.RegularExpressions; + using VersionCalculation.BaseVersionCalculators; + using LibGit2Sharp; + + public enum CommitMessageIncrementMode + { + Enabled, + Disabled, + MergeMessageOnly + } + + public static class IncrementStrategyFinder + { + public const string DefaultMajorPattern = @"\+semver:\s?(breaking|major)"; + public const string DefaultMinorPattern = @"\+semver:\s?(feature|minor)"; + public const string DefaultPatchPattern = @"\+semver:\s?(fix|patch)"; + + public static VersionField? DetermineIncrementedField(GitVersionContext context, BaseVersion baseVersion) + { + var commitMessageIncrement = FindCommitMessageIncrement(context, baseVersion); + var defaultIncrement = context.Configuration.Increment.ToVersionField(); + + // use the default branch config increment strategy if there are no commit message overrides + if (commitMessageIncrement == null) + { + return baseVersion.ShouldIncrement ? defaultIncrement : (VersionField?)null; + } + + // cap the commit message severity to minor for alpha versions + if (baseVersion.SemanticVersion < new SemanticVersion(1) && commitMessageIncrement > VersionField.Minor) + { + commitMessageIncrement = VersionField.Minor; + } + + // don't increment for less than the branch config increment, if the absense of commit messages would have + // still resulted in an increment of configuration.Increment + if (baseVersion.ShouldIncrement && commitMessageIncrement < defaultIncrement) + { + return defaultIncrement; + } + + return commitMessageIncrement; + } + + private static VersionField? FindCommitMessageIncrement(GitVersionContext context, BaseVersion baseVersion) + { + if (context.Configuration.CommitMessageIncrementing == CommitMessageIncrementMode.Disabled) + { + return null; + } + + var commits = GetIntermediateCommits(context.Repository, baseVersion.BaseVersionSource, context.CurrentCommit); + + if (context.Configuration.CommitMessageIncrementing == CommitMessageIncrementMode.MergeMessageOnly) + { + commits = commits.Where(c => c.Parents.Count() > 1); + } + + var majorRegex = CreateRegex(context.Configuration.MajorVersionBumpMessage ?? DefaultMajorPattern); + var minorRegex = CreateRegex(context.Configuration.MinorVersionBumpMessage ?? DefaultMinorPattern); + var patchRegex = CreateRegex(context.Configuration.PatchVersionBumpMessage ?? DefaultPatchPattern); + + var increments = commits + .Select(c => FindIncrementFromMessage(c.Message, majorRegex, minorRegex, patchRegex)) + .Where(v => v != null) + .Select(v => v.Value) + .ToList(); + + if (increments.Any()) + { + return increments.Max(); + } + + return null; + } + + private static IEnumerable GetIntermediateCommits(IRepository repo, Commit baseCommit, Commit headCommit) + { + var filter = new CommitFilter + { + Since = headCommit, + Until = baseCommit, + SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse + }; + + return repo.Commits.QueryBy(filter); + } + + private static VersionField? FindIncrementFromMessage(string message, Regex major, Regex minor, Regex patch) + { + if (major.IsMatch(message)) return VersionField.Major; + if (minor.IsMatch(message)) return VersionField.Minor; + if (patch.IsMatch(message)) return VersionField.Patch; + + return null; + } + + private static Regex CreateRegex(string pattern) + { + return new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); + } + } +} diff --git a/src/GitVersionCore/SemanticVersion.cs b/src/GitVersionCore/SemanticVersion.cs index 44775a82f4..46ea1cbb38 100644 --- a/src/GitVersionCore/SemanticVersion.cs +++ b/src/GitVersionCore/SemanticVersion.cs @@ -281,25 +281,25 @@ public string ToString(string format, IFormatProvider formatProvider = null) } } - public SemanticVersion IncrementVersion(IncrementStrategy incrementStrategy) + public SemanticVersion IncrementVersion(VersionField incrementStrategy) { var incremented = new SemanticVersion(this); if (!incremented.PreReleaseTag.HasTag()) { switch (incrementStrategy) { - case IncrementStrategy.None: + case VersionField.None: break; - case IncrementStrategy.Major: + case VersionField.Major: incremented.Major++; incremented.Minor = 0; incremented.Patch = 0; break; - case IncrementStrategy.Minor: + case VersionField.Minor: incremented.Minor++; incremented.Patch = 0; break; - case IncrementStrategy.Patch: + case VersionField.Patch: incremented.Patch++; break; default: @@ -318,4 +318,12 @@ public SemanticVersion IncrementVersion(IncrementStrategy incrementStrategy) return incremented; } } + + public enum VersionField + { + None, + Patch, + Minor, + Major + } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs index efe2888588..8747649830 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculator.cs @@ -74,7 +74,13 @@ public BaseVersion GetBaseVersion(GitVersionContext context) static SemanticVersion MaybeIncrement(GitVersionContext context, BaseVersion version) { - return version.ShouldIncrement ? version.SemanticVersion.IncrementVersion(context.Configuration.Increment) : version.SemanticVersion; + var increment = IncrementStrategyFinder.DetermineIncrementedField(context, version); + if (increment != null) + { + return version.SemanticVersion.IncrementVersion(increment.Value); + } + + return version.SemanticVersion; } } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs index 114c990dde..e4d3d09dac 100644 --- a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs @@ -38,9 +38,10 @@ public SemanticVersion FindVersion(GitVersionContext context) var baseVersion = baseVersionFinder.GetBaseVersion(context); var semver = baseVersion.SemanticVersion; - if (baseVersion.ShouldIncrement) + var increment = IncrementStrategyFinder.DetermineIncrementedField(context, baseVersion); + if (increment != null) { - semver = semver.IncrementVersion(context.Configuration.Increment); + semver = semver.IncrementVersion(increment.Value); } else Logger.WriteInfo("Skipping version increment"); diff --git a/src/GitVersionCore/packages.config b/src/GitVersionCore/packages.config index 08df824b16..0dee94dc32 100644 --- a/src/GitVersionCore/packages.config +++ b/src/GitVersionCore/packages.config @@ -5,6 +5,6 @@ - + \ No newline at end of file diff --git a/src/GitVersionExe/FodyWeavers.xml b/src/GitVersionExe/FodyWeavers.xml index f0d86efadb..2fd484f975 100644 --- a/src/GitVersionExe/FodyWeavers.xml +++ b/src/GitVersionExe/FodyWeavers.xml @@ -1,4 +1,4 @@ - + diff --git a/src/GitVersionExe/packages.config b/src/GitVersionExe/packages.config index 43718c921a..7d5720e321 100644 --- a/src/GitVersionExe/packages.config +++ b/src/GitVersionExe/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file