using System; using System.CommandLine; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StellaOps.Auth.Client; using StellaOps.Cli.Commands; using StellaOps.Cli.Configuration; using StellaOps.Cli.Services; using StellaOps.Cli.Telemetry; using StellaOps.AirGap.Policy; using StellaOps.Configuration; namespace StellaOps.Cli; internal static class Program { internal static async Task Main(string[] args) { var (options, configuration) = CliBootstrapper.Build(args); var services = new ServiceCollection(); services.AddSingleton(configuration); services.AddSingleton(options); services.AddOptions(); var verbosityState = new VerbosityState(); services.AddSingleton(verbosityState); services.AddAirGapEgressPolicy(configuration); services.AddStellaOpsCrypto(options.Crypto); services.AddLogging(builder => { builder.ClearProviders(); builder.AddSimpleConsole(logOptions => { logOptions.TimestampFormat = "HH:mm:ss "; logOptions.SingleLine = true; }); builder.AddFilter((category, level) => level >= verbosityState.MinimumLevel); }); if (!string.IsNullOrWhiteSpace(options.Authority.Url)) { services.AddStellaOpsAuthClient(clientOptions => { clientOptions.Authority = options.Authority.Url; clientOptions.ClientId = options.Authority.ClientId ?? string.Empty; clientOptions.ClientSecret = options.Authority.ClientSecret; clientOptions.DefaultScopes.Clear(); clientOptions.DefaultScopes.Add(string.IsNullOrWhiteSpace(options.Authority.Scope) ? StellaOps.Auth.Abstractions.StellaOpsScopes.ConcelierJobsTrigger : options.Authority.Scope); var resilience = options.Authority.Resilience ?? new StellaOpsCliAuthorityResilienceOptions(); clientOptions.EnableRetries = resilience.EnableRetries ?? true; if (resilience.RetryDelays is { Count: > 0 }) { clientOptions.RetryDelays.Clear(); foreach (var delay in resilience.RetryDelays) { if (delay > TimeSpan.Zero) { clientOptions.RetryDelays.Add(delay); } } } if (resilience.AllowOfflineCacheFallback.HasValue) { clientOptions.AllowOfflineCacheFallback = resilience.AllowOfflineCacheFallback.Value; } if (resilience.OfflineCacheTolerance.HasValue && resilience.OfflineCacheTolerance.Value >= TimeSpan.Zero) { clientOptions.OfflineCacheTolerance = resilience.OfflineCacheTolerance.Value; } }); var cacheDirectory = options.Authority.TokenCacheDirectory; if (!string.IsNullOrWhiteSpace(cacheDirectory)) { Directory.CreateDirectory(cacheDirectory); services.AddStellaOpsFileTokenCache(cacheDirectory); } services.AddHttpClient(client => { client.Timeout = TimeSpan.FromMinutes(2); if (Uri.TryCreate(options.Authority.Url, UriKind.Absolute, out var authorityUri)) { client.BaseAddress = authorityUri; } }).AddEgressPolicyGuard("stellaops-cli", "authority-revocation"); } services.AddHttpClient(client => { client.Timeout = TimeSpan.FromMinutes(5); if (!string.IsNullOrWhiteSpace(options.BackendUrl) && Uri.TryCreate(options.BackendUrl, UriKind.Absolute, out var backendUri)) { client.BaseAddress = backendUri; } }).AddEgressPolicyGuard("stellaops-cli", "backend-api"); services.AddHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(30); if (!string.IsNullOrWhiteSpace(options.ConcelierUrl) && Uri.TryCreate(options.ConcelierUrl, UriKind.Absolute, out var concelierUri)) { client.BaseAddress = concelierUri; } }).AddEgressPolicyGuard("stellaops-cli", "concelier-api"); services.AddHttpClient("stellaops-cli.ingest-download") .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All }) .AddEgressPolicyGuard("stellaops-cli", "sources-ingest"); services.AddSingleton(); services.AddSingleton(); await using var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService(); var startupLogger = loggerFactory.CreateLogger("StellaOps.Cli.Startup"); AuthorityDiagnosticsReporter.Emit(configuration, startupLogger); using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, eventArgs) => { eventArgs.Cancel = true; cts.Cancel(); }; var rootCommand = CommandFactory.Create(serviceProvider, options, cts.Token, loggerFactory); var commandConfiguration = new CommandLineConfiguration(rootCommand); int commandExit; try { commandExit = await commandConfiguration.InvokeAsync(args, cts.Token).ConfigureAwait(false); } catch (AirGapEgressBlockedException ex) { var guardLogger = loggerFactory.CreateLogger("StellaOps.Cli.AirGap"); guardLogger.LogError("{ErrorCode}: {Reason} Remediation: {Remediation}", AirGapEgressBlockedException.ErrorCode, ex.Reason, ex.Remediation); if (!string.IsNullOrWhiteSpace(ex.DocumentationUrl)) { guardLogger.LogInformation("Documentation: {DocumentationUrl}", ex.DocumentationUrl); } if (!string.IsNullOrWhiteSpace(ex.SupportContact)) { guardLogger.LogInformation("Support contact: {SupportContact}", ex.SupportContact); } Console.Error.WriteLine(ex.Message); return 1; } var finalExit = Environment.ExitCode != 0 ? Environment.ExitCode : commandExit; if (cts.IsCancellationRequested && finalExit == 0) { finalExit = 130; // Typical POSIX cancellation exit code } return finalExit; } }