< Summary

Information
Class: DotNetApiDiff.Program
Assembly: DotNetApiDiff
File(s): /home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/Program.cs
Line coverage
92%
Covered lines: 89
Uncovered lines: 7
Coverable lines: 96
Total lines: 161
Line coverage: 92.7%
Branch coverage
87%
Covered branches: 7
Total branches: 8
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Main(...)100%2295.91%
GetBuildTime()50%2264.28%
ConfigureServices(...)100%44100%

File(s)

/home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/Program.cs

#LineLine coverage
 1// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
 2using DotNetApiDiff.Commands;
 3using DotNetApiDiff.ExitCodes;
 4using DotNetApiDiff.Interfaces;
 5using Microsoft.Extensions.DependencyInjection;
 6using Microsoft.Extensions.Logging;
 7using Spectre.Console.Cli;
 8
 9namespace DotNetApiDiff;
 10
 11/// <summary>
 12/// Main entry point for the DotNet API Diff tool
 13/// </summary>
 14public class Program
 15{
 16    /// <summary>
 17    /// Application entry point
 18    /// </summary>
 19    /// <param name="args">Command line arguments</param>
 20    /// <returns>Exit code (0 for success, non-zero for failure)</returns>
 21    public static int Main(string[] args)
 922    {
 23        // Set up dependency injection
 924        var services = new ServiceCollection();
 925        ConfigureServices(services);
 26
 927        var serviceProvider = services.BuildServiceProvider();
 928        var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
 929        var exceptionHandler = serviceProvider.GetRequiredService<IGlobalExceptionHandler>();
 30
 31        // Set up global exception handling
 932        exceptionHandler.SetupGlobalExceptionHandling();
 33
 34        try
 935        {
 36            // Log application start with version information
 937            var version = typeof(Program).Assembly.GetName().Version;
 938            var buildTime = GetBuildTime();
 39
 940            logger.LogInformation(
 941                "DotNet API Diff Tool v{Version} started at {Time} (Build: {BuildTime})",
 942                version,
 943                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
 944                buildTime);
 45
 46            // Log environment information
 947            logger.LogDebug(
 948                "Environment: OS={OS}, Framework={Framework}, ProcessorCount={ProcessorCount}",
 949                Environment.OSVersion,
 950                Environment.Version,
 951                Environment.ProcessorCount);
 52
 53            // Get the exit code manager
 954            var exitCodeManager = serviceProvider.GetRequiredService<IExitCodeManager>();
 55
 56            // Configure Spectre.Console command app
 957            var app = new CommandApp(new TypeRegistrar(serviceProvider));
 58
 959            app.Configure(config =>
 960            {
 961                config.SetApplicationName("dotnet-api-diff");
 962                config.PropagateExceptions();
 963
 964                config.AddExample(new[] { "compare", "source.dll", "target.dll" });
 965                config.AddExample(new[] { "compare", "source.dll", "target.dll", "--output", "json" });
 966                config.AddExample(new[] { "compare", "source.dll", "target.dll", "--config", "config.json" });
 967
 968                config.SetExceptionHandler(ex =>
 069                {
 970                    // Use our centralized exception handler
 071                    return exceptionHandler.HandleException(ex, "Command execution");
 972                });
 973
 974                // Register the compare command
 975                config.AddCommand<CompareCommand>("compare")
 976                    .WithDescription("Compare two .NET assemblies and report API differences")
 977                    .WithExample(new[] { "compare", "source.dll", "target.dll" })
 978                    .WithExample(new[] { "compare", "source.dll", "target.dll", "--output", "json" })
 979                    .WithExample(new[] { "compare", "source.dll", "target.dll", "--filter", "System.Collections" });
 1880            });
 81
 82            // Run the command and return the exit code
 983            return app.Run(args);
 84        }
 385        catch (Exception ex)
 386        {
 87            // Use our centralized exception handler for any unhandled exceptions
 388            return exceptionHandler.HandleException(ex, "Application startup");
 89        }
 990    }
 91
 92    /// <summary>
 93    /// Gets the build time of the assembly from the file timestamp
 94    /// </summary>
 95    /// <returns>Build time as a string</returns>
 96    private static string GetBuildTime()
 1097    {
 98        try
 1099        {
 10100            var assembly = typeof(Program).Assembly;
 10101            var assemblyLocation = assembly.Location;
 10102            if (!string.IsNullOrEmpty(assemblyLocation))
 10103            {
 10104                var buildTime = File.GetLastWriteTime(assemblyLocation);
 10105                return buildTime.ToString("yyyy-MM-dd HH:mm:ss");
 106            }
 0107        }
 0108        catch (Exception)
 0109        {
 110            // Ignore errors getting build time
 0111        }
 112
 0113        return "Unknown";
 10114    }
 115
 116    /// <summary>
 117    /// Configures dependency injection services for the root container
 118    /// Only includes services needed for command instantiation
 119    /// </summary>
 120    /// <param name="services">Service collection to configure</param>
 121    public static void ConfigureServices(IServiceCollection services)
 25122    {
 123        // Configure logging with structured logging support
 25124        services.AddLogging(builder =>
 25125        {
 25126            builder.AddConsole(options =>
 24127            {
 25128                // Use ConsoleFormatterOptions instead of deprecated ConsoleLoggerOptions
 24129                options.FormatterName = "simple";
 49130            });
 25131
 25132            // Configure the simple console formatter
 25133            builder.AddSimpleConsole(options =>
 24134            {
 24135                options.IncludeScopes = true;
 24136                options.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] ";
 24137                options.SingleLine = false;
 24138                options.UseUtcTimestamp = false;
 49139            });
 25140
 25141            // Set minimum level based on environment variable if present
 25142            var logLevelEnv = Environment.GetEnvironmentVariable("DOTNET_API_DIFF_LOG_LEVEL");
 25143            var logLevel = LogLevel.Information; // Default level
 25144
 25145            if (!string.IsNullOrEmpty(logLevelEnv) &&
 25146                Enum.TryParse<LogLevel>(logLevelEnv, true, out var parsedLevel))
 6147            {
 6148                logLevel = parsedLevel;
 6149            }
 25150
 25151            builder.SetMinimumLevel(logLevel);
 50152        });
 153
 154        // Register only services needed for command instantiation
 25155        services.AddSingleton<IExitCodeManager, ExitCodeManager>();
 25156        services.AddSingleton<IGlobalExceptionHandler, GlobalExceptionHandler>();
 157
 158        // Note: Business logic services (AssemblyLoader, ApiExtractor, etc.) will be registered
 159        // in the command-specific container to avoid configuration timing issues
 25160    }
 161}