diff --git a/tools/DocGen/DocGen.csproj b/tools/DocGen/DocGen.csproj
index 1890c043d16..bf97292bb15 100644
--- a/tools/DocGen/DocGen.csproj
+++ b/tools/DocGen/DocGen.csproj
@@ -8,7 +8,8 @@
-
+
+
diff --git a/tools/DocGen/Program.cs b/tools/DocGen/Program.cs
index 80a62ef36ca..d4d70e110ac 100644
--- a/tools/DocGen/Program.cs
+++ b/tools/DocGen/Program.cs
@@ -1,66 +1,65 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
-using System.ComponentModel;
+using System.CommandLine;
using Nethermind.DocGen;
using Spectre.Console;
-using Spectre.Console.Cli;
-var app = new CommandApp();
-
-app.Run(args);
-
-public sealed class AppCommand : Command
+CliOption configOption = new("--config") { Description = "Generate configuration options docs" };
+CliOption dbSizeOption = new("--dbsize") { Description = "Generate DB sizes" };
+CliOption dbSizeSourceOption = new("--dbsize-src")
{
- public override int Execute(CommandContext context, AppSettings settings)
- {
- if (settings.DocsPath is null)
- {
- AnsiConsole.MarkupLine("[red]The path to the docs is not specified[/]");
- return 1;
- }
-
- if (!Directory.Exists(settings.DocsPath))
- {
- AnsiConsole.MarkupLine("[red]No docs not found at the path specified[/]");
- return 1;
- }
-
- if (settings.GenerateConfig)
- ConfigGenerator.Generate(settings.DocsPath);
-
- if (settings.GenerateDBSize)
- DBSizeGenerator.Generate(settings.DocsPath, settings.DBSizeSourcePath);
-
- if (settings.GenerateJsonRpc)
- JsonRpcGenerator.Generate(settings.DocsPath);
+ Description = "The path to the directory with DB size files",
+ HelpName = "path"
+};
+CliArgument docsDirArg = new("docs-dir")
+{
+ Description = "The path to the docs directory",
+ HelpName = "path"
+};
+CliOption jsonRpcOption = new("--jsonrpc") { Description = "Generate JSON-RPC API docs" };
+CliOption metricsOption = new("--metrics") { Description = "Generate metrics options docs" };
- if (settings.GenerateMetrics)
- MetricsGenerator.Generate(settings.DocsPath);
+dbSizeOption.Validators.Add(optionResult =>
+{
+ if (optionResult.Parent?.GetValue(dbSizeSourceOption) is null)
+ optionResult.AddError($"{dbSizeSourceOption.Name} must be specified when {dbSizeOption.Name} is set");
+});
+
+CliRootCommand rootCommand =
+[
+ configOption,
+ dbSizeOption,
+ dbSizeSourceOption,
+ docsDirArg,
+ jsonRpcOption,
+ metricsOption
+];
+rootCommand.SetAction(parseResult =>
+{
+ var docsPath = parseResult.GetValue(docsDirArg)!;
- return 0;
+ if (!Directory.Exists(docsPath))
+ {
+ AnsiConsole.MarkupLine("[red]The specified docs directory not found[/]");
+ return 1;
}
-}
-public sealed class AppSettings : CommandSettings
-{
- [Description("Path to the directory with DB size files")]
- [CommandOption("--dbsize-src")]
- public string? DBSizeSourcePath { get; init; }
+ if (parseResult.GetValue(configOption))
+ ConfigGenerator.Generate(docsPath);
+
+ if (parseResult.GetValue(dbSizeOption))
+ DBSizeGenerator.Generate(docsPath, parseResult.GetValue(dbSizeSourceOption));
- [Description("Path to the docs")]
- [CommandArgument(0, "[docspath]")]
- public string? DocsPath { get; init; }
+ if (parseResult.GetValue(jsonRpcOption))
+ JsonRpcGenerator.Generate(docsPath);
- [CommandOption("--config")]
- public bool GenerateConfig { get; init; }
+ if (parseResult.GetValue(metricsOption))
+ MetricsGenerator.Generate(docsPath);
- [CommandOption("--dbsize")]
- public bool GenerateDBSize { get; init; }
+ return 0;
+});
- [CommandOption("--jsonrpc")]
- public bool GenerateJsonRpc { get; init; }
+CliConfiguration cli = new(rootCommand);
- [CommandOption("--metrics")]
- public bool GenerateMetrics { get; init; }
-}
+return cli.Invoke(args);
diff --git a/tools/DocGen/Properties/launchSettings.json b/tools/DocGen/Properties/launchSettings.json
index 14e64bfc589..090d2b5ec38 100644
--- a/tools/DocGen/Properties/launchSettings.json
+++ b/tools/DocGen/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"DocGen": {
"commandName": "Project",
- "commandLineArgs": "/path/to/docs --config --dbsize --jsonrpc --metrics"
+ "commandLineArgs": "/path/to/docs --config --jsonrpc --metrics"
}
}
}
diff --git a/tools/HiveCompare/HiveCompare/HiveCompare.csproj b/tools/HiveCompare/HiveCompare/HiveCompare.csproj
index aa57c232794..61ac6a5d41c 100644
--- a/tools/HiveCompare/HiveCompare/HiveCompare.csproj
+++ b/tools/HiveCompare/HiveCompare/HiveCompare.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/tools/HiveCompare/HiveCompare/Program.cs b/tools/HiveCompare/HiveCompare/Program.cs
index 2107db69bd1..5da4acd2f65 100644
--- a/tools/HiveCompare/HiveCompare/Program.cs
+++ b/tools/HiveCompare/HiveCompare/Program.cs
@@ -1,5 +1,5 @@
using HiveCompare.Models;
-using McMaster.Extensions.CommandLineUtils;
+using System.CommandLine;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
@@ -13,53 +13,40 @@ internal class Program
private static void Main(string[] args)
{
- CommandLineApplication cli = CreateCommandLineInterface();
- try
+ CliOption firstFileOption = new("--first-file", "-f")
{
- cli.Execute(args);
- }
- catch (CommandParsingException)
+ Description = "The first file to be used for comparison",
+ Required = true,
+ HelpName = "path"
+ };
+ CliOption secondFileOption = new("--second-file", "-s")
{
- cli.ShowHelp();
- }
- }
-
- static CommandLineApplication CreateCommandLineInterface()
- {
- CommandLineApplication cli = new() { Name = "HiveCompare" };
- cli.HelpOption("-?|-h|--help");
- CommandOption firstFileOption = cli.Option("-f|--first-file", "first file to be used for comparison", CommandOptionType.SingleValue);
- CommandOption secondFileOption = cli.Option("-s|--second-file", "second file to be used for comparison", CommandOptionType.SingleValue);
+ Description = "The second file to be used for comparison",
+ Required = true,
+ HelpName = "path"
+ };
+ CliRootCommand rootCommand = [firstFileOption, secondFileOption];
- cli.OnExecute(() =>
+ rootCommand.SetAction(parseResult =>
{
- bool HasRequiredOption(CommandOption option)
+ static bool RequiredFileExists(string? filePath)
{
- if (option.HasValue() && !string.IsNullOrEmpty(option.Value())) return true;
+ if (File.Exists(filePath)) return true;
- cli.ShowHelp();
+ Console.WriteLine($"Could not find file '{filePath}'.");
return false;
}
- bool RequiredFileExists(CommandOption option)
- {
- if (File.Exists(option.Value())) return true;
-
- Console.WriteLine($"Could not find file '{option.Value()}'.");
- return false;
-
- }
+ string? firstFileValue = parseResult.GetValue(firstFileOption);
+ string? secondFileValue = parseResult.GetValue(secondFileOption);
- return HasRequiredOption(firstFileOption) && HasRequiredOption(secondFileOption)
- ? RequiredFileExists(firstFileOption) && RequiredFileExists(secondFileOption)
- ? ParseTests(firstFileOption.Value()!, secondFileOption.Value()!)
- ? 0
- : 4
- : 2
- : 1;
+ return RequiredFileExists(firstFileValue) && RequiredFileExists(secondFileValue)
+ ? ParseTests(firstFileValue!, secondFileValue!) ? 0 : 4
+ : 2;
});
- return cli;
+ CliConfiguration cli = new(rootCommand);
+ cli.Invoke(args);
}
private static bool ParseTests(string firstFile, string secondFile)
diff --git a/tools/Nethermind.Tools.Kute/Config.cs b/tools/Nethermind.Tools.Kute/Config.cs
index a6467c30312..63cba1a916b 100644
--- a/tools/Nethermind.Tools.Kute/Config.cs
+++ b/tools/Nethermind.Tools.Kute/Config.cs
@@ -1,135 +1,80 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
-using CommandLine;
using Nethermind.Tools.Kute.MetricsConsumer;
+using System.CommandLine;
namespace Nethermind.Tools.Kute;
-public class Config
+public static class Config
{
- [Option(
- shortName: 'i',
- longName: "input",
+ public static CliOption MessagesFilePath { get; } = new("--input", "-i")
+ {
+ Description = "Path to a file or directory containing JSON RPC messages",
+ HelpName = "path",
Required = true,
- HelpText = "Path to a Folder or a File containing JSON RPC messages"
- )]
- public string MessagesFilePath { get; }
-
- [Option(
- shortName: 'a',
- longName: "address",
- Required = false,
- Default = "http://localhost:8551",
- HelpText = "Address where to send JSON RPC requests"
- )]
- public string HostAddress { get; }
+ };
- [Option(
- shortName: 's',
- longName: "secret",
- Required = true,
- HelpText = "Path to File with hex encoded secret for JWT authentication"
- )]
- public string JwtSecretFilePath { get; }
+ public static CliOption HostAddress { get; } = new("--address", "-a")
+ {
+ DefaultValueFactory = r => "http://localhost:8551",
+ Description = "Address where to send JSON RPC requests",
+ HelpName = "URL"
+ };
- [Option(
- shortName: 't',
- longName: "ttl",
- Required = false,
- Default = 60,
- HelpText = "Authentication time to live (ttl) in seconds"
- )]
- public int AuthTtl { get; }
+ public static CliOption JwtSecretFilePath { get; } = new("--secret", "-s")
+ {
+ Description = "Path to file with hex-encoded secret for JWT authentication",
+ HelpName = "value",
+ Required = true
+ };
- [Option(
- shortName: 'd',
- longName: "dry",
- Required = false,
- Default = false,
- HelpText = "Only log into console"
- )]
- public bool DryRun { get; }
+ public static CliOption AuthTtl { get; } = new("--ttl", "-t")
+ {
+ DefaultValueFactory = r => 60,
+ Description = "Authentication time to live (TTL), in seconds",
+ HelpName = "value"
+ };
- [Option(
- shortName: 'p',
- longName: "progress",
- Required = false,
- Default = false,
- HelpText = "Show progress"
- )]
- public bool ShowProgress { get; }
+ public static CliOption DryRun { get; } = new("--dry", "-d")
+ {
+ Description = "Only log into console"
+ };
- [Option(
- shortName: 'o',
- longName: "output",
- Required = false,
- Default = MetricsOutputFormatter.Report,
- HelpText = "Strategy to report metrics"
- )]
- public MetricsOutputFormatter MetricsOutputFormatter { get; }
+ public static CliOption ShowProgress { get; } = new("--progress", "-p")
+ {
+ Description = "Show progress"
+ };
- [Option(
- shortName: 'f',
- longName: "filters",
- Separator = ',',
- Required = false,
- Default = new string[] { },
- HelpText = "A comma separated List of regexes of methods to be executed with optional limits"
- )]
- public IEnumerable MethodFilters { get; }
+ public static CliOption MetricsOutputFormatter { get; } = new("--output", "-o")
+ {
+ DefaultValueFactory = r => MetricsConsumer.MetricsOutputFormatter.Report,
+ Description = "Strategy to report metrics",
+ HelpName = "value",
+ };
- [Option(
- shortName: 'r',
- longName: "responses",
- Required = false,
- Default = null,
- HelpText = "Path to File to store JSON-RPC responses"
- )]
- public string? ResponsesTraceFile { get; }
+ public static CliOption> MethodFilters { get; } = new("--filters", "-f")
+ {
+ DefaultValueFactory = r => [],
+ CustomParser = r => r.Tokens.Count == 1 ? r.Tokens[0].Value.Split(',') : null,
+ Description = "A comma separated List of regexes of methods to be executed with optional limits",
+ HelpName = "value",
+ };
- [Option(
- shortName: 'e',
- longName: "rps",
- Required = false,
- Default = 0,
- HelpText = "If set to higher than 0, then requests will be send in selected RPS (Requests per seconds) rate. If 0 (or lower) then requests will be sent sequentionally."
- )]
- public int RequestsPerSecond { get; }
+ public static CliOption ResponsesTraceFile { get; } = new("--responses", "-r")
+ {
+ Description = "Path to file to store JSON-RPC responses",
+ HelpName = "path"
+ };
- [Option(
- shortName: 'u',
- longName: "unwrapBatch",
- Required = false,
- Default = false,
- HelpText = "If true then each batched request will be unwraped to single requests."
- )]
- public bool UnwrapBatch { get; }
+ public static CliOption RequestsPerSecond { get; } = new("--rps", "-e")
+ {
+ Description = "If set to higher than 0, then requests will be send in selected RPS (Requests per seconds) rate. If 0 (or lower) then requests will be sent sequentially",
+ HelpName = "value"
+ };
- public Config(
- string messagesFilePath,
- string hostAddress,
- string jwtSecretFilePath,
- int authTtl,
- bool dryRun,
- bool showProgress,
- MetricsOutputFormatter metricsOutputFormatter,
- IEnumerable methodFilters,
- string? responsesTraceFile,
- int requestsPerSecond,
- bool unwrapBatch
- )
+ public static CliOption UnwrapBatch { get; } = new("--unwrapBatch", "-u")
{
- MessagesFilePath = messagesFilePath;
- HostAddress = hostAddress;
- JwtSecretFilePath = jwtSecretFilePath;
- AuthTtl = authTtl;
- DryRun = dryRun;
- ShowProgress = showProgress;
- MetricsOutputFormatter = metricsOutputFormatter;
- MethodFilters = methodFilters;
- ResponsesTraceFile = responsesTraceFile;
- RequestsPerSecond = requestsPerSecond;
- UnwrapBatch = unwrapBatch;
- }
+ Description = "If true then each batched request will be unwraped to single requests"
+ };
}
diff --git a/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj b/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj
index 0e5870bdf32..32fb32af4b3 100644
--- a/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj
+++ b/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj
@@ -9,9 +9,9 @@
-
+
diff --git a/tools/Nethermind.Tools.Kute/Program.cs b/tools/Nethermind.Tools.Kute/Program.cs
index 3e5157cfcc9..67b6837d0c6 100644
--- a/tools/Nethermind.Tools.Kute/Program.cs
+++ b/tools/Nethermind.Tools.Kute/Program.cs
@@ -1,7 +1,6 @@
using App.Metrics.Formatters;
using App.Metrics.Formatters.Ascii;
using App.Metrics.Formatters.Json;
-using CommandLine;
using Microsoft.Extensions.DependencyInjection;
using Nethermind.Tools.Kute.Auth;
using Nethermind.Tools.Kute.FlowManager;
@@ -15,30 +14,52 @@
using Nethermind.Tools.Kute.ResponseTracer;
using Nethermind.Tools.Kute.SecretProvider;
using Nethermind.Tools.Kute.SystemClock;
+using System.CommandLine;
namespace Nethermind.Tools.Kute;
static class Program
{
- public static async Task Main(string[] args)
+ public static async Task Main(string[] args)
{
- await Parser.Default.ParseArguments(args).WithParsedAsync(async config =>
+ CliRootCommand rootCommand =
+ [
+ Config.MessagesFilePath,
+ Config.HostAddress,
+ Config.JwtSecretFilePath,
+ Config.AuthTtl,
+ Config.DryRun,
+ Config.ShowProgress,
+ Config.MetricsOutputFormatter,
+ Config.MethodFilters,
+ Config.ResponsesTraceFile,
+ Config.RequestsPerSecond,
+ Config.UnwrapBatch
+ ];
+ rootCommand.SetAction((parseResult, cancellationToken) =>
{
- IServiceProvider serviceProvider = BuildServiceProvider(config);
+ IServiceProvider serviceProvider = BuildServiceProvider(parseResult);
Application app = serviceProvider.GetService()!;
- await app.Run();
+ return app.Run();
});
+
+ CliConfiguration cli = new(rootCommand);
+
+ return await cli.InvokeAsync(args);
}
- static IServiceProvider BuildServiceProvider(Config config)
+ private static IServiceProvider BuildServiceProvider(ParseResult parseResult)
{
+ bool dryRun = parseResult.GetValue(Config.DryRun);
+ bool unwrapBatch = parseResult.GetValue(Config.UnwrapBatch);
+ string? responsesTraceFile = parseResult.GetValue(Config.ResponsesTraceFile);
IServiceCollection collection = new ServiceCollection();
collection.AddSingleton();
collection.AddSingleton();
collection.AddSingleton();
- collection.AddSingleton(new FileSecretProvider(config.JwtSecretFilePath));
+ collection.AddSingleton(new FileSecretProvider(parseResult.GetValue(Config.JwtSecretFilePath)!));
collection.AddSingleton(provider =>
new TtlAuth(
new JwtAuth(
@@ -46,42 +67,39 @@ static IServiceProvider BuildServiceProvider(Config config)
provider.GetRequiredService()
),
provider.GetRequiredService(),
- config.AuthTtl
+ parseResult.GetValue(Config.AuthTtl)
)
);
- collection.AddSingleton>(new FileMessageProvider(config.MessagesFilePath));
+ collection.AddSingleton>(new FileMessageProvider(parseResult.GetValue(Config.MessagesFilePath)!));
collection.AddSingleton>(serviceProvider =>
{
var messageProvider = serviceProvider.GetRequiredService>();
var jsonMessageProvider = new JsonRpcMessageProvider(messageProvider);
- return config.UnwrapBatch
- ? new UnwrapBatchJsonRpcMessageProvider(jsonMessageProvider)
- : jsonMessageProvider;
+ return unwrapBatch ? new UnwrapBatchJsonRpcMessageProvider(jsonMessageProvider) : jsonMessageProvider;
});
- collection.AddSingleton(
- config.DryRun
- ? new NullJsonRpcValidator()
- : new ComposedJsonRpcValidator(new List
- {
- new NonErrorJsonRpcValidator(), new NewPayloadJsonRpcValidator(),
- })
+ collection.AddSingleton(dryRun
+ ? new NullJsonRpcValidator()
+ : new ComposedJsonRpcValidator(new List
+ {
+ new NonErrorJsonRpcValidator(), new NewPayloadJsonRpcValidator(),
+ })
);
collection.AddSingleton(
new ComposedJsonRpcMethodFilter(
- config.MethodFilters
+ parseResult.GetValue(Config.MethodFilters)!
.Select(pattern => new PatternJsonRpcMethodFilter(pattern) as IJsonRpcMethodFilter)
.ToList()
)
);
collection.AddSingleton(provider =>
{
- if (!config.DryRun)
+ if (!dryRun)
{
return new HttpJsonRpcSubmitter(
provider.GetRequiredService(),
provider.GetRequiredService(),
- config.HostAddress
+ parseResult.GetValue(Config.HostAddress)!
);
}
@@ -92,13 +110,13 @@ static IServiceProvider BuildServiceProvider(Config config)
return new NullJsonRpcSubmitter();
});
collection.AddSingleton(
- config is { DryRun: false, ResponsesTraceFile: not null }
- ? new FileResponseTracer(config.ResponsesTraceFile)
+ !dryRun && responsesTraceFile is not null
+ ? new FileResponseTracer(responsesTraceFile)
: new NullResponseTracer()
);
collection.AddSingleton(provider =>
{
- if (config.ShowProgress)
+ if (parseResult.GetValue(Config.ShowProgress))
{
// NOTE:
// Terrible, terrible hack since it forces a double enumeration:
@@ -107,7 +125,7 @@ static IServiceProvider BuildServiceProvider(Config config)
// We can reduce the cost by not parsing each message on the first enumeration
// only when we're not unwrapping batches. If we are, we need to parse.
// This optimization relies on implementation details.
- IMessageProvider