jasperfx / oakton Goto Github PK
View Code? Open in Web Editor NEWParsing and Utilities for Command Line Tools in .Net
Home Page: http://jasperfx.github.io/oakton
License: Apache License 2.0
Parsing and Utilities for Command Line Tools in .Net
Home Page: http://jasperfx.github.io/oakton
License: Apache License 2.0
New command line for generically interacting with stateful resources like Rabbit MQ, databases, etc. at the command line.
Should be able to:
The documentation shows an example of where [FlagAlias] is used on a property called "c", which is an argument, not a flag. When I use [FlagAlias] on my arguments, it doesn't work. Since it doesn't work, I have no way of making a required named parameter. I'd certainly like to be explicit about names on all my parameters while having a way to mark them required, because a lot of parameters can make the command unruly and unreadable.
This will need to be explicitly configured. Do similar to the ApplyChangesOnStartup in Marten, but make it more general on resources
Hi!
First of all, super cool library. Thanks for sharing it.
Currently, it seems there is two ways to register commands on CommandFactory::
RegisterCommand<T>()
RegisterCommands(Assembly assembly)
I need register by Type not using generics.
RegisterCommand(typeof(T))
Would you accept a pull request for that?
Thanks again.
using Oakton;
var builder = WebApplication.CreateBuilder(args);
builder.Host.ApplyOaktonExtensions();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
return await app.RunOaktonCommands(args);
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ProblemConfig": "value with [bracket]"
}
dotnet run describe
, it will throw below exception:ERROR: System.InvalidOperationException: Could not find color or style 'bracket'.
I've submitted a PR #69 for fixing this issue by applying EscapeMarkup()
.
Be a decent sample app, and it's something we want anyway.
Nice if you could:
Here's what we write out as the CommonAssemblyInfo.cs file in fubumvc:
puts "Writing src/CommonAssemblyInfo.cs..."
File.open('src/CommonAssemblyInfo.cs', 'w') do |file|
file.write "using System.Reflection;\n"
file.write "using System.Runtime.InteropServices;\n"
file.write "[assembly: AssemblyDescription("#{options[:description]}")]\n"
file.write "[assembly: AssemblyProduct("#{options[:product_name]}")]\n"
file.write "[assembly: AssemblyCopyright("#{options[:copyright]}")]\n"
file.write "[assembly: AssemblyTrademark("#{options[:trademark]}")]\n"
file.write "[assembly: AssemblyVersion("#{options[:version]}")]\n"
file.write "[assembly: AssemblyFileVersion("#{options[:file_version]}")]\n"
file.write "[assembly: AssemblyInformationalVersion("#{options[:informational_version]}")]\n"
end
end
Make sure to test custom command factories too
Combine the two projections
Hi,
First of all, thank for sharing your project.
It is possible to mark some arguments as mandatory ?
mandatory in the sense that if they are missing then we have an error.
Use MemberInfo
throughout?
Lot of stuff in StructureMap (and maybe more in FubuCore) that could be useful in here.
Done explicitly, and by assuming that a single command means that that's the default. Should help w/ the new dotnet extensibility
This is getting copied from Jasper. Use a new assembly level attribute called [assembly:OaktonCommandAssembly]
as a marker. Auto-discover the assemblies.
If we take the example as below, both properties start with S
and no custom flag alias are defined. For this case, s
is being generated as the short flag alias for both the properties.
public class ProjectionInput
{
[Description("If specified, only execute against the named Marten store. Does not apply with only one store")]
public string StoreFlag { get; set; }
[Description("If specified, use this shard timeout value for daemon")]
public string ShardTimeoutFlag { get; set; }
}
I think for these cases, we should only add a long flag and ignore short flags. The workaround is to explicitly define the flag alias.
Trying out the library. Copy pasted the Getting Started example. The usage with optional arguments does not work.
When running:
dotnet run -- "Alex Smith"
it always shows the Invalid Usage until you specify the color as well:
dotnet run -- "Alex Smith" Green
disposing isn't good enough
Recreate the doc standard from other JasperFx projects here.
Just less code in Oakton
Recognize when the first argument is a flag of some kind and use the default command
Thinking a syntax like:
--prop:Key Value
and
public class SomeInput
{
public Dictionary<string, string> Props {get;set;}
}
Bring out the conditional compilation directives!
I hate computers.
How do I get this to work in .NET 6?
New addition to Oakton.AspNetCore to add self descriptive services to the application to "describe" itself. This is taken from the DescribeCommand
in Jasper and will replace that.
Use an interface like this?
/// <summary>
/// Use this to provide diagnostic information about your application
/// Jasper will discover and use any service registered in the IoC
/// container that implements this interface
/// </summary>
public interface IDescribeMyself
{
Task Describe(TextWriter writer);
string Title {get;}
}
The new Oakton.AspNetcore command will loop through all of these and spit it out to the screen and/or a file.
I cannot figure out usage of the .opts file.
I have commands that have flags. When I make an .opts file that contains some of these flags, Oakton tells me:
Invalid usage Unknown argument or flag for value
When I include the command in the .opts file, Oakton tells me the same thing. How should I use the .opts file?
Incidentally, I love this library! :-)
The included DescriptionAttribute is nice and simple but it would be nice to be able to put my command descriptions in resources and potentially translate them.
I've just done some (albeit very quick) checking and the System.ComponentModel.Annotations nuget package seems to support all the platforms oakton targets and that should make it possible to set the description on a command with a line like the following:
_commandType.ForAttribute<DisplayAttribute>(att => { _description = att.GetDescription(); });
Did you have specific reasons for not doing this in the first place or has it just not been needed yet? Would you be interested in a PR if I implemented it?
Not sure how important this one really is, but you never know.
Well underway and most of the code is copied straight over from Jasper.
The initial feature set:
Might try Appveyor for this.
When using code below the application remains running after Startup.StartAsync completed. When I replace .RunOaktonCommands(args); with .RunConsoleAsync(); it shuts down fine
static Task<int> Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
return CreateHostBuilder(args).ConfigureLogging(logging =>
{
logging.ClearProviders();
}
)
.UseSerilog()
.ConfigureServices((_, services) =>
{
services.AddDescription<OaktonConfigDescription>();
services.AddHostedService<Startup>();
}
).RunOaktonCommands(args);
}
It's not going to work, but at least get the test here and adjust the DI mechanics
With ability to support cancellation token.
E.g. OaktonAsyncCommand:
public abstract Task<bool> ExecuteAsync(T input, CancellationToken cancellationToken);
I'm wondering if there is currently any supported way to use sub-commands?
I'm thinking of the way the Docker CLI works, for example:
docker container rm abc
docker volume ls
We can break down the first example like:
docker container rm abc
^ app ^command ^ sub-command ^ args
I thought perhaps command aliases could have a space in them, which could make this work:
[Description("List volumes", Name = "volume ls")]
public class ListVolumesCommand : OaktonAsyncCommand<ListVolumeInput>
Unfortunately this doesn't work, and I guess it would break handling of enumerable arguments.
Opt in by adding it as an option to CommandExecutor.
Work by adding each line to the beginning of the command line. Make sure that you can override commands.
Test that:
Generated help for dictionaries is wrong, as it includes 4 dashes (----
) instead of 2
public class RunInput {
[Description("The connection string", Name = "connection-string")]
public string ConnectionString { get; set; }
[Description("Check scripts to be applied, but do not run them")]
[FlagAlias("dry-run")]
public bool DryRunFlag { get; set; }
[Description("An optional list of variables to be used in scripts")]
public Dictionary<string, string> VarFlag { get; set; } = new Dictionary<string, string>();
}
run - Create or update a database
└── Create or update a database
└── dotnet run -- run <connectionstring>
├── [-d, --dry-run]
└── [----var:<prop> <value>]
I guess this happens as a result of
oakton/src/Oakton/Parsing/InputParser.cs
Line 14 in cf9e006
oakton/src/Oakton/Parsing/InputParser.cs
Lines 111 to 116 in cf9e006
oakton/src/Oakton/Parsing/DictionaryFlag.cs
Lines 20 to 22 in cf9e006
and finally
afc4f12#diff-1653881f4444af532bfaf6787fb0c0f4
This commit broke possibility of optional arguments
- var command = Build(commandName);
+ var usages = new UsageGraph(_commandTypes[commandName]);
This change missing "configuration" of usages in constructor of command and always validate on "default" usage with all arguments.
This change in behavior was intended?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Oakton;
using Serilog;
using Serilog.Events;
namespace LegacyEnterpriseSync
{
public class Program
{
public static Task<int> Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting web host");
return CreateHostBuilder(args).RunOaktonCommands(args);
// CreateHostBuilder(args).Build().Run();
// return Task.FromResult(0);
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return Task.FromResult(1);
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host
.CreateDefaultBuilder(args)
.UseSerilog((context, servicesProvider, loggingConfig) =>
{
loggingConfig.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.ConfigureAppConfiguration(configBuilder => { configBuilder.AddEnvironmentVariables(); })
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
}
The app runs, but request logging, etc. is not printing to the console. Only that first message before the Oakton function's call.
When I instead use:
CreateHostBuilder(args).Build().Run();
return Task.FromResult(0);
All the logging takes place as expected. I have tried with and without the 2 stage logger initialization using the lambda:
(context, servicesProvider, loggingConfig) =>
{
loggingConfig.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Console();
}
Just a way to quietly add environment checks from discovery
We've got Spectre.Console, so let's use it!
Kind of useless w/o that;)
Running a command like dotnet run -- db-patch file.sql --environment Staging
results in the following error:
I tried to set up my .NET 6 web application as described in the docs. In summary, my Program.cs file looks like the following:
var builder = WebApplication.CreateBuilder(args);
builder.Host.ApplyOaktonExtensions();
// ... other changes in the host builder (e.g. builder.Services.AddAuthentication())
var app = builder.Build();
// ... changes in the app (e.g. app.UseAuthentication())
return await app.RunOaktonCommands(args);
My development environment is a macOS with Apple silicon.
I'm trying to forward args to a BenchmarkDotNet method:
BenchmarkSwitcher.Run(string[] args, ...)
BenchmarkDotNet expects war Main(string[] args)
args and is run in the following manner:
dotnet run -c Release -- --job short --runtimes clr core --filter *BenchmarkClass1*
My input class looks as follows:
public class BenchInput : RickInput
{
[Description("BenchmarkDotNet Run args")]
public IEnumerable<string> Args { get; set; }
}
Is there a way to use Oakton to propagate string args[] "verbatim" ?
Hi there,
I've been trying to use this great tool for some command line apps we write. Most of the functionality works quite well. However, I can't seem to be able to parse negative numbers properly. Specifically, multi-digit ones seem to cause the most trouble (I am wrapping in quotes, and that does seem to make single digit negative numbers happy).
I've created a simple repo with xunit tests that shows this behaviour:
https://github.com/chrispellett/OaktonCommandParsingSandbox
Any help would be greatly appreciated.
Thanks!
Gonna require some nonstandard usage to make this work.
Ability to do environment checks on startup of the app.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.