Skip to content

Commit e79ecae

Browse files
authored
Merge pull request #2327 from thomhurst/fix/ignored-failure-status
Fix ignored module failures incorrectly failing the pipeline
2 parents c3ba2a5 + 49758e0 commit e79ecae

File tree

8 files changed

+35
-11
lines changed

8 files changed

+35
-11
lines changed

src/ModularPipelines/Engine/Execution/ModuleRunner.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private async Task ExecuteCore(
113113

114114
await ExecuteModuleWithPipeline(moduleState, scope.ServiceProvider, cancellationToken).ConfigureAwait(false);
115115

116-
scheduler.MarkModuleCompleted(moduleType, true);
116+
scheduler.MarkModuleCompleted(moduleType, true, statusOverride: moduleState.Result?.ModuleStatus);
117117
}
118118
catch (Exception ex)
119119
{
@@ -157,11 +157,16 @@ private async Task ExecuteModuleWithPipeline(ModuleState moduleState, IServicePr
157157
{
158158
await ExecuteModuleLifecycle(moduleState, scopedServiceProvider, pipelineContext, executionContext, moduleContext, cancellationToken).ConfigureAwait(false);
159159

160-
// Record success or skip status on the Activity
160+
// Record success, skip, or ignored failure status on the Activity
161161
if (executionContext.Status == Enums.Status.Skipped)
162162
{
163163
ModuleActivityTracing.RecordSkipped(activity);
164164
}
165+
else if (executionContext.Status == Enums.Status.IgnoredFailure)
166+
{
167+
activity?.SetTag("module.status", "IgnoredFailure");
168+
activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Ok, "Module failed but failure was ignored");
169+
}
165170
else
166171
{
167172
ModuleActivityTracing.RecordSuccess(activity);

src/ModularPipelines/Engine/IModuleScheduler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Threading.Channels;
2+
using ModularPipelines.Enums;
23
using ModularPipelines.Modules;
34

45
namespace ModularPipelines.Engine;
@@ -33,7 +34,7 @@ internal interface IModuleScheduler : IDisposable
3334
/// <summary>
3435
/// Marks a module as completed and notifies dependents.
3536
/// </summary>
36-
void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null);
37+
void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null, Status? statusOverride = null);
3738

3839
/// <summary>
3940
/// Gets the completion task for a specific module.

src/ModularPipelines/Engine/IModuleStateTracker.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using ModularPipelines.Enums;
12
using ModularPipelines.Modules;
23

34
namespace ModularPipelines.Engine;
@@ -26,9 +27,10 @@ internal interface IModuleStateTracker
2627
/// Marks a module as completed and notifies dependents.
2728
/// </summary>
2829
/// <param name="moduleType">The type of the module to mark as completed.</param>
29-
/// <param name="success">True if execution succeeded, false otherwise.</param>
30+
/// <param name="success">True if execution succeeded (or failure was ignored), false otherwise.</param>
3031
/// <param name="exception">Optional exception if execution failed.</param>
31-
void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null);
32+
/// <param name="statusOverride">Optional status override. When provided, this status is used instead of computing from the success parameter.</param>
33+
void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null, Status? statusOverride = null);
3234

3335
/// <summary>
3436
/// Cancels all modules that are queued or pending (not yet executing).

src/ModularPipelines/Engine/MetricsCollector.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void RecordModuleCompleted(Type moduleType, DateTimeOffset time, bool suc
4444
{
4545
var data = _moduleMetrics.GetOrAdd(moduleType, _ => new ModuleMetricsData { ModuleType = moduleType });
4646
data.EndTime = time;
47-
data.WasSuccessful = success;
47+
data.WasSuccessful = status is Status.Successful or Status.UsedHistory;
4848
data.WasSkipped = skipped;
4949
data.Status = status;
5050
}
@@ -76,6 +76,10 @@ public PipelineMetrics ComputeMetrics(DateTimeOffset pipelineStart, DateTimeOffs
7676
{
7777
skippedCount++;
7878
}
79+
else if (data.Status == Status.IgnoredFailure)
80+
{
81+
// Ignored failures don't count as passed or failed
82+
}
7983
else if (data.WasSuccessful)
8084
{
8185
successfulCount++;

src/ModularPipelines/Engine/ModuleScheduler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,9 +427,9 @@ public bool MarkModuleStarted(Type moduleType)
427427
/// <summary>
428428
/// Marks a module as completed and notifies dependents.
429429
/// </summary>
430-
public void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null)
430+
public void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null, Status? statusOverride = null)
431431
{
432-
_stateTracker.MarkModuleCompleted(moduleType, success, exception);
432+
_stateTracker.MarkModuleCompleted(moduleType, success, exception, statusOverride);
433433
}
434434

435435
/// <summary>

src/ModularPipelines/Engine/ModuleStateTracker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public bool MarkModuleStarted(Type moduleType)
143143
}
144144

145145
/// <inheritdoc />
146-
public void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null)
146+
public void MarkModuleCompleted(Type moduleType, bool success, Exception? exception = null, Enums.Status? statusOverride = null)
147147
{
148148
if (!_moduleStates.TryGetValue(moduleType, out var state))
149149
{
@@ -171,7 +171,7 @@ public void MarkModuleCompleted(Type moduleType, bool success, Exception? except
171171
// Capture data for metrics recording outside lock
172172
completionTime = state.CompletionTime.Value;
173173
wasSkipped = state.SkipResult != null && state.SkipResult != Models.SkipDecision.DoNotSkip;
174-
status = wasSkipped ? Enums.Status.Skipped : (success ? Enums.Status.Successful : Enums.Status.Failed);
174+
status = statusOverride ?? (wasSkipped ? Enums.Status.Skipped : (success ? Enums.Status.Successful : Enums.Status.Failed));
175175

176176
// Capture counts for logging outside lock
177177
queuedCount = _queuedModules.Count;

src/ModularPipelines/Helpers/SpectreResultsPrinter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ private static void PrintHeader(PipelineSummary pipelineSummary)
7373
return timeline?.WasSkipped == true;
7474
});
7575

76+
var ignoredCount = pipelineSummary.Modules.Count(m =>
77+
{
78+
var timeline = pipelineSummary.ModuleTimelines?.FirstOrDefault(t => t.ModuleName == m.GetType().Name);
79+
return timeline?.Status == ModuleStatus.IgnoredFailure;
80+
});
81+
7682
var totalCount = metrics?.TotalModules ?? pipelineSummary.Modules.Count;
7783

7884
// Build the summary line
@@ -88,6 +94,11 @@ private static void PrintHeader(PipelineSummary pipelineSummary)
8894
parts.Add($"[red]{failedCount} failed[/]");
8995
}
9096

97+
if (ignoredCount > 0)
98+
{
99+
parts.Add($"[yellow]{ignoredCount} ignored[/]");
100+
}
101+
91102
if (skippedCount > 0)
92103
{
93104
parts.Add($"[yellow]{skippedCount} skipped[/]");

src/ModularPipelines/Models/PipelineSummary.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ private Status GetStatus()
169169
var result = _resultRegistry.GetResult(module.GetType());
170170
if (result != null)
171171
{
172-
if (result.ModuleResultType == ModuleResultType.Failure)
172+
if (result.ModuleResultType == ModuleResultType.Failure
173+
&& result.ModuleStatus != Status.IgnoredFailure)
173174
{
174175
return Status.Failed;
175176
}

0 commit comments

Comments
 (0)