| | | 1 | | using System.Reflection; |
| | | 2 | | using Spectre.Console.Cli; |
| | | 3 | | |
| | | 4 | | namespace NGql.Tool; |
| | | 5 | | |
| | | 6 | | internal static class Program |
| | | 7 | | { |
| | | 8 | | public static async Task<int> Main(string[] args) |
| | | 9 | | { |
| | | 10 | | // Handle --version and -v before delegating to Spectre, since Spectre 0.49 doesn't |
| | | 11 | | // automatically fire the built-in version path when the default command claims unknown |
| | | 12 | | // args. Doing it here keeps the contract: `ngql --version` prints a single semver line |
| | | 13 | | // on stdout and exits 0. |
| | 177 | 14 | | if (args.Length == 1 && (args[0] == "--version" || args[0] == "-v")) |
| | | 15 | | { |
| | 9 | 16 | | Console.WriteLine(GetVersion()); |
| | 9 | 17 | | return 0; |
| | | 18 | | } |
| | | 19 | | |
| | 168 | 20 | | args = EscapeStdInArgument(args); |
| | | 21 | | |
| | 168 | 22 | | var app = new CommandApp<RenderCommand>(); |
| | 168 | 23 | | app.Configure(config => |
| | 168 | 24 | | { |
| | 168 | 25 | | config.SetApplicationName("ngql"); |
| | 168 | 26 | | config.SetApplicationVersion(GetVersion()); |
| | 168 | 27 | | |
| | 168 | 28 | | // Internal subcommands used by the project's regression harness (`make skill-eval`). |
| | 168 | 29 | | // Hidden from --help but discoverable via direct invocation. |
| | 168 | 30 | | config.AddCommand<FixturesCommand>("fixtures") |
| | 168 | 31 | | .IsHidden() |
| | 168 | 32 | | .WithDescription("Run all built-in regression fixtures (contributor use only)."); |
| | 168 | 33 | | config.AddCommand<FixtureCommand>("fixture") |
| | 168 | 34 | | .IsHidden() |
| | 168 | 35 | | .WithDescription("Run a single named regression fixture (contributor use only)."); |
| | 168 | 36 | | |
| | 168 | 37 | | config.PropagateExceptions(); |
| | 336 | 38 | | }); |
| | | 39 | | |
| | | 40 | | try |
| | | 41 | | { |
| | 168 | 42 | | return await app.RunAsync(args); |
| | | 43 | | } |
| | 9 | 44 | | catch (CommandRuntimeException ex) |
| | | 45 | | { |
| | | 46 | | // Spectre's parse / validation / runtime failures (CommandParseException derives from this). |
| | 9 | 47 | | await Console.Error.WriteLineAsync($"ngql: {ex.Message}"); |
| | 9 | 48 | | await Console.Error.WriteLineAsync("See `ngql --help`."); |
| | 9 | 49 | | return ExitCodes.InvalidUsage; |
| | | 50 | | } |
| | 177 | 51 | | } |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// Spectre 0.49 reads a bare "-" as a malformed option ("Option does not have a name") rather |
| | | 55 | | /// than as the documented stdin argument, so it never reaches the command. Swap it for the |
| | | 56 | | /// marker the command recognises before parsing. |
| | | 57 | | /// </summary> |
| | | 58 | | private static string[] EscapeStdInArgument(string[] args) |
| | | 59 | | { |
| | 168 | 60 | | var dash = Array.IndexOf(args, "-"); |
| | 168 | 61 | | if (dash < 0) |
| | 162 | 62 | | return args; |
| | | 63 | | |
| | 6 | 64 | | var escaped = (string[])args.Clone(); |
| | 6 | 65 | | escaped[dash] = RenderCommand.StdInMarker; |
| | 6 | 66 | | return escaped; |
| | | 67 | | } |
| | | 68 | | |
| | 177 | 69 | | private static string GetVersion() => GetVersion(typeof(Program).Assembly); |
| | | 70 | | |
| | | 71 | | public static string GetVersion(Assembly asm) |
| | | 72 | | { |
| | 189 | 73 | | var info = asm.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); |
| | | 74 | | // AssemblyName.Version is always populated for a loaded assembly, so no fallback is needed. |
| | 189 | 75 | | var v = info.Length > 0 |
| | 189 | 76 | | ? ((AssemblyInformationalVersionAttribute)info[0]).InformationalVersion |
| | 189 | 77 | | : asm.GetName().Version!.ToString(); |
| | | 78 | | |
| | | 79 | | // Strip the "+<commit-sha>" SourceLink suffix. |
| | 189 | 80 | | var plus = v.IndexOf('+'); |
| | 189 | 81 | | return plus > 0 ? v[..plus] : v; |
| | | 82 | | } |
| | | 83 | | } |
| | | 84 | | |
| | | 85 | | internal static class ExitCodes |
| | | 86 | | { |
| | | 87 | | public const int Ok = 0; |
| | | 88 | | public const int RenderFailed = 1; |
| | | 89 | | public const int GraphQlErrors = 2; |
| | | 90 | | public const int HttpFailure = 3; |
| | | 91 | | public const int MutationBlocked = 4; |
| | | 92 | | public const int InvalidUsage = 64; |
| | | 93 | | public const int FileNotFound = 66; |
| | | 94 | | } |