Skip to main content

Skipping Modules

Using ModuleConfiguration​

The recommended way to configure module skipping is through the Configure(ModuleConfigurationBuilder) method with the fluent builder API:

Attribute conditions ([SkipIf<T1, ..., T4>], [RunIf<T1, ..., T4>], and [RunIfAny<T1, ..., T4>]) and the equivalent .WithRunIf<T>() and .WithSkipIf<T>() builder methods remain supported; see Run conditions. Attribute and fluent conditions run in the same execution pipeline after dependency waiting, so both invoke skipped hooks and lifecycle notifications.

PlanAsync() and --dry-run also evaluate these conditions before execution. Conditions must be side-effect-free and must not rely on being evaluated exactly once. If a fluent condition reads an awaited module result, that result does not exist during planning; the plan reports the module's skip decision as unknown and continues without executing modules.

Simple Condition​

public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(
_ => Environment.GetEnvironmentVariable("SKIP_MODULE") == "true",
"SKIP_MODULE is true");

protected override async Task<CommandResult> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
// Module logic here
}
}

Using Pipeline Context​

When you need access to the pipeline context for your skip condition:

public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(
async (ctx, _) => (await ctx.Tools.Git.Information.GetInfoAsync())?.BranchName != "main",
"This should only run on the main branch");

protected override async Task<CommandResult> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
// This only runs on the main branch
}
}

With Skip Reason​

The reason is optional. When it is omitted, a skipped module reports Skip condition was met:

protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(_ => Environment.GetEnvironmentVariable("SKIP_DEPLOY") == "true");

For better reporting, pass the reason alongside the boolean condition:

public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(async (ctx, _) =>
{
var repositoryInfo = await ctx.Tools.Git.Information.GetInfoAsync();
return repositoryInfo?.BranchName != "main";
}, "This should only run on the main branch");

protected override async Task<CommandResult> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
// Module logic here
}
}

Async Skip Conditions​

For conditions that require async operations:

public class MyModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(async (_, cancellationToken) =>
{
var response = await HttpClient.GetAsync(
"https://api.example.com/should-run",
cancellationToken);
return !response.IsSuccessStatusCode;
}, "The remote service is unavailable");
}

Combining with Other Behaviors​

Repeated WithSkipWhen conditions use OR-to-skip semantics, matching repeated [SkipIf<T>] attributes. They run in registration order, and evaluation stops when any condition returns SkipDecision.Skip.

For example, this module skips cleanup for either CI builds or non-main branches:

public class CleanupModule : Module<CommandResult>
{
protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhen(_ => Environment.GetEnvironmentVariable("CI") == "true", "Running in CI")
.WithSkipWhen(
async (ctx, _) => (await ctx.Tools.Git.Information.GetInfoAsync())?.BranchName != "main",
"Not on the main branch")
.WithAlwaysRun() // Run even if dependencies fail (when not skipped)
.WithTimeout(TimeSpan.FromMinutes(5));
}

When every condition must match before the module is skipped, group them explicitly with WithSkipWhenAll:

protected override void Configure(ModuleConfigurationBuilder module) => module
.WithSkipWhenAll(
_ => Environment.GetEnvironmentVariable("CI") == "true"
? SkipDecision.Skip("Running in CI")
: SkipDecision.DoNotSkip,
_ => Environment.GetEnvironmentVariable("DEPLOY_ENV") != "production"
? SkipDecision.Skip("Not deploying to production")
: SkipDecision.DoNotSkip);

Conditions inside a WithSkipWhenAll group use AND-to-skip semantics and combine their reasons. The group composes with other skip conditions using OR-to-skip semantics.

In V3, a later WithSkipWhen call replaced an earlier one. Analyzer MP0020 reports repeated WithSkipWhen calls on the same builder as a suggestion; suppress it when OR-to-skip is intended, or switch to WithSkipWhenAll when every condition must match.

History​

If a module was skipped, you can attempt to find its history from a previous run. See History

Run Conditions​

See Run Conditions

Categories​

See Categories