diff --git a/DevProxy/Commands/DevProxyCommand.cs b/DevProxy/Commands/DevProxyCommand.cs index 1740df9f1..3832bba5f 100644 --- a/DevProxy/Commands/DevProxyCommand.cs +++ b/DevProxy/Commands/DevProxyCommand.cs @@ -2,6 +2,7 @@ using DevProxy.Abstractions.Proxy; using DevProxy.Abstractions.Utils; using System.CommandLine; +using System.CommandLine.Help; using System.CommandLine.Parsing; using System.Globalization; @@ -217,7 +218,17 @@ public async Task InvokeAsync(string[] args, WebApplication app) var parseResult = IsStdioCommand ? StdioCommand.ParseStdioArgs(this, args) : Parse(args); - return await parseResult.InvokeAsync(app.Lifetime.ApplicationStopping); + var exitCode = await parseResult.InvokeAsync(app.Lifetime.ApplicationStopping); + + // Return exit code 2 for input validation and parse errors to distinguish + // them from runtime errors (exit code 1), following conventions from + // curl, git, and others + if (exitCode != 0 && parseResult.Errors.Count > 0) + { + return 2; + } + + return exitCode; } private async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken) @@ -521,6 +532,12 @@ private void ConfigureCommand() commands.AddRange(_plugins.SelectMany(p => p.GetCommands())); this.AddCommands(commands.OrderByName()); + var helpOption = Options.OfType().FirstOrDefault(); + if (helpOption?.Action is HelpAction helpAction) + { + helpOption.Action = new ExitCodeHelpAction(helpAction); + } + SetAction(InvokeAsync); } diff --git a/DevProxy/Commands/ExitCodeHelpAction.cs b/DevProxy/Commands/ExitCodeHelpAction.cs new file mode 100644 index 000000000..fae60abae --- /dev/null +++ b/DevProxy/Commands/ExitCodeHelpAction.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Invocation; + +namespace DevProxy.Commands; + +sealed class ExitCodeHelpAction : SynchronousCommandLineAction +{ + private readonly HelpAction _originalAction; + + public ExitCodeHelpAction(HelpAction originalAction) + { + _originalAction = originalAction; + Terminating = true; + } + + public override int Invoke(ParseResult parseResult) + { + var result = _originalAction.Invoke(parseResult); + + var output = parseResult.Configuration.Output; + output.WriteLine(); + output.WriteLine("Exit codes:"); + output.WriteLine(" 0 Success"); + output.WriteLine(" 1 Runtime error"); + output.WriteLine(" 2 Invalid input or usage"); + + return result; + } +}