Guider Upgrade with System.CommandLine - Implementing Help and Version Options
Another post outlines how to package a console project into a .NET CLI tool, using an example project called Guider
. This tool can be invoked from the terminal as guidgen
to generate a GUID and copy it to the clipboard for convenient access.
This post extends Guider
into a proper CLI tool, meaning it now supports the --help
and --version
options.
Before: (Options are ignored)
> guidgen
341f4f0a-4f2f-4b1c-9a57-bc06e6c8b68f
> guidgen --version
363dbd30-6339-4580-9950-11c0f958fb84
> guidgen --help
d3ef5e7e-4caa-4eb3-9fd1-7cdb3cb5fdff
After: (A proper CLI tool)
> guidgen
e1690e6f-2fe5-4e51-bb2d-25dfbbd05c00
> guidgen --version
1.1.0
> guidgen --help
Description:
Generate a GUID and copy it to the clipboard
Usage:
guidgen [options]
Options:
--version Show version information
-?, -h, --help Show help and usage information
The project is available on GitHub here. All changes described in this post are part of this pull request.
Introducing System.CommandLine
One possible way to handle command-line arguments is by using a specialised library. System.CommandLine is one such option.
The first commit introduces this library into the project. It extracts the GUID generation logic into the GenerateGuid
method and sets up a single command: rootCommand
. The --help
and --version
options are automatically generated by the library.
using TextCopy;
using System.CommandLine;
RootCommand rootCommand = new("Generate a GUID and copy it to the clipboard");
rootCommand.SetHandler(GenerateGuid);
return await rootCommand.InvokeAsync(args);
void GenerateGuid()
{
Guid guid = Guid.NewGuid();
Console.WriteLine(guid);
ClipboardService.SetText(guid.ToString());
}
Here is how it works:
> dotnet run
4e483f10-c83c-4675-a18d-ed1ba41d0667
> dotnet run -- --version
1.1.0+87e7ea8528b51bc603693fe622df85e5b2e8c701
> dotnet run -- --help
Description:
Generate a GUID and copy it to the clipboard
Usage:
Guider.Console [options]
Options:
--version Show version information
-?, -h, --help Show help and usage information
There are two issues with the implementation. One, the version output includes an appended commit hash, which is unnecessary. Two, the help output references the project name (Guider.Console
) instead of the tool name (guidgen
).
The following sections introduce failing tests for these issues and their respective fixes.
Adding Tests
The implementation issues necessitate testing the tool as a whole. The project can be executed with dotnet run -- {args}
. This process can be replicated and automated in tests. The second commit implements a CliRunner
class, adapted from this resource, and adds the first integration test:
[Test]
public void OutputsValidGuid()
{
// Act
var (exitCode, output) = CliRunner.Run("");
bool valid = Guid.TryParse(output, out Guid guid);
// Assert
valid.Should().Be(true);
exitCode.Should().Be(0);
}
The test executable is located in ./Guider.Tests/bin/Debug/net8.0/
, requiring the path to the console project (./Guider.Console
) to be specified as ../../../../Guider.Console
.
The third commit resolves the versioning issue by disabling IncludeSourceRevisionInInformationalVersion
in the project file. Details about this setting are available here.
[Test]
public void UsesMajorMinorPatchVersioning()
{
// Act
var (exitCode, output) = CliRunner.Run("--version");
bool valid = Regex.IsMatch(output, @"^\d+\.\d+\.\d+$");
// Assert
valid.Should().Be(true);
exitCode.Should().Be(0);
}
The fourth commit corrects the tool name displayed in the help output by renaming the root command.
[Test]
public void UsesCustomToolName()
{
// Act
var (exitCode, output) = CliRunner.Run("--help");
// Assert
output.Should().NotContain("Guider.Console");
exitCode.Should().Be(0);
}
Each of these tests takes over a second to execute, which is significantly longer than the typical time taken by unit tests (milliseconds). With just three tests, it is not yet an issue.
Sharing the Tool
With these changes, Guider
is ready to use. It can first be packed from the ./Guider.Console
directory:
dotnet pack
... and then installed if it does not yet exist:
dotnet tool install Guider --global --add-source ./packages
... or updated to the latest version if it does:
dotnet tool update Guider --global --add-source ./packages
Next Steps
Guider
now supports versioning, includes a help page, and has all commands and options covered by tests. It has become a proper CLI tool.
Potential next steps include additional functionality or distributing it via NuGet.org.