From 070978c8a79d57de3938159f5eb4ca868ce46dc8 Mon Sep 17 00:00:00 2001 From: Kieranties Date: Tue, 12 Mar 2019 12:04:44 +0000 Subject: [PATCH] Sync release/0.1 with master (#59) * Initial token rule implementation (#52) * Enable {branchname} and {shortsha} Token replacement to contribute towards #13 * Add shortbranchname token (#53) * #54 - Better error handling for malformed JSON (#56) * Fix #55 - Implement branchnamesuffix (#57) * Feature/overrides (#58) * Base model for overrides * Enable branch override configuration Contributes to #29 * Sample for branch overrides * Use new versioning overrides - bump label --- .simpleversion.json | 11 ++ docs/Configuration.md | 22 ++- docs/Results.md | 1 + docs/samples/.simpleversion.json | 28 ++++ integration/SimpleVersion.Command.Tests.ps1 | 62 +++++++- integration/assets/.simpleversion.json | 18 ++- .../Model/BranchConfiguration.cs | 13 ++ .../Model/BranchInfo.cs | 2 + .../Model/LabelConfiguration.cs | 13 ++ src/SimpleVersion.Abstractions/Rules/IRule.cs | 14 ++ .../Formatting/Semver1FormatProcess.cs | 43 ++---- .../Formatting/Semver2FormatProcess.cs | 47 ++---- .../Formatting/VersionFormatProcess.cs | 7 +- .../Pipeline/ResolveConfigurationProcess.cs | 68 +++++++-- .../Pipeline/VersionCalculator.cs | 6 +- .../Rules/BaseBranchNameRule.cs | 49 ++++++ .../Rules/BranchNameRule.cs | 24 +++ .../Rules/BranchNameSuffixRule.cs | 27 ++++ src/SimpleVersion.Core/Rules/HeightRule.cs | 48 ++++++ .../Rules/RuleExtensions.cs | 26 ++++ .../Rules/ShortBranchNameRule.cs | 24 +++ src/SimpleVersion.Core/Rules/ShortShaRule.cs | 36 +++++ .../Model/BranchConfigurationFixture.cs | 21 +++ .../Model/BranchInfoFixture.cs | 1 + .../Formatting/Semver1FormatProcessFixture.cs | 11 +- .../ResolveConfigurationProcessFixture.cs | 140 +++++++++++++++++- .../Rules/BranchNameRuleFixture.cs | 89 +++++++++++ .../Rules/BranchNameSuffixRuleFixture.cs | 89 +++++++++++ .../Rules/HeightRuleFixture.cs | 118 +++++++++++++++ .../Rules/ShortBranchNameRuleFixture.cs | 90 +++++++++++ .../Rules/ShortShaRuleFixture.cs | 76 ++++++++++ 31 files changed, 1118 insertions(+), 106 deletions(-) create mode 100644 docs/samples/.simpleversion.json create mode 100644 src/SimpleVersion.Abstractions/Model/BranchConfiguration.cs create mode 100644 src/SimpleVersion.Abstractions/Model/LabelConfiguration.cs create mode 100644 src/SimpleVersion.Abstractions/Rules/IRule.cs create mode 100644 src/SimpleVersion.Core/Rules/BaseBranchNameRule.cs create mode 100644 src/SimpleVersion.Core/Rules/BranchNameRule.cs create mode 100644 src/SimpleVersion.Core/Rules/BranchNameSuffixRule.cs create mode 100644 src/SimpleVersion.Core/Rules/HeightRule.cs create mode 100644 src/SimpleVersion.Core/Rules/RuleExtensions.cs create mode 100644 src/SimpleVersion.Core/Rules/ShortBranchNameRule.cs create mode 100644 src/SimpleVersion.Core/Rules/ShortShaRule.cs create mode 100644 test/SimpleVersion.Abstractions.Tests/Model/BranchConfigurationFixture.cs create mode 100644 test/SimpleVersion.Core.Tests/Rules/BranchNameRuleFixture.cs create mode 100644 test/SimpleVersion.Core.Tests/Rules/BranchNameSuffixRuleFixture.cs create mode 100644 test/SimpleVersion.Core.Tests/Rules/HeightRuleFixture.cs create mode 100644 test/SimpleVersion.Core.Tests/Rules/ShortBranchNameRuleFixture.cs create mode 100644 test/SimpleVersion.Core.Tests/Rules/ShortShaRuleFixture.cs diff --git a/.simpleversion.json b/.simpleversion.json index bf3955f..f0191f8 100644 --- a/.simpleversion.json +++ b/.simpleversion.json @@ -6,6 +6,17 @@ "^refs/heads/master$", "^refs/heads/preview/.+$", "^refs/heads/release/.+$" + ], + "overrides": [ + { + "match": "^refs/heads/feature/.+$", + "metadata": [ "{shortbranchname}" ] + }, + { + "match": "^refs/heads/release/.+$", + "label": [], + "metadata": [ "*" ] + } ] } } \ No newline at end of file diff --git a/docs/Configuration.md b/docs/Configuration.md index 6ec6392..050c273 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -19,8 +19,6 @@ All of the following are accepted values: "Version" : "1.2.3.4" ``` -> You may also specify '*' in place of a number to insert the generated height. - Label ----- @@ -33,7 +31,6 @@ The `Label` property specifies an array of labels to be included in the version. "Label" : ["alpha1"] "Label" : ["alpha1", "test"] ``` -> You may also specify '*' in place of a value to insert the generated height. MetaData -------- @@ -49,8 +46,6 @@ in the final version. "MetaData" : ["demo", "sprint1"] ``` -> You may also specify '*' in place of a value to insert the generated height. - OffSet ------ @@ -98,4 +93,19 @@ Semver2 verison of `0.1.0-alpha2.5` when there are five commits. All other branches will append the short sha, generating a Semver2 version of `0.1.0-alpha2.5.c903782` when there are five commits and the sha begins with -_903782_ \ No newline at end of file +_903782_ + +Replacement Tokens +------------------ + +SimpleVersion allows specific _tokens_ to be used in some properties to allow +substitution of values during invocation. The following tokens may be used: + + +| Name | Token | Where | Description | +| ------------------ | -------------------- | ------------------------------ | -------------------------------------------------------------------------- | +| Height | `*` | `Version`, `Label`, `Metadata` | Inserts the calculated height | +| Branch Name | `{branchname}` | `Label`, `Metadata` | Inserts the canonical branch name, stripped of non-alphanumeric characters | +| Short Branch Name | `{shortbranchname}` | `Label`, `Metadata` | Inserts the friendly branch name, stripped of non-alphanumeric characters | +| Branch Name Suffix | `{branchnamesuffix}` | `Label`, `Metadata` | Inserts the last segment of the canonical name of a branch | +| Short Sha | `{shortsha}` | `Label`, `Metadata` | Inserts the first seven characters of the commit sha, prefixed with `c` | \ No newline at end of file diff --git a/docs/Results.md b/docs/Results.md index 2ad9911..ae81620 100644 --- a/docs/Results.md +++ b/docs/Results.md @@ -16,6 +16,7 @@ The following is an example from invoking the command line tool: "Height": 18, "HeightPadded": "0018", "Sha": "ebc8f22ae83bfa3c1e36d6bf70c2a383ae30c9dd", + "CanonicalBranchName": "refs/heads/preview/test", "BranchName": "preview/test", "Formats": { "Semver1": "0.1.0-alpha2-0018", diff --git a/docs/samples/.simpleversion.json b/docs/samples/.simpleversion.json new file mode 100644 index 0000000..3524ab0 --- /dev/null +++ b/docs/samples/.simpleversion.json @@ -0,0 +1,28 @@ +// An example branch strategy +{ + // Specify the base version number + "version": "1.0.0", + // Specify the base label - Use the current branchname, height appended by default + "label": [ "{branchname}" ], + "branches": { + // The following branches will build packages to be released + "release": [ + "master$", + "release/.+" + ], + "overrides" : [ + { + // The master branch will always label as rc1 with sha metadata + "match": "master$", + "label": ["rc1"], + "metadata" : ["{shortsha}"] + }, + { + // The release branch will have no label and put height in the metadata + "match": "release/.*", + "label": [], + "metadata" : ["*"] + } + ] + } +} \ No newline at end of file diff --git a/integration/SimpleVersion.Command.Tests.ps1 b/integration/SimpleVersion.Command.Tests.ps1 index 0c30607..94a3098 100644 --- a/integration/SimpleVersion.Command.Tests.ps1 +++ b/integration/SimpleVersion.Command.Tests.ps1 @@ -74,7 +74,7 @@ Describe 'SimpleVersion.Command'{ } } - It 'Returns base values for initial commit'{ + It 'Returns base values for initial commit' { Copy-Item $PSScriptRoot\assets\.simpleversion.json -Destination $pwd git add * git commit -m 'Initial commit' @@ -130,7 +130,7 @@ Describe 'SimpleVersion.Command'{ git commit -m 'empty' --allow-empty $json = Get-Content $pwd\.simpleversion.json -Raw | ConvertFrom-Json $json.Version = "1.0.0" - Set-Content $pwd\.simpleversion.json (ConvertTo-Json $json) + Set-Content $pwd\.simpleversion.json (ConvertTo-Json $json -Depth 100) git add * git commit -m 'Updated version' git commit -m 'empty' --allow-empty @@ -154,4 +154,62 @@ Describe 'SimpleVersion.Command'{ } } } + + Context 'Branch Overrides' { + Context 'Override Matches' { + + BeforeAll { + $dir = New-Item "${TestDrive}\$(Get-Random)" -ItemType Directory + Push-Location $dir + git init + git config user.email "SimpleVersion@Kieranties.com" + git config user.name "Simple Version" + + Copy-Item $PSScriptRoot\assets\.simpleversion.json -Destination $pwd + git add * + git commit -m 'Initial commit' + + $sha = git rev-parse HEAD + $expectedSha = "c$($sha.Substring(0, 7))" + } + + AfterAll { + Pop-Location + Remove-Item $dir -Recurse -Force + } + + It 'Returns override label and meta if provided' { + + git checkout -b test/feature + $result = Invoke + Validate $result -AsSuccess { + $json.BranchName | Should -Be 'test/feature' + $json.Formats.Semver1 | Should -Be '0.1.0-testfeature-0001' + $json.Formats.Semver2 | Should -Be '0.1.0-testfeature.1+internal' + } + } + + It 'Returns override label only if provided' { + + git checkout -b test/hotfix + $result = Invoke + Validate $result -AsSuccess { + $json.BranchName | Should -Be 'test/hotfix' + $json.Formats.Semver1 | Should -Be "0.1.0-$expectedSha-0001" + $json.Formats.Semver2 | Should -Be "0.1.0-$expectedSha.1" + } + } + + It 'Returns override label only if provided' { + + git checkout -b test/release + $result = Invoke + Validate $result -AsSuccess { + $json.BranchName | Should -Be 'test/release' + $json.Formats.Semver1 | Should -Be "0.1.0-alpha1-0001" + $json.Formats.Semver2 | Should -Be "0.1.0-alpha1.1+1.$expectedSha" + } + } + } + } } \ No newline at end of file diff --git a/integration/assets/.simpleversion.json b/integration/assets/.simpleversion.json index d95c775..4bef30a 100644 --- a/integration/assets/.simpleversion.json +++ b/integration/assets/.simpleversion.json @@ -5,7 +5,23 @@ "release": [ "^refs/heads/master$", "^refs/heads/preview/.+$", - "^refs/heads/release/.+$" + "^refs/heads/release/.+$", + "^refs/heads/test/.+$" + ], + "overrides" : [ + { + "match": "test/feature", + "label": ["{branchname}"], + "metadata" : ["internal"] + }, + { + "match": "test/release", + "metadata": ["*", "{shortsha}"] + }, + { + "match": "test/hotfix", + "label": ["{shortsha}"] + } ] } } \ No newline at end of file diff --git a/src/SimpleVersion.Abstractions/Model/BranchConfiguration.cs b/src/SimpleVersion.Abstractions/Model/BranchConfiguration.cs new file mode 100644 index 0000000..a883c20 --- /dev/null +++ b/src/SimpleVersion.Abstractions/Model/BranchConfiguration.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace SimpleVersion.Model +{ + public class BranchConfiguration + { + public string Match { get; set; } = string.Empty; + + public List Label { get; set; } = null; + + public List MetaData { get; set; } = null; + } +} diff --git a/src/SimpleVersion.Abstractions/Model/BranchInfo.cs b/src/SimpleVersion.Abstractions/Model/BranchInfo.cs index 5654bb3..2f52fb4 100644 --- a/src/SimpleVersion.Abstractions/Model/BranchInfo.cs +++ b/src/SimpleVersion.Abstractions/Model/BranchInfo.cs @@ -6,5 +6,7 @@ namespace SimpleVersion.Model public class BranchInfo { public List Release { get; } = new List(); + + public List Overrides { get; } = new List(); } } diff --git a/src/SimpleVersion.Abstractions/Model/LabelConfiguration.cs b/src/SimpleVersion.Abstractions/Model/LabelConfiguration.cs new file mode 100644 index 0000000..0c3da5b --- /dev/null +++ b/src/SimpleVersion.Abstractions/Model/LabelConfiguration.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SimpleVersion.Model +{ + public class LabelConfiguration + { + public List Label { get; } = new List(); + } +} diff --git a/src/SimpleVersion.Abstractions/Rules/IRule.cs b/src/SimpleVersion.Abstractions/Rules/IRule.cs new file mode 100644 index 0000000..5d9d6c5 --- /dev/null +++ b/src/SimpleVersion.Abstractions/Rules/IRule.cs @@ -0,0 +1,14 @@ +using SimpleVersion.Pipeline; +using System.Collections.Generic; + +namespace SimpleVersion.Rules +{ + public interface IRule + { + string Token { get; } + + T Resolve(VersionContext context, T value); + + IEnumerable Apply(VersionContext context, IEnumerable value); + } +} diff --git a/src/SimpleVersion.Core/Pipeline/Formatting/Semver1FormatProcess.cs b/src/SimpleVersion.Core/Pipeline/Formatting/Semver1FormatProcess.cs index e2dfaa7..ac9081d 100644 --- a/src/SimpleVersion.Core/Pipeline/Formatting/Semver1FormatProcess.cs +++ b/src/SimpleVersion.Core/Pipeline/Formatting/Semver1FormatProcess.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; +using SimpleVersion.Rules; namespace SimpleVersion.Pipeline.Formatting { @@ -7,37 +6,17 @@ public class Semver1FormatProcess : ICalculatorProcess { public void Apply(VersionContext context) { - var labelParts = new List(context.Configuration.Label); - - if (!context.Configuration.Version.Contains("*")) - { - // if we have a label, ensure height is included - if (labelParts.Count != 0 && !labelParts.Contains("*")) - labelParts.Add("*"); - } - - // add short sha if required - if (labelParts.Count > 0) + var rules = new IRule[] { - var addShortSha = true; - foreach (var pattern in context.Configuration.Branches.Release) - { - if (Regex.IsMatch(context.Result.CanonicalBranchName, pattern)) - { - addShortSha = false; - break; - } - } - - if (addShortSha) - { - var shortSha = context.Result.Sha.Substring(0, 7); - labelParts.Add($"c{shortSha}"); - } - } - - var label = string.Join("-", labelParts); - label = label.Replace("*", context.Result.HeightPadded); + new HeightRule(true), + ShortShaRule.Instance, + BranchNameRule.Instance, + ShortBranchNameRule.Instance, + BranchNameSuffixRule.Instance + }; + + var labelParts = context.Configuration.Label.ApplyRules(context, rules); + var label = string.Join("-", labelParts).ResolveRules(context, rules); var format = context.Result.Version; diff --git a/src/SimpleVersion.Core/Pipeline/Formatting/Semver2FormatProcess.cs b/src/SimpleVersion.Core/Pipeline/Formatting/Semver2FormatProcess.cs index dd3e315..ecd552c 100644 --- a/src/SimpleVersion.Core/Pipeline/Formatting/Semver2FormatProcess.cs +++ b/src/SimpleVersion.Core/Pipeline/Formatting/Semver2FormatProcess.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; +using SimpleVersion.Rules; namespace SimpleVersion.Pipeline.Formatting { @@ -7,39 +6,19 @@ public class Semver2FormatProcess : ICalculatorProcess { public void Apply(VersionContext context) { - var labelParts = new List(context.Configuration.Label); - var metaParts = new List(context.Configuration.MetaData); - - if (!context.Configuration.Version.Contains("*")) - { - // if we have a label, ensure height is included - if (labelParts.Count != 0 && !labelParts.Contains("*")) - labelParts.Add("*"); - } - - // add short sha if required - var addShortSha = true; - foreach (var pattern in context.Configuration.Branches.Release) - { - if (Regex.IsMatch(context.Result.CanonicalBranchName, pattern)) - { - addShortSha = false; - break; - } - } - - if (addShortSha) + var rules = new IRule[] { - var shortSha = context.Result.Sha.Substring(0, 7); - labelParts.Add($"c{shortSha}"); - } - - var label = string.Join(".", labelParts); - label = label.Replace("*", context.Result.Height.ToString()); - - var meta = string.Join(".", metaParts); - meta = meta.Replace("*", context.Result.Height.ToString()); - + HeightRule.Instance, + ShortShaRule.Instance, + BranchNameRule.Instance, + ShortBranchNameRule.Instance, + BranchNameSuffixRule.Instance + }; + + var labelParts = context.Configuration.Label.ApplyRules(context, rules); + var label = string.Join(".", labelParts).ResolveRules(context, rules); + var meta = string.Join(".", context.Configuration.MetaData).ResolveRules(context, rules); + var format = context.Result.Version; if (!string.IsNullOrWhiteSpace(label)) diff --git a/src/SimpleVersion.Core/Pipeline/Formatting/VersionFormatProcess.cs b/src/SimpleVersion.Core/Pipeline/Formatting/VersionFormatProcess.cs index 81f6300..ce1dd27 100644 --- a/src/SimpleVersion.Core/Pipeline/Formatting/VersionFormatProcess.cs +++ b/src/SimpleVersion.Core/Pipeline/Formatting/VersionFormatProcess.cs @@ -1,4 +1,5 @@ -using System; +using SimpleVersion.Rules; +using System; namespace SimpleVersion.Pipeline.Formatting { @@ -6,9 +7,7 @@ public class VersionFormatProcess : ICalculatorProcess { public void Apply(VersionContext context) { - var versionString = context.Configuration.Version; - if (versionString.Contains("*")) - versionString = versionString.Replace("*", context.Result.Height.ToString()); + var versionString = HeightRule.Instance.Resolve(context, context.Configuration.Version); if (Version.TryParse(versionString, out var version)) { diff --git a/src/SimpleVersion.Core/Pipeline/ResolveConfigurationProcess.cs b/src/SimpleVersion.Core/Pipeline/ResolveConfigurationProcess.cs index 214d47a..e48affb 100644 --- a/src/SimpleVersion.Core/Pipeline/ResolveConfigurationProcess.cs +++ b/src/SimpleVersion.Core/Pipeline/ResolveConfigurationProcess.cs @@ -5,12 +5,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; namespace SimpleVersion.Pipeline { public class ResolveConfigurationProcess : ICalculatorProcess { - private readonly ConfigurationVersionLabelComparer _comparer = new ConfigurationVersionLabelComparer(); + private static readonly ConfigurationVersionLabelComparer _comparer = new ConfigurationVersionLabelComparer(); public void Apply(VersionContext context) { @@ -21,8 +22,14 @@ public void Apply(VersionContext context) throw new ArgumentException($"{nameof(context.RepositoryPath)} must be a directory"); using(var repo = new Repository(context.RepositoryPath)){ - var config = GetConfiguration(repo.Head?.Tip) - ?? throw new InvalidOperationException($"No commits found for '{Constants.VersionFileName}'"); + if(string.IsNullOrWhiteSpace(context.Result.CanonicalBranchName)) + context.Result.CanonicalBranchName = repo.Head.CanonicalName; + + if(string.IsNullOrWhiteSpace(context.Result.BranchName)) + context.Result.BranchName = repo.Head.FriendlyName; + + var config = GetConfiguration(repo.Head?.Tip, context) + ?? throw new InvalidOperationException($"Could not read '{Constants.VersionFileName}', has it been committed?"); context.Configuration = config; @@ -48,25 +55,26 @@ private void PopulateResult(VersionContext context, Repository repo) // Perform a diff var diff = repo.Diff.Compare(next, tipTree); // If a change to the file is found, stop counting - if (HasVersionChange(diff, commits.Current, context.Configuration)) + if (HasVersionChange(diff, commits.Current, context)) break; // Increment height height++; } - context.Result.CanonicalBranchName = repo.Head.CanonicalName; - context.Result.BranchName = repo.Head.FriendlyName; context.Result.Sha = repo.Head.Tip.Sha; context.Result.Height = height; } - private bool HasVersionChange(TreeChanges diff, Commit commit, SVM.Configuration config) + private bool HasVersionChange( + TreeChanges diff, + Commit commit, + VersionContext context) { if (diff.Any(d => d.Path == Constants.VersionFileName)) { - var commitConfig = GetConfiguration(commit); - return !_comparer.Equals(config, commitConfig); + var commitConfig = GetConfiguration(commit, context); + return commitConfig != null && !_comparer.Equals(context.Configuration, commitConfig); } return false; @@ -84,15 +92,51 @@ private IEnumerable GetReachableCommits(Repository repo) return repo.Commits.QueryBy(filter).Reverse(); } - private SVM.Configuration GetConfiguration(Commit commit) + private SVM.Configuration GetConfiguration(Commit commit, VersionContext context) { var gitObj = commit?.Tree[Constants.VersionFileName]?.Target; if (gitObj == null) return null; - return Read((gitObj as Blob).GetContentText()); + var config = Read((gitObj as Blob).GetContentText()); + ApplyConfigOverrides(config, context); + return config; } - private SVM.Configuration Read(string rawConfiguration) => JsonConvert.DeserializeObject(rawConfiguration); + private void ApplyConfigOverrides(SVM.Configuration config, VersionContext context) + { + if (config == null) + return; + + var firstMatch = config.Branches + .Overrides.FirstOrDefault(x => Regex.IsMatch(context.Result.CanonicalBranchName, x.Match, RegexOptions.IgnoreCase)); + + if (firstMatch != null) + { + if (firstMatch.Label != null) + { + config.Label.Clear(); + config.Label.AddRange(firstMatch.Label); + } + + if(firstMatch.MetaData != null) + { + config.MetaData.Clear(); + config.MetaData.AddRange(firstMatch.MetaData); + } + } + } + private SVM.Configuration Read(string rawConfiguration) + { + try + { + return JsonConvert.DeserializeObject(rawConfiguration); + } + catch + { + //TODO handle logger of invalid parsing + return null; + } + } } } diff --git a/src/SimpleVersion.Core/Pipeline/VersionCalculator.cs b/src/SimpleVersion.Core/Pipeline/VersionCalculator.cs index f3ffbdb..b62fcfd 100644 --- a/src/SimpleVersion.Core/Pipeline/VersionCalculator.cs +++ b/src/SimpleVersion.Core/Pipeline/VersionCalculator.cs @@ -8,11 +8,11 @@ namespace SimpleVersion.Pipeline { public class VersionCalculator : IVersionCalculator { - public static IVersionCalculator Default() + public static IVersionCalculator Default() => new VersionCalculator() .AddProcessor() - .AddProcessor() .AddProcessor() + .AddProcessor() .AddProcessor() .AddProcessor() .AddProcessor(); @@ -31,7 +31,7 @@ public VersionResult GetResult(string path) foreach (var processor in _queue) processor.Value.Apply(ctx); - + return ctx.Result; } } diff --git a/src/SimpleVersion.Core/Rules/BaseBranchNameRule.cs b/src/SimpleVersion.Core/Rules/BaseBranchNameRule.cs new file mode 100644 index 0000000..c9f6414 --- /dev/null +++ b/src/SimpleVersion.Core/Rules/BaseBranchNameRule.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using SimpleVersion.Pipeline; + +namespace SimpleVersion.Rules +{ + public abstract class BaseBranchNameRule : IRule + { + protected static string _defaultPattern = "[^a-z0-9]"; + + + protected BaseBranchNameRule() : this(_defaultPattern) + { + } + + protected BaseBranchNameRule(string pattern) + { + Pattern = new Regex(pattern, RegexOptions.IgnoreCase); + } + + public abstract string Token { get; protected set; } + + public Regex Pattern { get; protected set; } + + public virtual IEnumerable Apply(VersionContext context, IEnumerable value) + { + // No default implementation applies branch name + return value; + } + + public virtual string Resolve(VersionContext context, string value) + { + if (Regex.IsMatch(value, Token, RegexOptions.IgnoreCase)) + { + var name = ResolveBranchName(context); + name = Pattern.Replace(name, ""); + return Regex.Replace(value, Regex.Escape(Token), name, RegexOptions.IgnoreCase); + } + + return value; + } + + protected abstract string ResolveBranchName(VersionContext context); + } +} diff --git a/src/SimpleVersion.Core/Rules/BranchNameRule.cs b/src/SimpleVersion.Core/Rules/BranchNameRule.cs new file mode 100644 index 0000000..65f2311 --- /dev/null +++ b/src/SimpleVersion.Core/Rules/BranchNameRule.cs @@ -0,0 +1,24 @@ +using System; +using SimpleVersion.Pipeline; + +namespace SimpleVersion.Rules +{ + public class BranchNameRule : BaseBranchNameRule + { + private static Lazy _default = new Lazy(() => new BranchNameRule()); + + public static BranchNameRule Instance => _default.Value; + + public BranchNameRule() : base() + { + } + + public BranchNameRule(string pattern) : base(pattern) + { + } + + public override string Token { get; protected set; } = "{branchname}"; + + protected override string ResolveBranchName(VersionContext context) => context.Result.CanonicalBranchName; + } +} diff --git a/src/SimpleVersion.Core/Rules/BranchNameSuffixRule.cs b/src/SimpleVersion.Core/Rules/BranchNameSuffixRule.cs new file mode 100644 index 0000000..a8b3f9a --- /dev/null +++ b/src/SimpleVersion.Core/Rules/BranchNameSuffixRule.cs @@ -0,0 +1,27 @@ +using SimpleVersion.Pipeline; +using System; + +namespace SimpleVersion.Rules +{ + public class BranchNameSuffixRule : BaseBranchNameRule + { + private static Lazy _default = new Lazy(() => new BranchNameSuffixRule()); + + public static BranchNameSuffixRule Instance => _default.Value; + + public BranchNameSuffixRule() : base() + { + } + + public BranchNameSuffixRule(string pattern) : base(pattern) + { + } + + public override string Token { get; protected set; } = "{branchnamesuffix}"; + + protected override string ResolveBranchName(VersionContext context) + { + return context.Result.CanonicalBranchName.Substring(context.Result.CanonicalBranchName.LastIndexOf('/') + 1); + } + } +} diff --git a/src/SimpleVersion.Core/Rules/HeightRule.cs b/src/SimpleVersion.Core/Rules/HeightRule.cs new file mode 100644 index 0000000..6b68bab --- /dev/null +++ b/src/SimpleVersion.Core/Rules/HeightRule.cs @@ -0,0 +1,48 @@ +using SimpleVersion.Pipeline; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SimpleVersion.Rules +{ + public class HeightRule : IRule + { + private static Lazy _default = new Lazy(() => new HeightRule()); + + public static HeightRule Instance => _default.Value; + + public HeightRule() :this(false) + { + + } + + public HeightRule(bool padded) + { + Padded = padded; + } + + public bool Padded { get; } + + public string Token => "*"; + + public string Resolve(VersionContext context, string value) + { + if (Padded) + return value.Replace(Token, context.Result.HeightPadded); + else + return value.Replace(Token, context.Result.Height.ToString()); + } + + public IEnumerable Apply(VersionContext context, IEnumerable input) + { + if (!context.Configuration.Version.Contains(Token) + && input.Count() != 0 + && !input.Contains(Token)) + { + return input.Concat(new[] { Token }); + } + + return input; + } + } +} diff --git a/src/SimpleVersion.Core/Rules/RuleExtensions.cs b/src/SimpleVersion.Core/Rules/RuleExtensions.cs new file mode 100644 index 0000000..e554cae --- /dev/null +++ b/src/SimpleVersion.Core/Rules/RuleExtensions.cs @@ -0,0 +1,26 @@ +using SimpleVersion.Pipeline; +using System.Collections.Generic; + +namespace SimpleVersion.Rules +{ + public static class RuleExtensions + { + public static IEnumerable ApplyRules(this IEnumerable value, VersionContext context, IEnumerable> rules) + { + var next = value; + foreach (var rule in rules) + next = rule.Apply(context, next); + + return next; + } + + public static T ResolveRules(this T value, VersionContext context, IEnumerable> rules) + { + var next = value; + foreach (var rule in rules) + next = rule.Resolve(context, next); + + return next; + } + } +} diff --git a/src/SimpleVersion.Core/Rules/ShortBranchNameRule.cs b/src/SimpleVersion.Core/Rules/ShortBranchNameRule.cs new file mode 100644 index 0000000..36e1b10 --- /dev/null +++ b/src/SimpleVersion.Core/Rules/ShortBranchNameRule.cs @@ -0,0 +1,24 @@ +using SimpleVersion.Pipeline; +using System; + +namespace SimpleVersion.Rules +{ + public class ShortBranchNameRule : BaseBranchNameRule + { + private static Lazy _default = new Lazy(() => new ShortBranchNameRule()); + + public static ShortBranchNameRule Instance => _default.Value; + + public ShortBranchNameRule() : base() + { + } + + public ShortBranchNameRule(string pattern) : base(pattern) + { + } + + public override string Token { get; protected set; } = "{shortbranchname}"; + + protected override string ResolveBranchName(VersionContext context) => context.Result.BranchName; + } +} diff --git a/src/SimpleVersion.Core/Rules/ShortShaRule.cs b/src/SimpleVersion.Core/Rules/ShortShaRule.cs new file mode 100644 index 0000000..1954dcc --- /dev/null +++ b/src/SimpleVersion.Core/Rules/ShortShaRule.cs @@ -0,0 +1,36 @@ +using SimpleVersion.Pipeline; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace SimpleVersion.Rules +{ + public class ShortShaRule : IRule + { + private static Lazy _default = new Lazy(() => new ShortShaRule()); + + public static ShortShaRule Instance => _default.Value; + + public string Token => "{shortsha}"; + + public string Resolve(VersionContext context, string value) + { + var shortSha = context.Result.Sha.Substring(0, 7); + return Regex.Replace(value, Regex.Escape(Token), $"c{shortSha}", RegexOptions.IgnoreCase); + } + + public IEnumerable Apply(VersionContext context, IEnumerable input) + { + var isRelease = context.Configuration.Branches.Release + .Any(x => Regex.IsMatch(context.Result.CanonicalBranchName, x)); + + if (!isRelease && !input.Contains(Token)) + { + return input.Concat(new[] { Token }); + } + + return input; + } + } +} diff --git a/test/SimpleVersion.Abstractions.Tests/Model/BranchConfigurationFixture.cs b/test/SimpleVersion.Abstractions.Tests/Model/BranchConfigurationFixture.cs new file mode 100644 index 0000000..d531030 --- /dev/null +++ b/test/SimpleVersion.Abstractions.Tests/Model/BranchConfigurationFixture.cs @@ -0,0 +1,21 @@ +using FluentAssertions; +using SimpleVersion.Model; +using Xunit; + +namespace SimpleVersion.Abstractions.Tests.Model +{ + public class BranchConfigurationFixture + { + [Fact] + public void Ctor_SetsDefaults() + { + // Arrange / Act + var sut = new BranchConfiguration(); + + // Assert + sut.Label.Should().BeNull(); + sut.Match.Should().BeEmpty(); + sut.MetaData.Should().BeNull(); + } + } +} diff --git a/test/SimpleVersion.Abstractions.Tests/Model/BranchInfoFixture.cs b/test/SimpleVersion.Abstractions.Tests/Model/BranchInfoFixture.cs index 3950033..ffda620 100644 --- a/test/SimpleVersion.Abstractions.Tests/Model/BranchInfoFixture.cs +++ b/test/SimpleVersion.Abstractions.Tests/Model/BranchInfoFixture.cs @@ -14,6 +14,7 @@ public void Ctor_PopulatesEmpty_Release() // Assert sut.Release.Should().BeEmpty(); + sut.Overrides.Should().BeEmpty(); } } } diff --git a/test/SimpleVersion.Core.Tests/Pipeline/Formatting/Semver1FormatProcessFixture.cs b/test/SimpleVersion.Core.Tests/Pipeline/Formatting/Semver1FormatProcessFixture.cs index ecd98c3..77a722e 100644 --- a/test/SimpleVersion.Core.Tests/Pipeline/Formatting/Semver1FormatProcessFixture.cs +++ b/test/SimpleVersion.Core.Tests/Pipeline/Formatting/Semver1FormatProcessFixture.cs @@ -41,14 +41,9 @@ public void Apply_LabelParts_NonRelease_Is_Formatted( }; context.Result.Version = context.Configuration.Version; - var fullExpected = expectedPart; - - if (parts.Length > 0) - { - var shaSub = context.Result.Sha.Substring(0, 7); - fullExpected = $"{expectedPart}-c{shaSub}"; - } - + var shaSub = context.Result.Sha.Substring(0, 7); + var fullExpected = $"{expectedPart}-c{shaSub}"; + // Act _sut.Apply(context); diff --git a/test/SimpleVersion.Core.Tests/Pipeline/ResolveConfigurationProcessFixture.cs b/test/SimpleVersion.Core.Tests/Pipeline/ResolveConfigurationProcessFixture.cs index 09c6b7c..d4829d4 100644 --- a/test/SimpleVersion.Core.Tests/Pipeline/ResolveConfigurationProcessFixture.cs +++ b/test/SimpleVersion.Core.Tests/Pipeline/ResolveConfigurationProcessFixture.cs @@ -3,6 +3,8 @@ using SimpleVersion.Model; using SimpleVersion.Pipeline; using System; +using System.Collections.Generic; +using System.IO; using Xunit; namespace SimpleVersion.Core.Tests.Pipeline @@ -56,7 +58,7 @@ public void Apply_NoCommits_ShouldThrow() // Assert action.Should().Throw() - .WithMessage($"No commits found for '{Constants.VersionFileName}'"); + .WithMessage($"Could not read '{Constants.VersionFileName}', has it been committed?"); } } @@ -77,7 +79,7 @@ public void Apply_CommitsForFile_ShouldThrow() // Assert action.Should().Throw() - .WithMessage($"No commits found for '{Constants.VersionFileName}'"); + .WithMessage($"Could not read '{Constants.VersionFileName}', has it been committed?"); } } @@ -181,7 +183,7 @@ public void Apply_Feature_Branch_No_Change_Increments_Merge_Once() // Act _sut.Apply(context); - + context.Result.Height.Should().Be(6); } } @@ -287,7 +289,7 @@ public void Apply_Feature_Branch_Sets_BranchName() fixture.MakeACommit(); // feature 1 fixture.MakeACommit(); // feature 2 fixture.MakeACommit(); // feature 3 - + // Act _sut.Apply(context); @@ -295,5 +297,135 @@ public void Apply_Feature_Branch_Sets_BranchName() context.Result.CanonicalBranchName.Should().Be("refs/heads/feature/other"); } } + + [Fact] + public void Apply_BranchOverride_AppliesOverride() + { + using (var fixture = new EmptyRepositoryFixture()) + { + // Arrange + var context = new VersionContext { RepositoryPath = fixture.RepositoryPath }; + + var expectedLabel = new List { "{branchName}" }; + var expectedMeta = new List { "meta" }; + + // write the version file + var config = new Configuration + { + Version = "0.1.0", + Branches = { + Overrides = { + new BranchConfiguration + { + Match = "feature/other", + Label = expectedLabel, + MetaData = expectedMeta + } + } + } + }; + Utils.WriteConfiguration(config, fixture); // 1 + + fixture.MakeACommit(); // 2 + fixture.MakeACommit(); // 3 + fixture.MakeACommit(); // 4 + fixture.MakeACommit(); // 5 + + fixture.BranchTo("feature/other"); + fixture.MakeACommit(); // feature 1 + fixture.MakeACommit(); // feature 2 + fixture.MakeACommit(); // feature 3 + + // Act + _sut.Apply(context); + + context.Configuration.Label.Should().BeEquivalentTo(expectedLabel, options => options.WithStrictOrdering()); + context.Configuration.MetaData.Should().BeEquivalentTo(expectedMeta, options => options.WithStrictOrdering()); + } + } + + [Fact] + public void Apply_Malformed_Json_At_Commit_Throws() + { + using (var fixture = new EmptyRepositoryFixture()) + { + // Arrange + var context = new VersionContext { RepositoryPath = fixture.RepositoryPath }; + + // write the version file (Well formaed) + var config = new Configuration { Version = "0.1.0" }; + Utils.WriteConfiguration(config, fixture); // 1 + + fixture.MakeACommit(); // 2 + fixture.MakeACommit(); // 3 + fixture.MakeACommit(); // 4 + + + // Write the version file (with parsing errors) + var file = Path.Combine(fixture.RepositoryPath, Constants.VersionFileName); + using (var writer = File.AppendText(file)) + { + writer.WriteLine("This will not parse"); + writer.Flush(); + } + + fixture.Repository.Index.Add(Constants.VersionFileName); + fixture.Repository.Index.Write(); + fixture.MakeACommit(); // 5 + fixture.MakeACommit(); // 6 + fixture.MakeACommit(); // 7 + fixture.MakeACommit(); // 8 + + // Act + Action action = () => _sut.Apply(context); + + // Assert + action.Should().Throw() + .WithMessage($"Could not read '{Constants.VersionFileName}', has it been committed?"); + } + } + + [Fact] + public void Apply_Malformed_Json_Committed_Counts_As_No_Change() + { + using (var fixture = new EmptyRepositoryFixture()) + { + // Arrange + var context = new VersionContext { RepositoryPath = fixture.RepositoryPath }; + + // write the version file (Well formaed) + var config = new Configuration { Version = "0.1.0" }; + Utils.WriteConfiguration(config, fixture); // 1 + + fixture.MakeACommit(); // 2 + fixture.MakeACommit(); // 3 + fixture.MakeACommit(); // 4 + + + // Write the version file (with parsing errors) + var file = Path.Combine(fixture.RepositoryPath, Constants.VersionFileName); + + using (var writer = File.AppendText(file)) + { + writer.WriteLine("This will not parse"); + writer.Flush(); + } + + fixture.Repository.Index.Add(Constants.VersionFileName); + fixture.Repository.Index.Write(); + fixture.MakeACommit(); // 5 + fixture.MakeACommit(); // 6 + fixture.MakeACommit(); // 7 + fixture.MakeACommit(); // 8 + + config = new Configuration { Version = "0.1.0" }; + Utils.WriteConfiguration(config, fixture); // 9 + + // Act + _sut.Apply(context); + + context.Result.Height.Should().Be(9); + } + } } } diff --git a/test/SimpleVersion.Core.Tests/Rules/BranchNameRuleFixture.cs b/test/SimpleVersion.Core.Tests/Rules/BranchNameRuleFixture.cs new file mode 100644 index 0000000..b3d30f0 --- /dev/null +++ b/test/SimpleVersion.Core.Tests/Rules/BranchNameRuleFixture.cs @@ -0,0 +1,89 @@ +using FluentAssertions; +using SimpleVersion.Pipeline; +using SimpleVersion.Rules; +using System; +using System.Collections.Generic; +using Xunit; + +namespace SimpleVersion.Core.Tests.Rules +{ + public class BranchNameRuleFixture + { + [Fact] + public void Instance_SetsDefaults() + { + // Arrange / Act + var sut = BranchNameRule.Instance; + + // Assert + sut.Pattern.Should().NotBeNull(); + sut.Token.Should().Be("{branchname}"); + } + + public static IEnumerable ApplyData() + { + yield return new object[] { null, null }; + yield return new object[] { null, Array.Empty() }; + yield return new object[] { new VersionContext(), new[] { "this" } }; + } + + [Theory] + [MemberData(nameof(ApplyData))] + public void Apply_Returns_Input(VersionContext context, IEnumerable input) + { + // Arrange + var sut = new BranchNameRule(); + + // Act + var result = sut.Apply(context, input); + + // Assert + result.Should().BeSameAs(input); + } + + [Theory] + [InlineData("refs/heads/master", "{branchName}", "refsheadsmaster")] + [InlineData("refs/heads/master", "{BRANCHNAME}", "refsheadsmaster")] + [InlineData("refs/heads/release/1.0", "{BRANCHNAME}", "refsheadsrelease10")] + [InlineData("refs/heads/release-1.0", "{BRANCHNAME}", "refsheadsrelease10")] + public void Resolve_Replaces_CanonicalBranchName(string branchName, string input, string expected) + { + // Arrange + var sut = new BranchNameRule(); + var context = new VersionContext + { + Result = + { + CanonicalBranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + + [Theory] + [InlineData("master", "{branchName}", "[mr]", "aste")] + public void Resolve_CustomPattern_Replaces_BranchName(string branchName, string input, string pattern, string expected) + { + // Arrange + var sut = new BranchNameRule(pattern); + var context = new VersionContext + { + Result = + { + CanonicalBranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + } +} diff --git a/test/SimpleVersion.Core.Tests/Rules/BranchNameSuffixRuleFixture.cs b/test/SimpleVersion.Core.Tests/Rules/BranchNameSuffixRuleFixture.cs new file mode 100644 index 0000000..23fde54 --- /dev/null +++ b/test/SimpleVersion.Core.Tests/Rules/BranchNameSuffixRuleFixture.cs @@ -0,0 +1,89 @@ +using FluentAssertions; +using SimpleVersion.Pipeline; +using SimpleVersion.Rules; +using System; +using System.Collections.Generic; +using Xunit; + +namespace SimpleVersion.Core.Tests.Rules +{ + public class BranchNameSuffixRuleFixture + { + [Fact] + public void Instance_SetsDefaults() + { + // Arrange / Act + var sut = BranchNameSuffixRule.Instance; + + // Assert + sut.Pattern.Should().NotBeNull(); + sut.Token.Should().Be("{branchnamesuffix}"); + } + + public static IEnumerable ApplyData() + { + yield return new object[] { null, null }; + yield return new object[] { null, Array.Empty() }; + yield return new object[] { new VersionContext(), new[] { "this" } }; + } + + [Theory] + [MemberData(nameof(ApplyData))] + public void Apply_Returns_Input(VersionContext context, IEnumerable input) + { + // Arrange + var sut = new BranchNameSuffixRule(); + + // Act + var result = sut.Apply(context, input); + + // Assert + result.Should().BeSameAs(input); + } + + [Theory] + [InlineData("refs/heads/master", "{branchNameSuffix}", "master")] + [InlineData("refs/heads/master", "{BRANCHNAMESUFFIX}", "master")] + [InlineData("refs/heads/release/1.0", "{BRANCHNAMESuffix}", "10")] + [InlineData("refs/heads/release-1.0", "{BRANCHNAMEsuffix}", "release10")] + public void Resolve_Replaces_CanonicalBranchName(string branchName, string input, string expected) + { + // Arrange + var sut = new BranchNameSuffixRule(); + var context = new VersionContext + { + Result = + { + CanonicalBranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + + [Theory] + [InlineData("master", "{branchNameSuffix}", "[mr]", "aste")] + public void Resolve_CustomPattern_Replaces_BranchName(string branchName, string input, string pattern, string expected) + { + // Arrange + var sut = new BranchNameSuffixRule(pattern); + var context = new VersionContext + { + Result = + { + CanonicalBranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + } +} diff --git a/test/SimpleVersion.Core.Tests/Rules/HeightRuleFixture.cs b/test/SimpleVersion.Core.Tests/Rules/HeightRuleFixture.cs new file mode 100644 index 0000000..9e219d6 --- /dev/null +++ b/test/SimpleVersion.Core.Tests/Rules/HeightRuleFixture.cs @@ -0,0 +1,118 @@ +using FluentAssertions; +using SimpleVersion.Pipeline; +using SimpleVersion.Rules; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace SimpleVersion.Core.Tests.Rules +{ + public class HeightRuleFixture + { + [Fact] + public void Instance_Padded_IsFalse() + { + // Arrange + var sut = HeightRule.Instance; + + // Act + var result = sut.Padded; + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public void Token_Is_Asterisk() + { + // Arrange / Act + var sut = new HeightRule(); + + // Assert + sut.Token.Should().Be("*"); + } + + [Fact] + public void Padded_ByDefault_IsFalse() + { + // Arrange / Act + var sut = new HeightRule(); + + // Assert + sut.Padded.Should().BeFalse(); + } + + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Padded_WhenSet_SetsCorrectly(bool usePadding) + { + // Arrange / Act + var sut = new HeightRule(usePadding); + + // Assert + sut.Padded.Should().Be(usePadding); + } + + [Theory] + [InlineData(true, 10, "myString-*", "myString-0010")] + [InlineData(false, 15, "myString-*", "myString-15")] + [InlineData(true, 10, "myString-*.*", "myString-0010.0010")] + [InlineData(false, 15, "myString-*-*", "myString-15-15")] + [InlineData(true, 10, "myString", "myString")] + [InlineData(false, 15, "myString", "myString")] + public void Resolve_ReplacesToken_IfNeeded(bool usePadding, int height, string input, string expected) + { + // Arrange + var sut = new HeightRule(usePadding); + var context = new VersionContext + { + Result = + { + Height = height + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + + public static IEnumerable ApplyData() + { + // Version in string, Do not add to label + yield return new object[] { "1.*", new[] { "this" }, new[] { "this" } }; + // No release label, Do not add to label + yield return new object[] { "1.0", Array.Empty(), Array.Empty() }; + // Not in version and release, append to end + yield return new object[] { "1.0", new[] { "this" }, new[] { "this", "*" } }; + } + + + [Theory] + [MemberData(nameof(ApplyData))] + public void Apply_Appends_IfRequired(string version, IEnumerable input, IEnumerable expected) + { + // Arrange + var sut = new HeightRule(); + var context = new VersionContext + { + Configuration = + { + Version = version + } + }; + + // Act + var result = sut.Apply(context, input); + + // Assert + result.Should().BeEquivalentTo(expected, options => options.WithStrictOrdering()); + } + + } +} diff --git a/test/SimpleVersion.Core.Tests/Rules/ShortBranchNameRuleFixture.cs b/test/SimpleVersion.Core.Tests/Rules/ShortBranchNameRuleFixture.cs new file mode 100644 index 0000000..4081afa --- /dev/null +++ b/test/SimpleVersion.Core.Tests/Rules/ShortBranchNameRuleFixture.cs @@ -0,0 +1,90 @@ +using FluentAssertions; +using SimpleVersion.Pipeline; +using SimpleVersion.Rules; +using System; +using System.Collections.Generic; +using Xunit; + +namespace SimpleVersion.Core.Tests.Rules +{ + public class ShortBranchNameRuleFixture + { + [Fact] + public void Instance_SetsDefaults() + { + // Arrange / Act + var sut = ShortBranchNameRule.Instance; + + // Assert + sut.Pattern.Should().NotBeNull(); + sut.Token.Should().Be("{shortbranchname}"); + } + + public static IEnumerable ApplyData() + { + yield return new object[] { null, null }; + yield return new object[] { null, Array.Empty() }; + yield return new object[] { new VersionContext(), new[] { "this" } }; + } + + [Theory] + [MemberData(nameof(ApplyData))] + public void Apply_Returns_Input(VersionContext context, IEnumerable input) + { + // Arrange + var sut = new ShortBranchNameRule(); + + // Act + var result = sut.Apply(context, input); + + // Assert + result.Should().BeSameAs(input); + } + + [Theory] + [InlineData("master", "{shortBranchName}", "master")] + [InlineData("master", "{SHORTBRANCHNAME}", "master")] + [InlineData("release/1.0", "{shortBRANCHNAME}", "release10")] + [InlineData("release-1.0", "{shortBRANCHNAME}", "release10")] + public void Resolve_Replaces_BranchName(string branchName, string input, string expected) + { + // Arrange + var sut = new ShortBranchNameRule(); + var context = new VersionContext + { + Result = + { + BranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + + + [Theory] + [InlineData("master", "{shortbranchName}", "[mr]", "aste")] + public void Resolve_CustomPattern_Replaces_BranchName(string branchName, string input, string pattern, string expected) + { + // Arrange + var sut = new ShortBranchNameRule(pattern); + var context = new VersionContext + { + Result = + { + BranchName = branchName + } + }; + + // Act + var result = sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + } +} diff --git a/test/SimpleVersion.Core.Tests/Rules/ShortShaRuleFixture.cs b/test/SimpleVersion.Core.Tests/Rules/ShortShaRuleFixture.cs new file mode 100644 index 0000000..27f72b8 --- /dev/null +++ b/test/SimpleVersion.Core.Tests/Rules/ShortShaRuleFixture.cs @@ -0,0 +1,76 @@ +using FluentAssertions; +using SimpleVersion.Pipeline; +using SimpleVersion.Rules; +using System; +using System.Collections.Generic; +using Xunit; + +namespace SimpleVersion.Core.Tests.Rules +{ + public class ShortShaRuleFixture + { + private ShortShaRule _sut; + + public ShortShaRuleFixture() + { + _sut = new ShortShaRule(); + } + + [Fact] + public void Token_Is_Correct() + { + // Act / Assert + _sut.Token.Should().Be("{shortsha}"); + } + + [Theory] + [InlineData("{shortSha}", "c4ca82d2")] + [InlineData("{SHORTSHA}", "c4ca82d2")] + [InlineData("this-{shortsha}", "this-c4ca82d2")] + [InlineData("this", "this")] + public void Resolve_ReplacesToken_IfNeeded(string input, string expected) + { + // Arrange + var context = new VersionContext + { + Result = Utils.GetVersionResult(10) + }; + + // Act + var result = _sut.Resolve(context, input); + + // Assert + result.Should().Be(expected); + } + + public static IEnumerable ApplyData() + { + // release branch does not include sha + yield return new object[] { true, new[] { "this" }, new[] { "this" } }; + // non-release sha appends + yield return new object[] { false, new[] { "this" }, new[] { "this", "{shortsha}" } }; + // non-release sha does not append if already there + yield return new object[] { false, new[] { "{shortsha}", "this" }, new[] { "{shortsha}", "this" } }; + // empty array appends + yield return new object[] { false, Array.Empty(), new[] { "{shortsha}" } }; + } + + [Theory] + [MemberData(nameof(ApplyData))] + public void Apply_AppendsToken_IfNeeded(bool isRelease, IEnumerable input, IEnumerable expected) + { + // Arrange + var context = new VersionContext + { + Configuration = Utils.GetConfiguration("1.2.3"), + Result = Utils.GetVersionResult(10, isRelease) + }; + + // Act + var result = _sut.Apply(context, input); + + // Assert + result.Should().BeEquivalentTo(expected, options => options.WithStrictOrdering()); + } + } +}