Skip to main content

Sub-modules

What are they?​

Sub-modules track and organise blocks of execution where it doesn't make sense to refactor into a module. This is useful when iterating through data in a loop.

For instance, you have 10 .NET projects to package into NuGet packages.

By declaring each package operation as a sub-module, you can track failures and see each duration in the console progress display.

A sub-module takes a name and a token-aware body to execute.

If a sub-module fails, its original exception propagates while its name remains visible in progress output.

In the example below, the .csproj filename identifies each sub-module.

Example​

[DependsOn<NugetVersionGeneratorModule>]
public class PackProjectsModule : Module<CommandResult[]>
{
protected override async Task<CommandResult[]> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var packageVersion = await context.GetModule<NugetVersionGeneratorModule>();

var repositoryInfo = await context.Tools.Git.Information.GetRequiredInfoAsync(cancellationToken);
var projects = repositoryInfo.Root
.GetFiles(x =>
x.Extension == ".csproj" && !x.Name.Contains("test", StringComparison.InvariantCultureIgnoreCase))
.ToList();

return await PackProjectsAsync(context, projects, packageVersion.Value, cancellationToken).ToArrayAsync(cancellationToken: cancellationToken);
}

private async IAsyncEnumerable<CommandResult> PackProjectsAsync(IModuleContext context, List<FilePath> projects, string packageVersion, [EnumeratorCancellation] CancellationToken cancellationToken)
{
foreach (var project in projects)
{
yield return await context.RunSubModuleAsync(project.Name, token => context.Tools.DotNet.PackAsync(new DotNetPackOptions
{
ProjectSolution = project.Path,
Configuration = "Release",
Properties =
[
new KeyValue("PackageVersion", packageVersion),
new KeyValue("Version", packageVersion),
],
}, cancellationToken: token), cancellationToken);
}
}
}