This repo provides a boilerplate project for creating cross-platform .net console applications using a command-line interface (CLI) parsed by Mono.Options. The motivation behind this project is to help demonstrate the use of commands with the Mono.Options library, while leveraging .net core dependency injection to simplify the integration of these commands into a functional application. The project provides a good starting point that allows a developer to focus on the commands and functionality of the app without spending time on the setup.
You must have the .NET 6 SDK or higher installed to use this project.
The CLI command structure resembles that used by the dotnet CLI and consists of the executable (or "assembly"), the command (or "verb"), and possible command arguments and/or options.
myconsoleapp [options] [command] [arguments] [command-options]
Note: [options] can also follow the command arguments and be included with the [command-options].
The command (or "verb") is simply a command that performs an action. The commands are implemented as a console application using a myconsoleapp {command}
convention.
The arguments passed on the command line are the arguments for the command. For example when you execute myconsoleapp {command} {argument}
the {argument}
itself is passed to the {command}
.
The options passed on the command line are the options for the command. These options can include ones specific to the command, or general options for the executable. For example, if you execute something like myconsoleapp {command} --verbose
, the --verbose
option will be used for the {command}
.
This section contains essential details for creating the commands and options. An example weather command, and other related files, are included in the project in order to help demonstrate the setup and functionality, these files can be safely removed at anytime.
First create a new command builder that implements the ICommandBuilder
interface. This is added to the Services folder in the Commands project. For example:
public class NewCommandBuilder : ICommandBuilder
Next, implement the BuildCommand
method to setup the command and incorporate additional functionality in the service class. See the example below and the included WeatherCommandBuilder.cs
file, also check out Mono.Options for more details.
Note: at a minimum an OptionSet with help output must be configured.
public Command BuildCommand()
{
var option1 = false;
return new Command("mycommand", "The command help description.")
{
Options = new OptionSet
{
"Usage: myconsoleapp mycommand <argument1> [options]",
"",
"argument1:",
" The argument help description.",
"",
"Options:",
{ "o|option1", "The option1 help description.", opt =>
{
// example - set to a local variable to know if the option was passed
option1 = opt is not null;
}
},
{ "v|value1=", "The value1 help description.", opt =>
{
if (string.IsNullOrWhiteSpace(opt))
{ // throw exception if the value is required, incorrect, etc
throw new CommandException("mycommand", "A value is required for the [value1] option.");
}
_myValue1 = opt; // example - set to a private class field
}
}
},
Run = args =>
{
// do something with the args and options here...
}
};
}
Finally, the new service will need to be added to the configuration in Program.cs.
services.AddTransient<ICommandBuilder, NewCommandBuilder>();
The AsyncCommand
class can be used in place of the Command
class if the Run delegate for the command needs to incorporate asynchronous calls. Here is an example of using async/await in a command service:
public Command BuildCommand()
{
return new AsyncCommand("mycommand", "An example using async/await.")
{
Options = new OptionSet
{
"Usage: myconsoleapp mycommand"
},
Run = async args =>
{
// ...
await callApiResource();
}
};
}
The help descriptions for the main application, and for each registered command, will be output automatically by Mono.Options when the help flag is passed:
myconsoleapp help|--help|-h
myconsoleapp mycommand --help|-h
Note: directions for help usage are also included in the message for some exceptions.
The help description can be customized by providing a help option for the command. This will override the built-in help output and allow for handling this in the CommandBuilder service.
public Command BuildCommand()
{
var showHelp = false;
var options = new OptionSet
{
"Usage: mycommand [options]",
{
"help", "", h =>
{
showHelp = h is not null;
},
true // hide the option since it's already shown as a global app option
}
};
return new Command("mycommand", "My command help description.")
{
Options = options,
Run = args =>
{
if (showHelp)
{
// optionally output the option description (the default help output)
Reporter.Output.WriteOptionDescriptions(options);
// output the custom help description using the Reporter
Reporter.Output.WriteLine("My additional custom help description.");
return;
}
// ... the code to execute when help is not passed
}
};
}
Global application options can be access from any command builder service by using the static CommandContext
class.
These options are defined in Program.cs and can also be set via an environment variable. See the predefined Verbose option for an example on usage and for adding additional options.
The Reporter allows for sending output to the console. This class has static methods that can be used to write content at any time:
Reporter.Output.WriteLine("my message")
Reporter.Error.WriteLine("my error")
Reporter.Verbose.WriteLine("my verbose message")
Note: only when the verbose option is passed will messages sent to the Verbose stream be written.
The project has been setup to use the options pattern in .net core. The pattern uses models to represent groups of related settings. The project has an example WeatherOptions
model to demonstrate the usage.
- Add a property for the option to the appsettings.json file in the CLI project
- Add a new option model to the Common project
- Register the option in the Program.cs file using the
configureOptions
method
Publish the solution directly using the dotnet CLI.
Run the following command to create an executable for windows:
dotnet publish /Path/To/ConsoleAppMonoBoilerplate.csproj -r win-x64 --self-contained
This will create a "win-x64" directory in the release bin of the CLI project, or you can specify the output directory when publishing:
dotnet publish /Path/To/ConsoleAppMonoBoilerplate.csproj -o c:\output -r win-x64 --self-contained
Note: the project is setup to support the following runtimes: win-x64, linux-x64, and osx-x64.