Skip to main content

Executing CLI Tools

Interacting with third-party command-line interface tools (CLIs) is an essential task in build automation. This includes a wide range of aspects, such as resolution of the tool path, construction of arguments to be passed, evaluation of the exit code and capturing of standard and error output. NUKE hides these concerns in dedicated auto-generated CLI wrappers.

Exhaustive list of supported tools
ToolSupported Commands
AzureSignToolsign
BenchmarkDotNetSingle top-level command
BootsSingle top-level command
Chocolateyfind, list, new, outdated, pack, push, search
CloudFoundryapi, auth, bind-service, bind-service, create-route, create-service, create-space, cups, curl, delete, delete-service, delete-space, login, map-route, push, restage, restart, scale -f, service, set-env, set-env, start, stop, target, unmap-route
CodecovSingle top-level command
CodeMetricsSingle top-level command
CorFlagsSingle top-level command
CoverallsNetSingle top-level command
CoverletSingle top-level command
DocFXbuild, dependency, download, help, init, merge, metadata, pdf, serve, template
Dockerattach, build, builder, builder build, builder prune, buildx build, checkpoint, checkpoint create, checkpoint ls, checkpoint rm, commit, config, config create, config inspect, config ls, config rm, container, container attach, container commit, container create, container diff, container exec, container export, container inspect, container kill, container logs, container ls, container pause, container port, container prune, container rename, container restart, container rm, container run, container start, container stats, container stop, container top [ps, container unpause, container update, container wait, context, context create, context export, context import, context inspect, context ls, context rm, context update, context use, create, deploy, diff, engine, engine activate, engine check, engine update, events, exec, export, history, image, image build, image history, image import, image inspect, image load, image ls, image prune, image pull, image push, image rm, image save, image tag, images, import, info, inspect, kill, load, login, logout, logs, manifest, manifest annotate, manifest create, manifest inspect, manifest push, network, network connect, network create, network disconnect, network inspect, network ls, network prune, network rm, node, node demote, node inspect, node ls, node promote, node ps, node rm, node update, pause, plugin, plugin create, plugin disable, plugin enable, plugin inspect, plugin install, plugin ls, plugin push, plugin rm, plugin set, plugin upgrade, port, ps, pull, push, rename, restart, rm, rmi, run, save, search, secret, secret create, secret inspect, secret ls, secret rm, service, service create, service inspect, service logs, service ls, service ps, service rm, service rollback, service scale, service update, stack, stack deploy, stack ls, stack ps, stack rm, stack services, start, stats, stop, swarm, swarm ca, swarm init, swarm join HOST:PORT, swarm join-token, swarm leave, swarm unlock, swarm unlock-key, swarm update, system, system df, system events, system info, system prune, tag, top [ps, trust, trust inspect, trust key, trust key generate, trust key load, trust revoke, trust sign IMAGE:TAG, trust signer, trust signer add, trust signer remove, unpause, update, version, volume, volume create, volume inspect, volume ls, volume prune, volume rm, wait
DotCoveranalyse, cover, delete, dotnet, merge, report, zip
DotNetbuild, clean, msbuild, nuget add source, nuget push, pack, publish, restore, run, test, tool install, tool restore, tool uninstall, tool update
EntityFrameworkef database drop, ef database update, ef dbcontext info, ef dbcontext list, ef dbcontext scaffold, ef dbcontext script, ef migrations add, ef migrations list, ef migrations remove, ef migrations script
FixieSingle top-level command
GitLinkSingle top-level command
GitReleaseManageraddasset, close, create, export, publish
GitVersionSingle top-level command
Helmcompletion, create, delete, dependency build, dependency list, dependency update, fetch, get, get hooks, get manifest, get notes, get values, history, home, init, inspect, inspect chart, inspect readme, inspect values, install, lint, list, package, plugin install, plugin list, plugin remove, plugin update, repo add, repo index, repo list, repo remove, repo update, reset, rollback, search, serve, status, template, test, upgrade, verify, version
ILRepackSingle top-level command
InnoSetupSingle top-level command
Kubernetesalpha, annotate, api-resources, api-versions, apply, apply -k, attach, auth, autoscale, certificate, cluster-info, completion, config, convert, cordon, cp, create, delete, describe, drain, edit, exec, explain, expose, get, kubectl, label, logs, options, patch, plugin, port-forward, proxy, replace, rolling-update, rollout, run, run-container, scale, set, taint, top, uncordon, version, wait
MauiCheckconfig
MinVerSingle top-level command
MSBuildSingle top-level command
MSpecSingle top-level command
NerdbankGitVersioningcloud, get-commits, get-version, install, prepare-release, set-version, tag
Netlifynetlify deploy, netlify sites:create, netlify sites:delete
Npmci, install, run
NSwagaspnetcore2openapi, aspnetcore2swagger, jsonschema2csclient, jsonschema2tsclient, list-controllers, list-types, new, openapi2csclient, openapi2cscontroller, openapi2tsclient, run, swagger2csclient, swagger2cscontroller, swagger2tsclient, types2openapi, types2swagger, version, webapi2openapi, webapi2swagger
NuGetinstall, pack, push, restore, sources add, sources disable, sources enable, sources list, sources remove, sources update
NUnitSingle top-level command
Octopusbuild-information, create-release, deploy-release, pack, push
OctoVersionoctoversion, octoversion
OpenCoverSingle top-level command
Paketpack, push, restore, update
PowerShellSingle top-level command
Pulumiconfig, config cp, config get, config refresh, config rm, config set, destroy, new, preview, stack, stack change-secrets-provider, stack export, stack graph, stack history, stack import, stack init, stack ls, stack output, stack rename, stack rm, stack select, stack tag get, stack tag ls, stack tag rm, stack tag set, up
ReportGeneratorSingle top-level command
ReSharpercleanupcode, dupfinder, inspectcode
SignClientsign
SignToolsign
SonarScannerbegin, end
SpecFlowbuildserverrun, mstestexecutionreport, nunitexecutionreport, register, register, register, run, stepdefinitionreport
SquirrelSingle top-level command
TestCloudsubmit
Unity-createManualActivationFile, -returnlicense
VSTestSingle top-level command
VSWhereSingle top-level command
WebConfigTransformRunnerSingle top-level command
XunitSingle top-level command

You can execute MSBuild using the lightweight API as follows:

MSBuildTasks.MSBuild($"{SolutionFile} /target:Rebuild /p:Configuration={Configuration} /nr:false");

The returned object is a collection of standard and error output.

info

Many CLI tasks require to add a package reference to the build project file. For instance, when using NUnitTasks there should be one of the following entries to ensure the tool is available:

_build.csproj
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" ExcludeAssets="all" />
</ItemGroup>

</Project>

While it would be possible to magically download required packages, this explicit approach ensures that your builds are reproducible at any time. If a package is not referenced, the resulting error message will include a command to install the package via the global tool.

Fluent API​

While the example from above is quite easy to understand, it also illustrates certain weaknesses. What if SolutionFile contains a space and must be quoted? What is the separator to pass multiple targets? Is the configuration actually passed as a dedicated argument or as an MSBuild property? What does the /nr switch stand for? To solve these issues, you can use the individual fluent interfaces:

MSBuildTasks.MSBuild(_ => _
.SetTargetPath(SolutionFile)
.SetTargets("Clean", "Build")
.SetConfiguration(Configuration)
.EnableNodeReuse());

You can also use the fluent interfaces to manipulate the process invocation, including tool path, arguments, working directory, timeout and environment variables.

info

All fluent interfaces implement a variation of the builder pattern, in which every fluent call will create an immutable copy of the current ToolSettings instance with the intended changes applied. This enables great flexibility in composing similar process invocations.

Conditional Modifications​

In some cases, you may want to apply certain options only when a particular condition is met. This can be done fluently too, by using the When extension:

DotNetTasks.DotNetTest(_ => _
.SetProjectFile(ProjectFile)
.SetConfiguration(Configuration)
.EnableNoBuild()
.When(PublishTestResults, _ => _
.SetLogger("trx")
.SetResultsDirectory(TestResultsDirectory)));

Combinatorial Modifications​

A typical situation when using MSBuild for compilation, is to compile for different configurations, target frameworks or runtimes. You can use the CombineWith method to create different combinations for invocation:

var publishCombinations =
from project in new[] { FirstProject, SecondProject }
from framework in project.GetTargetFrameworks()
from runtime in new[] { "win10-x86", "osx-x64", "linux-x64" }
select new { project, framework, runtime };

DotNetTasks.DotNetPublish(_ => _
.EnableNoRestore()
.SetConfiguration(Configuration)
.CombineWith(publishCombinations, (_, v) => _
.SetProject(v.project)
.SetFramework(v.framework)
.SetRuntime(v.runtime)));

Multiple Invocations​

Based on combinatorial modifications it is possible to set a degreeOfParallelism (default 1) and a flag to continueOnFailure (default false):

DotNetTasks.DotNetNuGetPush(_ => _
.SetSource(Source)
.SetSymbolSource(SymbolSource)
.SetApiKey(ApiKey)
.CombineWith(
OutputDirectory.GlobFiles("*.nupkg").NotEmpty(), (_, v) => _
.SetTargetPath(v)),
degreeOfParallelism: 5,
continueOnFailure: true);

This example will always have 5 packages being pushed simultaneously. Possible exceptions, for instance when a package already exists, are accumulated to an AggregateException and thrown when all invocations have been processed. The console output is buffered until all invocations are completed.

Custom Arguments​

It may happen that certain arguments are not available from the fluent interface. In this case, the SetProcessArgumentConfigurator method can be used to add them manually:

MSBuildTasks.MSBuild(_ => _
.SetTargetPath(SolutionFile)
.SetProcessArgumentConfigurator(_ => _
.Add("/r")));

Exit Code Handling​

By default, every invocation is asserted to have a zero exit code. However, you can also overwrite this behavior using SetProcessExitHandler when required:

NUnitTasks.NUnit3(_ => _
.SetInputFiles(Assemblies)
.SetProcessExitHandler(p => p.ExitCode switch
{
-1 => throw new Exception("Invalid args"),
>0 => throw new Exception($"{p.ExitCode} tests have failed"),
_ => null
}));

As a shorthand syntax, you can also disable the default assertion of a zero exit code, which essentially sets an empty delegate as the exit handler:

NUnitTasks.NUnit3(_ => _
.SetInputFiles(Assemblies)
.DisableProcessAssertZeroExitCode());

Verbosity Mapping​

Using the VerbosityMappingAttribute, it is possible to automatically map the verbosity passed via --verbosity to individual tools. The attribute must be applied on the build class level:

[VerbosityMapping(typeof(MSBuildVerbosity),
Quiet = nameof(MSBuildVerbosity.Quiet),
Minimal = nameof(MSBuildVerbosity.Minimal),
Normal = nameof(MSBuildVerbosity.Normal),
Verbose = nameof(MSBuildVerbosity.Detailed))]
class Build : NukeBuild
{
// ...
}

Lightweight API​

Many of the most popular tools are already implemented. In case a certain tool is not yet supported with a proper CLI task class, NUKE allows you to use the following injection attributes to load them:

[PathVariable]
readonly Tool Git;

[NuGetPackage(
packageId: "Redth.Net.Maui.Check",
packageExecutable: "MauiCheck.dll",
// Must be set for tools shipping multiple versions
Framework = "net6.0")]
readonly Tool MauiCheck;

// Relative to root directory or absolute path
[LocalPath("./tools/corflags.exe")]
readonly Tool CorFlags;

// Different path on Windows and Unix
[LocalPath(
windowsPath: "gradlew.bat",
unixPath: "gradlew")]
readonly Tool Gradle;

The injected Tool delegate allows passing arguments, working directory, environment variables and many more process-specific options:

// Pass arguments with string interpolation
Git($"checkout -b {Branch}");

// Change working directory and environment variables
CorFlags(
arguments: $"...",
workingDirectory: SourceDirectory,
environmentVariables: EnvironmentInfo.Variables
.ToDictionary(x => x.Key, x => x.Value)
.SetKeyValue("key", "value").AsReadOnly());

// Only execute when available
// Requires: <PackageDownload Include="Redth.Net.Maui.Check" Version="0.10.0" />
MauiCheck?.Invoke($"--fix --preview");