Micro-framework for .NET console application. Cocona makes it easy and fast to build console applications on .NET.
MIT License
Bot releases are hidden (Show)
Full Changelog: https://github.com/mayuki/Cocona/compare/v2.1.0...v2.2.0
Published by mayuki almost 2 years ago
OptionLikeCommand
's description by @moreal in https://github.com/mayuki/Cocona/pull/81
Full Changelog: https://github.com/mayuki/Cocona/compare/v2.0.3...v2.1.0
Published by mayuki over 2 years ago
Full Changelog: https://github.com/mayuki/Cocona/compare/v2.0.2...v2.0.3
Published by mayuki almost 3 years ago
Full Changelog: https://github.com/mayuki/Cocona/compare/v2.0.1...v2.0.2
Published by mayuki almost 3 years ago
Full Changelog: https://github.com/mayuki/Cocona/compare/v2.0.0...v2.0.1
Published by mayuki almost 3 years ago
This release introduces ASP.NET Core-like Minimal API. It's an enhancement for .NET 6 and C# 10.
Minimal API supports most of the expressions that were possible with the previous Class-based style.
Please refer to the updated documentation for more details and see CoconaSample.MinimalApi.InAction.
CoconaApp.Run
and CoconaApp.RunAsync
Run
and RunAsync
is a shortcut that makes it easy to build command line applications.
await CoconaApp.RunAsync((string? name) => Console.WriteLine($"Hello {(name ?? "Guest")}!"));
This is equivalent to the following code using Minimal API Builder.
var builder = CoconaApp.CreateBuilder(args);
var app = builder.Build();
app.AddCommand((string? name) => Console.WriteLine($"Hello {(name ?? "Guest")}!"));
app.Run();
CoconaApp.CreateBuilder()
and CoconaApp.Create()
CreateBuilder
and Create
provide builders to configure services and register/configure multiple commands.
var builder = CoconaApp.CreateBuilder();
builder.Services.AddTransient<IMyService, MyService>();
builder.Configuration.AddJsonFile("path/to/additonalsettings.json");
var app = builder.Build();
app.AddCommand("hello", (string? name) => Console.WriteLine($"Hello {(name ?? "Guest")}!"));
app.AddSubCommand("server", x =>
{
x.UseFilter(new RequirePrivilege());
x.AddCommand("start", () => Console.WriteLine("Start"));
x.AddCommand("stop", () => Console.WriteLine("Stop"));
});
app.AddSubCommand("client", x =>
{
x.AddCommand("connect", () => Console.WriteLine("Connect"));
x.AddCommand("disconnect", () => Console.WriteLine("Disconnect"));
});
app.AddComands<CommandType>();
await app.RunAsync();
var app = CoconaApp.Create(); // Shorthand for CoconaApp.CreateBuilder().Build();
// Add a command and configure.
app.AddCommand("hello", (string? name) => Console.WriteLine($"Hello {(name ?? "Guest")}!"))
// Sets a description to the command.
.WithDescription("Say hello");
// Sets aliases to the command.
.WithAliases(new[] { "hey" })
// Sets a metadata (attribute) to the command.
.WithMetadata(new HiddenAttribute());
// Use a user-defined command filter.
app.UseFilter(new MyFilterAttribute());
// Configure filters per command.
app.AddCommand("konnichiwa", (string name) => Console.WriteLine($"Konnichiwa {name}!"))
.WithFilter(new MyFilterAttribute())
.WithFilter(async (ctx, next) =>
{
if (Environment.UserName != "Administrator")
{
throw new CommandExitedException("Insufficient Permissions");
}
return next(ctx);
})
.CommandLikeOption(x =>
{
// CommandLikeOption: konnichiwa --info
x.Add("info", () => Console.WriteLine("Show Information"));
});
CoconaAppBuilder
Services
Build()
CoconaApp
(ICoconaCommandsBuilder
)
Create
: Shorthand for CoconaApp.CreateBuilder().Build()
CreateBuilder
: Creates CoconaAppBuilder
instance.CreateHostBuilder
: Creates CoconaAppHostBuilder
instance. (former CoconaApp.Create()
)AddCommand(Delegate)
AddCommand(string, Delegate)
AddCommands(Type)
AddCommands<T>()
AddSubCommand(string, Action<ICoconaCommandsBuilder>)
UseFilter
Run
/ RunAsync
[Option(StopParsingOptions = true)]
(https://github.com/mayuki/Cocona/pull/36)[Option(StopParsingOptions = true)]
enables to stop parsing options after a option on a command line.
public void A([Option]int a, [Option(StopParsingOptions = true)]string b, [Argument]string arg0, [Argument]string[] args)
{
// $ ./myapp --a 123 --b valueB -c -d --e f
// a = 123
// b = "valueB"
// arg0 = "-c"
// args = new [] { "d", "--e", "f" }
}
Cocona waits for a timeout before shutting down when canceling with Ctrl+C. This will allow you to perform a graceful shutdown.
var builder = CoconaApp.CreateBuilder();
builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(15);
});
CoconaLiteApp.Create(options =>
{
options.ShutdownTimeout = TimeSpan.FromSecond(15);
});
This release introduces localization mechanism for user-defined commands and add built-in ja-jp localization of messages.
ICoconaLocalizer
interface: Provides a localized description of the command. (see CoconaSample.Advanced.Localization)Support for Shell completion feature is now disabled by default.
If you want to continue to enable it, set the EnableShellCompletionSupport
option to true
.
CoconaApp.Create()
-> CoconaApp.CreateHostBuilder()
IHostBuilder.UseCocona
-> IHostBuilder.ConfigureCocona
CommandTypes
from CoconaAppOptions
and CoconaLiteAppOptions
Full Changelog: https://github.com/mayuki/Cocona/compare/v1.6.0...v2.0.0
Published by mayuki about 3 years ago
Introduce a mechanism called Parameter set that defines common parameters for multiple commands.
For example, if every command receives a user name, host name, etc., it would be annoying to define them in a method for each command.
A class or record
implements the ICommandParameterSet
interface and treats it as a Parameter set.
If a class (or record class) has a parameterized constructor, it is treated as part of the definition of a command method.
public record CommonParameters(
[Option('t', Description = "Specifies the remote host to connect.")]
string Host,
[Option('p', Description = "Port to connect to on the remote host.")]
int Port,
[Option('u', Description = "Specifies the user to log in as on the remote host.")]
string User = "root",
[Option('f', Description = "Perform without user confirmation.")]
bool Force = false
) : ICommandParameterSet;
public void Add(CommonParameters commonParams, [Argument] string from, [Argument] string to)
=> Console.WriteLine($"Add: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");
public void Update(CommonParameters commonParams, [Option('r', Description = "Traverse recursively to perform.")] bool recursive, [Argument] string path)
=> Console.WriteLine($"Update: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");
If a class has a parameter-less constructor, you can mark the public property as Option
or Argument
.
NOTE: Option defined as a property is treated as required by default. If you want a non-required Option to have a default value, mark it with HasDefaultValue
attribute.
public class CommonParameters : ICommandParameterSet
{
[Option('t', Description = "Specifies the remote host to connect.")]
public string Host { get; set; }
[Option('p', Description = "Port to connect to on the remote host.")]
public int Port { get; set; }
[Option('u', Description = "Specifies the user to log in as on the remote host.")]
[HasDefaultValue]
public string User { get; set; } = "root";
[Option('f', Description = "Perform without user confirmation.")]
public bool Force { get; set; } = false;
}
public void Add(CommonParameters commonParams, [Argument] string from, [Argument] string to)
=> Console.WriteLine($"Add: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");
public void Update(CommonParameters commonParams, [Option('r', Description = "Traverse recursively to perform.")] bool recursive, [Argument] string path)
=> Console.WriteLine($"Update: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");
Published by mayuki over 4 years ago
Introduce HelpMessageBuilder, which makes is easy to help messages can be displayed from user code.
// Show help for commands. (same as `./HelpOnDemand --help`)
public void ForContext(bool optionA, [FromService] ICoconaHelpMessageBuilder helpMessageBuilder)
{
Console.WriteLine(helpMessageBuilder.BuildAndRenderForCurrentContext());
}
// Show help for this command. (same as `./HelpOnDemand for-command --help`)
public void ForCommand(bool optionA, [FromService] ICoconaHelpMessageBuilder helpMessageBuilder)
{
Console.WriteLine(helpMessageBuilder.BuildAndRenderForCurrentCommand());
}
Published by mayuki over 4 years ago
Cocona provides support for shell command-line completion (also known as tab completion).
Currently, The supported shells are bash
and zsh
.
Cocona generates a shell script for command-line completion from a command definition and allows users to use command-line completion by loading it. The --completion
built-in option is used to specify the name of a shell to generate a script.
$ source <(./myapp --completion bash)
or
% ./myapp --completion zsh > ~/.zsh/functions
This feature is enabled by default, or you can set the EnableShellCompletionSupport
option to false
if you don't need it.
It is also possible to dynamically generate command-line completion candidates and to prepare candidates at script generation time. Please see the sample below for more details.
The option-like command is a way to achieve an independent command that at first glance, looks like an option in a command.
For example, easy to understand examples like --version
and --help
.
These are the options of a command, but they behave as a command when specified.
[OptionLikeCommand("hello", new[] {'f'}, typeof(Program), nameof(Hello))]
public void Execute()
=> Console.WriteLine("Execute");
private void Hello([Argument]string name)
=> Console.WriteLine($"Hello {name}!");
$ ./myapp
Execute
$ ./myapp --hello Alice
Hello Alice!
--foo --bar --optionlikecommand --baz arg0
and --optionlikecommand
is an Option-like command, the command will be passed --baz arg0
.The CommandMethodForwardedTo
attribute allows you to specify that the substance of the specified command method is a different method and that the operation should be forwarded.
If this attribute is given to a command method, the destination's attribute and its implementation are used. Excepts for the Command
and Hidden
attributes specified by the method.
For example, it can be used if the command implementation is defined in an external assembly or to call a built-in command (such as help) or compatibility purposes.
[CommandMethodForwardedTo(typeof(BuiltInOptionLikeCommands), nameof(BuiltInOptionLikeCommands.ShowHelp))]
public void MyHelp()
=> throw new NotSupportedException(); // NOTE: The method body and parameters used is BuiltInOptionLikeCommands.ShowHelp.
Cocona treats unknown options as errors by default.
Now, you can set the IgnoreUnknownOptions attribute to ignore unknown options.
Published by mayuki over 4 years ago
Added nested sub-commands support. You can implement nested sub-commands in your applications.
Specify the class that has nested sub-commands using HasSubCommands
attribute.
[HasSubCommands(typeof(Server), Description = "Server commands")]
[HasSubCommands(typeof(Client), Description = "Client commands")]
class Program
{
static void Main(string[] args) => CoconaApp.Run<Program>(args);
// ./myapp info
public void Info() => Console.WriteLine("Show information");
}
// ./myapp server [command]
class Server
{
public void Start() => Console.WriteLine("Start");
public void Stop() => Console.WriteLine("Stop");
}
// ./myapp client [command]
class Client
{
public void Connect() => Console.WriteLine("Connect");
public void Disconnect() => Console.WriteLine("Disconnect");
}
$ ./SubCommandApp
Usage: SubCommandApp [command]
Usage: SubCommandApp [--help] [--version]
SubCommandApp
Commands:
info
server Server commands
client Client commands
Options:
-h, --help Show help message
--version Show version
$ ./SubCommandApp
Usage: SubCommandApp server [command]
Usage: SubCommandApp server [--help]
SubCommandApp
Commands:
start
stop
Options:
-h, --help Show help message