< Summary - ZhEaIsNsAaBn.Libraries coverage (affected: 23 packages)

Information
Class: ZhEaIsNsAaBn.DbSeeder.Tool.DbSeederTool
Assembly: ZhEaIsNsAaBn.DbSeeder.Tool
File(s): /home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/DataBasePackages/ZhEaIsNsAaBn.DbSeeder.Tool/DbSeederTool.cs
Line coverage
11%
Covered lines: 6
Uncovered lines: 45
Coverable lines: 51
Total lines: 132
Line coverage: 11.7%
Branch coverage
12%
Covered branches: 7
Total branches: 58
Branch coverage: 12%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Version()0%2040%
RunAsync()0%812280%
ExecArguments(...)0%7280%
ChildEnvironment(...)87.5%88100%
BuildAsync()0%7280%
Fail(...)100%210%
PathsEqual(...)0%620%

File(s)

/home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/DataBasePackages/ZhEaIsNsAaBn.DbSeeder.Tool/DbSeederTool.cs

#LineLine coverage
 1using System.Reflection;
 2
 3namespace ZhEaIsNsAaBn.DbSeeder.Tool;
 4
 5/// <summary>
 6/// <c>dotnet dbseeder</c>, the way <c>dotnet ef</c> works: build the startup project, check its Design version, then
 7/// <c>dotnet exec --depsfile App.deps.json --runtimeconfig App.runtimeconfig.json --additionalprobingpath … bootstrap.d
 8/// so the commands run on the app's own EF Core / provider / Design. Exit code: the command's, or 2 for a tool error.
 9/// </summary>
 10internal static class DbSeederTool
 11{
 12    public const int ToolError = 2;
 13
 14    public const string BootstrapFile = "ZhEaIsNsAaBn.DbSeeder.Tool.Bootstrap.dll";
 15
 16
 17    public static string Version
 018        => typeof( DbSeederTool ).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVer
 019        ?? typeof( DbSeederTool ).Assembly.GetName().Version!.ToString( 3 );
 20
 21
 22    public static async Task<int> RunAsync( IReadOnlyList<string> args, TextWriter output, TextWriter errors, string cur
 23    {
 024        if ( !ToolOptions.TryParse( args, out var options, out var parseError ) ) { return Fail( errors, parseError ); }
 25
 026        if ( options.Version )
 27        {
 028            await output.WriteLineAsync( Version ).ConfigureAwait( false );
 29
 030            return 0;
 31        }
 32
 033        if ( options.Help || options.Command.Count == 0 )
 34        {
 035            await output.WriteLineAsync( ToolOptions.Usage ).ConfigureAwait( false );
 36
 037            return options.Help ? 0 : ToolError;
 38        }
 39
 40
 41        try
 42        {
 043            var process     = new ToolProcess( errors, options.Verbose );
 044            var projectFile = ProjectMetadata.ResolveProjectFile( options.Project, currentDirectory );
 045            var startupFile = options.StartupProject is null ? projectFile : ProjectMetadata.ResolveProjectFile( options
 046            var framework   = await ProjectMetadata.TargetFrameworkAsync( startupFile, options.Framework, options.Config
 47
 048            if ( !options.NoBuild ) { await BuildAsync( startupFile, framework, options, process, errors ).ConfigureAwai
 49
 050            var startup = await ProjectMetadata.ReadAsync( startupFile, framework, options.Configuration, process ).Conf
 051            var project = PathsEqual( projectFile, startupFile ) ? startup : await ProjectMetadata.ReadAsync( projectFil
 52
 053            if ( !File.Exists( startup.RuntimeConfigFile ) )
 54            {
 055                throw new ToolException( File.Exists( startup.TargetPath )
 056                                             ? $"{startup.Name} has no runtimeconfig.json. Use an executable startup pro
 057                                             : $"{startup.Name} isn't built ({startup.TargetPath}). Build it, or drop --
 58            }
 59
 060            var versionError = DesignVersionCheck.Check( Version, startup.Name, await File.ReadAllTextAsync( startup.Dep
 061                                                         project == startup ? null : project.Name,
 062                                                         project == startup || !File.Exists( project.DepsFile ) ? null :
 63
 064            if ( versionError is not null ) { throw new ToolException( versionError ); }
 65
 66
 067            return await process.RunAsync( ExecArguments( startup, project, options ), startup.ProjectDirectory, ChildEn
 68        }
 069        catch ( ToolException exception ) { return Fail( errors, exception.Message ); }
 070    }
 71
 72
 73    /// <summary>The <c>dotnet exec</c> command line for the bootstrap.</summary>
 74    public static List<string> ExecArguments( ProjectMetadata startup, ProjectMetadata project, ToolOptions options )
 75    {
 076        List<string> args = [ "exec", "--depsfile", startup.DepsFile, "--runtimeconfig", startup.RuntimeConfigFile ];
 77
 078        foreach ( var folder in startup.PackageFolders ) { args.AddRange( [ "--additionalprobingpath", folder ] ); }
 79
 080        args.AddRange( [ Path.Combine( AppContext.BaseDirectory, "bootstrap", BootstrapFile ), "--startup-assembly", sta
 81
 082        if ( project != startup ) { args.AddRange( [ "--project-assembly", project.TargetPath ] ); }
 83
 084        args.AddRange( [ "--project-dir", project.ProjectDirectory.TrimEnd( Path.DirectorySeparatorChar, Path.AltDirecto
 85
 086        if ( options.Context is not null ) { args.AddRange( [ "--context", options.Context ] ); }
 87
 088        foreach ( var arg in options.AppArgs ) { args.AddRange( [ "--app-arg", arg ] ); }
 89
 090        args.Add( "--" );
 091        args.AddRange( options.Command );
 92
 093        return args;
 94    }
 95
 96    /// <summary>
 97    /// <c>--environment</c> sets both variables; with neither set, both become <c>Development</c> (as <c>dotnet ef</c> 
 98    /// doesn't default to Production. An environment the user set is kept.
 99    /// </summary>
 100    public static Dictionary<string, string> ChildEnvironment( ToolOptions options )
 101    {
 12102        var environment = options.Environment
 12103                       ?? ( string.IsNullOrEmpty( System.Environment.GetEnvironmentVariable( "DOTNET_ENVIRONMENT" ) )
 12104                            && string.IsNullOrEmpty( System.Environment.GetEnvironmentVariable( "ASPNETCORE_ENVIRONMENT"
 12105                                ? "Development"
 12106                                : null );
 107
 12108        return environment is null ? [] : new Dictionary<string, string> { [ "DOTNET_ENVIRONMENT" ] = environment, [ "AS
 109    }
 110
 111
 112    private static async Task BuildAsync( string startupFile, string framework, ToolOptions options, ToolProcess process
 113    {
 0114        if ( options.Verbose ) { await errors.WriteLineAsync( $"Building {Path.GetFileName( startupFile )} ({framework},
 115
 0116        var (code, output) = await process.CaptureAsync( [ "build", startupFile, "-c", options.Configuration, "-f", fram
 117
 0118        if ( options.Verbose || code != 0 ) { await errors.WriteLineAsync( output ).ConfigureAwait( false ); }
 119
 0120        if ( code != 0 ) { throw new ToolException( $"Build failed: {Path.GetFileName( startupFile )}." ); }
 0121    }
 122
 123    private static int Fail( TextWriter errors, string message )
 124    {
 0125        errors.WriteLine( $"error: {message}" );
 126
 0127        return ToolError;
 128    }
 129
 130    private static bool PathsEqual( string left, string right )
 0131        => string.Equals( Path.GetFullPath( left ), Path.GetFullPath( right ), OperatingSystem.IsWindows() ? StringCompa
 132}