From 9bf451baff5925535635cdbdbb27c15f2537f346 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 10 Jan 2025 18:11:38 +0100 Subject: [PATCH] Add possibility to pass handler to build breaking aliases --- src/Cake.Issues.Tests/BuildBreakerTests.cs | 269 ++++++++++++++++++--- src/Cake.Issues/Aliases.BuildBreaking.cs | 198 ++++++++++++++- src/Cake.Issues/BuildBreaker.cs | 42 +++- 3 files changed, 464 insertions(+), 45 deletions(-) diff --git a/src/Cake.Issues.Tests/BuildBreakerTests.cs b/src/Cake.Issues.Tests/BuildBreakerTests.cs index edfb21a0b..2b810fd3a 100644 --- a/src/Cake.Issues.Tests/BuildBreakerTests.cs +++ b/src/Cake.Issues.Tests/BuildBreakerTests.cs @@ -13,7 +13,7 @@ public void Should_Throw_If_Issues_Is_Null() IEnumerable issues = null; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, null)); // Then result.IsArgumentNullException("issues"); @@ -29,7 +29,7 @@ public void Should_Throw_If_Any_Issues() .Create()]; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -42,10 +42,44 @@ public void Should_Not_Throw_If_No_Issues() var issues = new List(); // When - BuildBreaker.BreakBuildOnIssues(issues); + BuildBreaker.BreakBuildOnIssues(issues, null); // Then } + + [Fact] + public void Should_Call_Handler_If_Any_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create()]; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, handler)); + + // Then + issuesPassedToHandler.ShouldNotBeNull().ShouldContain(issues.Single()); + result.IsIssuesFoundException("Found 1 issue."); + } + + [Fact] + public void Should_Not_Call_Handler_If_No_Issues() + { + // Given + var issues = new List(); + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + BuildBreaker.BreakBuildOnIssues(issues, handler); + + // Then + issuesPassedToHandler.ShouldBeNull(); + } } public sealed class TheBreakBuildOnIssuesMethodWithPriorityParameter @@ -58,7 +92,7 @@ public void Should_Throw_If_Issues_Is_Null() var priority = IssuePriority.Error; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, priority)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, priority, null)); // Then result.IsArgumentNullException("issues"); @@ -76,7 +110,7 @@ public void Should_Throw_If_Matching_Issues() var priority = IssuePriority.Warning; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, priority)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, priority, null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -94,10 +128,51 @@ public void Should_Not_Throw_If_No_Matching_Issues() var priority = IssuePriority.Error; // When - BuildBreaker.BreakBuildOnIssues(issues, priority); + BuildBreaker.BreakBuildOnIssues(issues, priority, null); // Then } + + [Fact] + public void Should_Call_Handler_If_Any_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .WithPriority(IssuePriority.Warning) + .Create()]; + var priority = IssuePriority.Warning; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, priority, handler)); + + // Then + issuesPassedToHandler.ShouldNotBeNull().ShouldContain(issues.Single()); + result.IsIssuesFoundException("Found 1 issue."); + } + + [Fact] + public void Should_Not_Call_Handler_If_No_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .WithPriority(IssuePriority.Warning) + .Create()]; + var priority = IssuePriority.Error; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + BuildBreaker.BreakBuildOnIssues(issues, priority, handler); + + // Then + issuesPassedToHandler.ShouldBeNull(); + } } public sealed class TheBreakBuildOnIssuesMethodWithProviderTypeParameter @@ -110,7 +185,7 @@ public void Should_Throw_If_Issues_Is_Null() var providerType = "ProviderType Foo"; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, null)); // Then result.IsArgumentNullException("issues"); @@ -127,7 +202,7 @@ public void Should_Throw_If_ProviderType_Is_Null() string providerType = null; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, null)); // Then result.IsArgumentNullException("providerType"); @@ -144,7 +219,7 @@ public void Should_Throw_If_ProviderType_Is_Empty() var providerType = string.Empty; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, null)); // Then result.IsArgumentOutOfRangeException("providerType"); @@ -161,7 +236,7 @@ public void Should_Throw_If_ProviderType_Is_Whitespace() var providerType = " "; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, null)); // Then result.IsArgumentOutOfRangeException("providerType"); @@ -178,7 +253,7 @@ public void Should_Throw_If_Matching_Issues() var providerType = "ProviderType Foo"; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -196,9 +271,49 @@ public void Should_Not_Throw_If_No_Matching_Issues() var providerType = "ProviderType Bar"; // When - BuildBreaker.BreakBuildOnIssues(issues, providerType); + BuildBreaker.BreakBuildOnIssues(issues, providerType, null); + + // Then + } + + [Fact] + public void Should_Call_Handler_If_Any_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create()]; + var providerType = "ProviderType Foo"; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, providerType, handler)); + + // Then + issuesPassedToHandler.ShouldNotBeNull().ShouldContain(issues.Single()); + result.IsIssuesFoundException("Found 1 issue."); + } + + [Fact] + public void Should_Not_Call_Handler_If_No_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .WithPriority(IssuePriority.Warning) + .Create()]; + var providerType = "ProviderType Bar"; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + BuildBreaker.BreakBuildOnIssues(issues, providerType, handler); // Then + issuesPassedToHandler.ShouldBeNull(); } } @@ -218,7 +333,8 @@ public void Should_Throw_If_Issues_Is_Null() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsArgumentNullException("issues"); @@ -241,7 +357,8 @@ public void Should_Throw_If_IssueProvidersToConsider_Is_Null() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsArgumentNullException("issueProvidersToConsider"); @@ -265,7 +382,8 @@ public void Should_Throw_If_IssueProvidersToIgnore_Is_Null() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsArgumentNullException("issueProvidersToIgnore"); @@ -288,7 +406,8 @@ public void Should_Throw_If_Any_Issue_And_No_Filter() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -316,7 +435,8 @@ public void Should_Throw_If_Issue_Of_Relevant_Priority(IssuePriority priority) issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -341,7 +461,8 @@ public void Should_Not_Throw_If_No_Issue_Of_Relevant_Priority(IssuePriority prio issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore); + issueProvidersToIgnore, + null); // Then } @@ -365,7 +486,8 @@ public void Should_Throw_If_IssueProvider_To_Consider(string value) issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -390,7 +512,8 @@ public void Should_Not_Throw_If_No_Issue_Is_Not_In_List_Of_Issue_Providers_To_Co issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore); + issueProvidersToIgnore, + null); // Then } @@ -413,7 +536,8 @@ public void Should_Throw_If_IssueProvider_Is_Not_Ignored(string value) issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore)); + issueProvidersToIgnore, + null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -439,7 +563,8 @@ public void Should_Not_Throw_If_IssueProvider_Is_Ignored(string value) issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore); + issueProvidersToIgnore, + null); // Then } @@ -461,7 +586,8 @@ public void Should_Not_Throw_If_IssueProvider_Is_To_Consider_And_Ignored() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore); + issueProvidersToIgnore, + null); // Then } @@ -484,9 +610,57 @@ public void Should_Not_Throw_If_Priority_Matches_But_IssueProvider_Is_Ignored() issues, minimumPriority, issueProvidersToConsider, - issueProvidersToIgnore); + issueProvidersToIgnore, + null); + + // Then + } + + [Fact] + public void Should_Call_Handler_If_Any_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create()]; + var minimumPriority = IssuePriority.Undefined; + IList issueProvidersToConsider = []; + IList issueProvidersToIgnore = []; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues( + issues, + minimumPriority, + issueProvidersToConsider, + issueProvidersToIgnore, + handler)); // Then + issuesPassedToHandler.ShouldNotBeNull().ShouldContain(issues.Single()); + result.IsIssuesFoundException("Found 1 issue."); + } + + [Fact] + public void Should_Not_Call_Handler_If_No_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .WithPriority(IssuePriority.Warning) + .Create()]; + var providerType = "ProviderType Bar"; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + BuildBreaker.BreakBuildOnIssues(issues, providerType, handler); + + // Then + issuesPassedToHandler.ShouldBeNull(); } } @@ -500,7 +674,7 @@ public void Should_Throw_If_Issues_Is_Null() static bool predicate(IIssue x) => true; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate, null)); // Then result.IsArgumentNullException("issues"); @@ -517,7 +691,7 @@ public void Should_Throw_If_Predicate_Is_Null() Func predicate = null; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate, null)); // Then result.IsArgumentNullException("predicate"); @@ -534,7 +708,7 @@ public void Should_Throw_If_Matching_Issues() static bool predicate(IIssue x) => x.MessageText == "Message Foo"; // When - var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate)); + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate, null)); // Then result.IsIssuesFoundException("Found 1 issue."); @@ -551,9 +725,48 @@ public void Should_Not_Throw_If_No_Matching_Issues() static bool predicate(IIssue x) => false; // When - BuildBreaker.BreakBuildOnIssues(issues, predicate); + BuildBreaker.BreakBuildOnIssues(issues, predicate, null); + + // Then + } + + [Fact] + public void Should_Call_Handler_If_Any_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create()]; + static bool predicate(IIssue x) => x.MessageText == "Message Foo"; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + var result = Record.Exception(() => BuildBreaker.BreakBuildOnIssues(issues, predicate, handler)); + + // Then + issuesPassedToHandler.ShouldNotBeNull().ShouldContain(issues.Single()); + result.IsIssuesFoundException("Found 1 issue."); + } + + [Fact] + public void Should_Not_Call_Handler_If_No_Issues() + { + // Given + IEnumerable issues = [ + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create()]; + static bool predicate(IIssue x) => false; + IEnumerable issuesPassedToHandler = null; + void handler(IEnumerable x) => issuesPassedToHandler = x; + + // When + BuildBreaker.BreakBuildOnIssues(issues, predicate, handler); // Then + issuesPassedToHandler.ShouldBeNull(); } } } diff --git a/src/Cake.Issues/Aliases.BuildBreaking.cs b/src/Cake.Issues/Aliases.BuildBreaking.cs index d4cc3f6c1..cb2b77457 100644 --- a/src/Cake.Issues/Aliases.BuildBreaking.cs +++ b/src/Cake.Issues/Aliases.BuildBreaking.cs @@ -32,7 +32,36 @@ public static void BreakBuildOnIssues( context.NotNull(); issues.NotNull(); - BuildBreaker.BreakBuildOnIssues(issues); + BuildBreaker.BreakBuildOnIssues(issues, null); + } + + /// + /// Fails build if any issues are found. + /// + /// The context. + /// Issues which should be checked. + /// Optional handler to call if contains items. + /// + /// Fails build if issues are found: + /// + /// Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory(IssuesAliasConstants.BuildBreakingCakeAliasCategory)] + public static void BreakBuildOnIssues( + this ICakeContext context, + IEnumerable issues, + Action> handler) + { + context.NotNull(); + issues.NotNull(); + + BuildBreaker.BreakBuildOnIssues(issues, handler); } /// @@ -59,7 +88,39 @@ public static void BreakBuildOnIssues( context.NotNull(); issues.NotNull(); - BuildBreaker.BreakBuildOnIssues(issues, priority); + BuildBreaker.BreakBuildOnIssues(issues, priority, null); + } + + /// + /// Fails build if any issues of certain minimum priority are found. + /// + /// The context. + /// Issues which should be checked. + /// Minimum priority of issues which should be considered. + /// Optional handler to call when issues of are found. + /// + /// Fails build if errors are found: + /// + /// Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory(IssuesAliasConstants.BuildBreakingCakeAliasCategory)] + public static void BreakBuildOnIssues( + this ICakeContext context, + IEnumerable issues, + IssuePriority priority, + Action> handler) + { + context.NotNull(); + issues.NotNull(); + + BuildBreaker.BreakBuildOnIssues(issues, priority, handler); } /// @@ -86,7 +147,39 @@ public static void BreakBuildOnIssues( context.NotNull(); issues.NotNull(); - BuildBreaker.BreakBuildOnIssues(issues, providerType); + BuildBreaker.BreakBuildOnIssues(issues, providerType, null); + } + + /// + /// Fails build if any issues from a specific issue provider are found. + /// + /// The context. + /// Issues which should be checked. + /// Type of the issue provider. + /// Optional handler to call when issues from are found. + /// + /// Fails build if issues from MsBuild are found: + /// + /// Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory(IssuesAliasConstants.BuildBreakingCakeAliasCategory)] + public static void BreakBuildOnIssues( + this ICakeContext context, + IEnumerable issues, + string providerType, + Action> handler) + { + context.NotNull(); + issues.NotNull(); + + BuildBreaker.BreakBuildOnIssues(issues, providerType, handler); } /// @@ -138,7 +231,65 @@ public static void BreakBuildOnIssues( issues, settings.MinimumPriority, settings.IssueProvidersToConsider, - settings.IssueProvidersToIgnore); + settings.IssueProvidersToIgnore, + null); + } + + /// + /// Fails build if any issues are found with settings to limit to priority and issue provider types. + /// + /// The context. + /// Issues which should be checked. + /// Settings to apply. + /// Optional handler to call when issues matching parameters are found. + /// + /// Fails build if issues with severity warning or higher from MsBuild are found: + /// + /// Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + /// + /// Fails build if issues with severity warning or higher are found, ignoring MsBuild issues: + /// + /// Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory(IssuesAliasConstants.BuildBreakingCakeAliasCategory)] + public static void BreakBuildOnIssues( + this ICakeContext context, + IEnumerable issues, + BuildBreakingSettings settings, + Action> handler) + { + context.NotNull(); + issues.NotNull(); + settings.NotNull(); + + BuildBreaker.BreakBuildOnIssues( + issues, + settings.MinimumPriority, + settings.IssueProvidersToConsider, + settings.IssueProvidersToIgnore, + handler); } /// @@ -151,7 +302,9 @@ public static void BreakBuildOnIssues( /// Fails build if errors are found: /// /// x.Priority >= (int)IssuePriority.Error); /// ]]> /// /// @@ -166,6 +319,39 @@ public static void BreakBuildOnIssues( issues.NotNull(); predicate.NotNull(); - BuildBreaker.BreakBuildOnIssues(issues, predicate); + BuildBreaker.BreakBuildOnIssues(issues, predicate, null); + } + + /// + /// Fails build if any issues are found matching a specific predicate. + /// + /// The context. + /// Issues which should be checked. + /// Predicate to . + /// Optional handler to call when issues matching are found. + /// + /// Fails build if errors are found: + /// + /// x.Priority >= (int)IssuePriority.Error, + /// x => Information("{0} issues found", x.Count())); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory(IssuesAliasConstants.BuildBreakingCakeAliasCategory)] + public static void BreakBuildOnIssues( + this ICakeContext context, + IEnumerable issues, + Func predicate, + Action> handler) + { + context.NotNull(); + issues.NotNull(); + predicate.NotNull(); + + BuildBreaker.BreakBuildOnIssues(issues, predicate, handler); } } diff --git a/src/Cake.Issues/BuildBreaker.cs b/src/Cake.Issues/BuildBreaker.cs index a8236d408..408ba4b05 100644 --- a/src/Cake.Issues/BuildBreaker.cs +++ b/src/Cake.Issues/BuildBreaker.cs @@ -13,13 +13,14 @@ internal static class BuildBreaker /// Fails build if any issues are found. /// /// Issues which should be checked. - public static void BreakBuildOnIssues(IEnumerable issues) + /// Optional handler to call if contains items. + public static void BreakBuildOnIssues(IEnumerable issues, Action> handler) { issues.NotNull(); if (issues.Any()) { - BreakBuild(issues); + BreakBuild(issues, handler); } } @@ -28,11 +29,15 @@ public static void BreakBuildOnIssues(IEnumerable issues) /// /// Issues which should be checked. /// Minimum priority of issues which should be considered. - public static void BreakBuildOnIssues(IEnumerable issues, IssuePriority priority) + /// Optional handler to call when issues of are found. + public static void BreakBuildOnIssues( + IEnumerable issues, + IssuePriority priority, + Action> handler) { issues.NotNull(); - BreakBuildOnIssues(issues, x => x.Priority >= (int)priority); + BreakBuildOnIssues(issues, x => x.Priority >= (int)priority, handler); } /// @@ -40,12 +45,16 @@ public static void BreakBuildOnIssues(IEnumerable issues, IssuePriority /// /// Issues which should be checked. /// Type of the issue provider. - public static void BreakBuildOnIssues(IEnumerable issues, string providerType) + /// Optional handler to call when issues from are found. + public static void BreakBuildOnIssues( + IEnumerable issues, + string providerType, + Action> handler) { issues.NotNull(); providerType.NotNullOrWhiteSpace(); - BreakBuildOnIssues(issues, x => x.ProviderType == providerType); + BreakBuildOnIssues(issues, x => x.ProviderType == providerType, handler); } /// @@ -57,11 +66,13 @@ public static void BreakBuildOnIssues(IEnumerable issues, string provide /// Issue providers to consider. /// If empty, all providers are considered. /// Issue providers to ignore. + /// Optional handler to call when issues matching parameters are found. public static void BreakBuildOnIssues( IEnumerable issues, IssuePriority minimumPriority, IEnumerable issueProvidersToConsider, - IEnumerable issueProvidersToIgnore) + IEnumerable issueProvidersToIgnore, + Action> handler) { issues.NotNull(); issueProvidersToConsider.NotNull(); @@ -75,7 +86,8 @@ public static void BreakBuildOnIssues( (x.Priority == null || x.Priority >= (int)minimumPriority) && (!issueProvidersToConsider.Any() || issueProvidersToConsider.Contains(x.ProviderType)) && !issueProvidersToIgnore.Contains(x.ProviderType); - }); + }, + handler); } /// @@ -83,17 +95,25 @@ public static void BreakBuildOnIssues( /// /// Issues which should be checked. /// Predicate to . - public static void BreakBuildOnIssues(IEnumerable issues, Func predicate) + /// Optional handler to call when issues matching are found. + public static void BreakBuildOnIssues( + IEnumerable issues, + Func predicate, + Action> handler) { issues.NotNull(); predicate.NotNull(); if (issues.Any(predicate)) { - BreakBuild(issues); + BreakBuild(issues, handler); } } - private static void BreakBuild(IEnumerable issues) => + private static void BreakBuild(IEnumerable issues, Action> handler) + { + handler?.Invoke(issues); + throw new IssuesFoundException(issues.Count()); + } }