Releases: mayuki/Cocona
v2.2.0
What's Changed
- Add net7 SDK in pipeline by @wayneyaoo in #90
- Resolve the last dependency to allow injection overwrite by @wayneyaoo in #93
- Fix nested sub-command description for class-style by @mayuki in #88
- Add unit test for #93 by @mayuki in #95
- Hidden command should not get suggested when command not found. by @wayneyaoo in #96
- Bump version by @mayuki in #98
New Contributors
- @wayneyaoo made their first contribution in #90
Full Changelog: v2.1.0...v2.2.0
v2.1.0
What's Changed
- Fix typo by @mayuki in #68
- Move Obtaining Command Line Args Into DI by @hwoodiwiss in #72
- Remove net5.0 support by @moreal in #82
- Correct shellcheck warning by @moreal in #83
- Normalize resource file names by @marengohue in #84
- Support
OptionLikeCommand
's description by @moreal in #81
New Contributors
- @hwoodiwiss made their first contribution in #72
- @moreal made their first contribution in #82
- @marengohue made their first contribution in #84
Full Changelog: v2.0.3...v2.1.0
v2.0.3
What's Changed
- Update README.md by @1franck in #62
- Fixed some typos in exception messages by @icalvo in #61
- Update README.md by @vikramjb in #63
- Allow ConfigureLogging with Context in CoconaAppHostBuilder by @Hechamon in #64
New Contributors
- @1franck made their first contribution in #62
- @icalvo made their first contribution in #61
- @vikramjb made their first contribution in #63
- @Hechamon made their first contribution in #64
Full Changelog: v2.0.2...v2.0.3
v2.0.2
What's Changed
- Add Chinese translations by @maikebing in #59
New Contributors
- @maikebing made their first contribution in #59
Full Changelog: v2.0.1...v2.0.2
v2.0.1
v2.0.0
Features
Introduce Minimal API (#43, #45)
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();
Add a command and configure using the builder
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"));
});
New APIs
CoconaAppBuilder
Services
Build()
CoconaApp
(ICoconaCommandsBuilder
)- Static methods
Create
: Shorthand forCoconaApp.CreateBuilder().Build()
CreateBuilder
: CreatesCoconaAppBuilder
instance.CreateHostBuilder
: CreatesCoconaAppHostBuilder
instance. (formerCoconaApp.Create()
)
- Instance methods
AddCommand(Delegate)
AddCommand(string, Delegate)
AddCommands(Type)
AddCommands<T>()
AddSubCommand(string, Action<ICoconaCommandsBuilder>)
UseFilter
Run
/RunAsync
- Static methods
Introduce [Option(StopParsingOptions = true)]
(#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" }
}
Handle shutdown timeout (#57)
Cocona waits for a timeout before shutting down when canceling with Ctrl+C. This will allow you to perform a graceful shutdown.
Applied to Cocona
var builder = CoconaApp.CreateBuilder();
builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(15);
});
Applied to Cocona.Lite
CoconaLiteApp.Create(options =>
{
options.ShutdownTimeout = TimeSpan.FromSecond(15);
});
Localization (#44)
This release introduces localization mechanism for user-defined commands and add built-in ja-jp localization of messages.
New APIs
ICoconaLocalizer
interface: Provides a localized description of the command. (see CoconaSample.Advanced.Localization)
Help Wanted: Contributions are welcome! 🤝
- English: Since I (@mayuki) am not a native English speaker, this project has some problems such as lack of documentation and grammatical errors. Contributions of XML Doc comments, exception messages and README improvements would be welcome!
- Non-English: Contributions of non-English language (ja-jp, ko-kr, ...) resources are also welcome!
Breaking changes
Disable support for shell completion by default (#50)
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 Host APIs (#43)
- Rename
CoconaApp.Create()
->CoconaApp.CreateHostBuilder()
- Rename
IHostBuilder.UseCocona
->IHostBuilder.ConfigureCocona
- Remove
CommandTypes
fromCoconaAppOptions
andCoconaLiteAppOptions
Changes
Improvements
- Adopt to .NET 6 (#41)
- Add scope support for ServiceProvider (#56)
- Handle static methods marked as commands. (#35)
Fixes
- Update NuGet package info (#55)
- Throw exceptions in Run when building the application (#51)
- Throw an exception if a sub-command name is duplicated (#52)
Full Changelog: v1.6.0...v2.0.0
v1.6.0
Features
Introduce ParameterSet (#31)
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.
By parameterized constructor (includes record class)
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)" : "")}");
By properties (parameter-less constructor)
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)" : "")}");
Improvements
- Adpto to .NET 5 (#30)
Fixes
- Preserve the long named help option (#32)
v1.5.0
Features
Introduce HelpMessageBuilder (#22, #23)
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());
}
v1.4.0
Features
Shell command-line completion support (#14, #15, #18)
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.
Option-like commands (#17)
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!
Limitations
- Any previous options or arguments specified by OptionLikeCommand will be ignored.
- Example: If
--foo --bar --optionlikecommand --baz arg0
and--optionlikecommand
is an Option-like command, the command will be passed--baz arg0
.
- Example: If
- Arguments are not displayed in help.
CommandMethodForwardedTo attribute (#19)
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.
IgnoreUnknownOptions attribute
Cocona treats unknown options as errors by default.
Now, you can set the IgnoreUnknownOptions attribute to ignore unknown options.
Improvements
- #16: Share ServiceCollectionExtensions code between Cocona and Cocona.Lite.
Fixes
- #20: Capture stack trace of exception thrown during command execution.
v1.3.0
Improvements
#13: Nested sub-commands
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