예제 #1
0
파일: Output.cs 프로젝트: lpedras/bullseye
 public Output(TextWriter writer, Palette palette)
 {
     this.writer = writer;
     this.p      = palette;
 }
예제 #2
0
 public Logger(TextWriter writer, bool skipDependencies, bool dryRun, bool parallel, Palette palette, bool verbose)
 {
     this.writer           = writer;
     this.skipDependencies = skipDependencies;
     this.dryRun           = dryRun;
     this.parallel         = parallel;
     this.p       = palette;
     this.verbose = verbose;
 }
예제 #3
0
 // pad right printed
 public static string Prp(this string text, int totalWidth, char paddingChar) =>
 text.PadRight(totalWidth + (text.Length - Palette.StripColours(text).Length), paddingChar);
예제 #4
0
        private static string ToString(this TargetCollection targets, bool listDependencies, bool listInputs, Palette p)
        {
            var value = new StringBuilder();

            foreach (var target in targets.OrderBy(target => target.Name))
            {
                value.AppendLine($"{p.Target}{target.Name}{p.Default}");

                if (listDependencies)
                {
                    var writeHeader = listInputs;
                    var indent      = writeHeader ? "    " : "  ";
                    foreach (var dependency in target.Dependencies)
                    {
                        if (writeHeader)
                        {
                            value.AppendLine($"  {p.Label}Dependencies:{p.Default}");
                            writeHeader = false;
                        }

                        value.AppendLine($"{indent}{p.Dependency}{dependency}{(targets.Contains(dependency) ? "" : $" {p.Failed}(missing)")}{p.Default}");
                    }
                }

                if (listInputs)
                {
                    var writeHeader = listDependencies;
                    var indent      = writeHeader ? "    " : "  ";
                    if (target is IHaveInputs hasInputs)
                    {
                        foreach (var input in hasInputs.Inputs)
                        {
                            if (writeHeader)
                            {
                                value.AppendLine($"  {p.Label}Inputs:{p.Default}");
                                writeHeader = false;
                            }

                            value.AppendLine($"{indent}{p.Input}{input}{p.Default}");
                        }
                    }
                }
            }
예제 #5
0
        private async Task Results()
        {
            // whitespace (e.g. can change to '·' for debugging)
            var ws = ' ';

            var totalDuration = results.Sum(i => i.Value.DurationMilliseconds ?? 0 + i.Value.InputResults.Sum(i2 => i2.DurationMilliseconds));

            var rows = new List <Tuple <string, string, string, string> > {
                Tuple.Create($"{p.Label}Duration", "", $"{p.Label}Outcome", $"{p.Label}Target")
            };

            foreach (var item in results.OrderBy(i => i.Value.DurationMilliseconds))
            {
                var duration = $"{p.Timing}{ToStringFromMilliseconds(item.Value.DurationMilliseconds, true)}";

                var percentage = item.Value.DurationMilliseconds.HasValue && totalDuration > 0
                    ? $"{p.Timing}{100 * item.Value.DurationMilliseconds / totalDuration:N1}%"
                    : "";

                var outcome = item.Value.Outcome == TargetOutcome.Failed
                    ? $"{p.Failed}Failed!"
                    : item.Value.Outcome == TargetOutcome.NoInputs
                        ? $"{p.Warning}No inputs!"
                        : $"{p.Succeeded}Succeeded";

                var target = $"{p.Target}{item.Key}";

                rows.Add(Tuple.Create(duration, percentage, outcome, target));

                var index = 0;

                foreach (var result in item.Value.InputResults.OrderBy(r => r.DurationMilliseconds))
                {
                    var inputDuration = $"{p.Tree}{(index < item.Value.InputResults.Count - 1 ? p.TreeFork : p.TreeCorner)}{p.Timing}{ToStringFromMilliseconds(result.DurationMilliseconds, true)}";

                    var inputPercentage = totalDuration > 0
                        ? $"{p.Tree}{(index < item.Value.InputResults.Count - 1 ? p.TreeFork : p.TreeCorner)}{p.Timing}{100 * result.DurationMilliseconds / totalDuration:N1}%"
                        : "";

                    var inputOutcome = result.Outcome == TargetInputOutcome.Failed ? $"{p.Failed}Failed!" : $"{p.Succeeded}Succeeded";

                    var input = $"{ws}{ws}{p.Input}{result.Input.ToString()}";

                    rows.Add(Tuple.Create(inputDuration, inputPercentage, inputOutcome, input));

                    ++index;
                }
            }

            // time column width
            var timW = rows.Count > 1 ? rows.Skip(1).Max(row => Palette.StripColours(row.Item1).Length) : 0;

            // percentage column width
            var perW = rows.Max(row => Palette.StripColours(row.Item2).Length);

            // duration column width (time and percentage)
            var durW = Max(Palette.StripColours(rows[0].Item1).Length, timW + 2 + perW);

            // expand percentage column width to ensure time and percentage are as wide as duration
            perW = Max(durW - timW - 2, perW);

            // outcome column width
            var outW = rows.Max(row => Palette.StripColours(row.Item3).Length);

            // target name column width
            var tarW = rows.Max(row => Palette.StripColours(row.Item4).Length);

            // summary start separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Symbol}{"".Prp(durW + 2 + outW + 2 + tarW, p.Dash)}").Tax();

            // header
            await this.writer.WriteLineAsync($"{GetPrefix()}{rows[0].Item1.Prp(durW, ws)}{ws}{ws}{rows[0].Item3.Prp(outW, ws)}{ws}{ws}{rows[0].Item4.Prp(tarW, ws)}").Tax();

            // header separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Symbol}{"".Prp(durW, p.Dash)}{ws}{ws}{"".Prp(outW, p.Dash)}{ws}{ws}{"".Prp(tarW, p.Dash)}").Tax();

            // targets
            foreach (var row in rows.Skip(1))
            {
                await this.writer.WriteLineAsync($"{GetPrefix()}{row.Item1.Prp(timW, ws)}{ws}{ws}{row.Item2.Prp(perW, ws)}{ws}{ws}{row.Item3.Prp(outW, ws)}{ws}{ws}{row.Item4.Prp(tarW, ws)}").Tax();
            }

            // summary end separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Symbol}{"".Prp(durW + 2 + outW + 2 + tarW, p.Dash)}{p.Default}").Tax();
        }
예제 #6
0
        private static async Task RunAsync(this TargetCollection targets, List <string> args, IConsole console)
        {
            var clear            = false;
            var dryRun           = false;
            var listDependencies = false;
            var listInputs       = false;
            var listTargets      = false;
            var noColor          = false;
            var parallel         = false;
            var skipDependencies = false;
            var verbose          = false;
            var host             = Host.Unknown;
            var showHelp         = false;

            var helpOptions    = new[] { "--help", "-h", "-?" };
            var optionsArgs    = args.Where(arg => arg.StartsWith("-", StringComparison.Ordinal)).ToList();
            var unknownOptions = new List <string>();

            foreach (var option in optionsArgs)
            {
                switch (option)
                {
                case "-c":
                case "--clear":
                    clear = true;
                    break;

                case "-n":
                case "--dry-run":
                    dryRun = true;
                    break;

                case "-D":
                case "--list-dependencies":
                    listDependencies = true;
                    break;

                case "-I":
                case "--list-inputs":
                    listInputs = true;
                    break;

                case "-T":
                case "--list-targets":
                    listTargets = true;
                    break;

                case "-N":
                case "--no-color":
                    noColor = true;
                    break;

                case "-p":
                case "--parallel":
                    parallel = true;
                    break;

                case "-s":
                case "--skip-dependencies":
                    skipDependencies = true;
                    break;

                case "-v":
                case "--verbose":
                    verbose = true;
                    break;

                case "--appveyor":
                    host = Host.Appveyor;
                    break;

                case "--travis":
                    host = Host.Travis;
                    break;

                case "--teamcity":
                    host = Host.TeamCity;
                    break;

                default:
                    if (helpOptions.Contains(option, StringComparer.OrdinalIgnoreCase))
                    {
                        showHelp = true;
                    }
                    else
                    {
                        unknownOptions.Add(option);
                    }

                    break;
                }
            }

            if (unknownOptions.Count > 0)
            {
                throw new BullseyeException($"Unknown option{(unknownOptions.Count > 1 ? "s" : "")} {unknownOptions.Spaced()}. \"--help\" for usage.");
            }

            if (clear)
            {
                console.Clear();
            }

            var operatingSystem = OperatingSystem.Unknown;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                operatingSystem = OperatingSystem.Windows;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                operatingSystem = OperatingSystem.Linux;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                operatingSystem = OperatingSystem.MacOS;
            }

            if (!noColor && operatingSystem == OperatingSystem.Windows)
            {
                await WindowsConsole.TryEnableVirtualTerminalProcessing(console.Out, verbose).ConfigureAwait(false);
            }

            var isHostForced = true;

            if (host == Host.Unknown)
            {
                isHostForced = false;

                if (Environment.GetEnvironmentVariable("APPVEYOR")?.ToUpperInvariant() == "TRUE")
                {
                    host = Host.Appveyor;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TRAVIS_OS_NAME")))
                {
                    host = Host.Travis;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME")))
                {
                    host = Host.TeamCity;
                }
            }

            var palette = new Palette(noColor, host, operatingSystem);
            var log     = new Logger(console, skipDependencies, dryRun, parallel, palette, verbose);

            await log.Version().ConfigureAwait(false);

            await log.Verbose($"Host: {host}{(host != Host.Unknown ? $" ({(isHostForced ? "forced" : "detected")})" : "")}").ConfigureAwait(false);

            await log.Verbose($"OS: {operatingSystem}").ConfigureAwait(false);

            await log.Verbose($"Args: {string.Join(" ", args)}").ConfigureAwait(false);

            if (showHelp)
            {
                await console.Out.WriteLineAsync(GetUsage(palette)).ConfigureAwait(false);

                return;
            }

            if (listDependencies || listInputs || listTargets)
            {
                await console.Out.WriteLineAsync(targets.ToString(listDependencies, listInputs, palette)).ConfigureAwait(false);

                return;
            }

            var names = args.Where(arg => !arg.StartsWith("-")).ToList();

            if (names.Count == 0)
            {
                names.Add("default");
            }

            await targets.RunAsync(names, skipDependencies, dryRun, parallel, log).ConfigureAwait(false);
        }
예제 #7
0
        private async Task Results()
        {
            // whitespace (e.g. can change to '·' for debugging)
            var ws = ' ';

            var rows = new List <SummaryRow> {
                new SummaryRow {
                    TargetOrInput = $"{p.Default}Target{p.Reset}", Outcome = $"{p.Default}Outcome{p.Reset}", Duration = $"{p.Default}Duration{p.Reset}", Percentage = ""
                }
            };

            foreach (var item in results.OrderBy(i => i.Value.Ordinal))
            {
                var target = $"{p.Target}{item.Key}{p.Reset}";

                var outcome = item.Value.Outcome == TargetOutcome.Failed
                    ? $"{p.Failed}Failed!{p.Reset}"
                    : item.Value.Outcome == TargetOutcome.NoInputs
                        ? $"{p.Warning}No inputs!{p.Reset}"
                        : $"{p.Succeeded}Succeeded{p.Reset}";

                var duration = item.Value.Duration.HasValue
                    ? $"{p.Timing}{item.Value.Duration.Humanize()}{p.Reset}"
                    : "";

                var percentage = item.Value.Duration.HasValue && totalDuration.HasValue && totalDuration.Value > TimeSpan.Zero
                    ? $"{p.Timing}{100 * item.Value.Duration.Value.TotalMilliseconds / totalDuration.Value.TotalMilliseconds:N1}%{p.Reset}"
                    : "";

                rows.Add(new SummaryRow {
                    TargetOrInput = target, Outcome = outcome, Duration = duration, Percentage = percentage
                });

                var index = 0;

                foreach (var result in item.Value.InputResults.Values.OrderBy(result => result.Ordinal))
                {
                    var input = $"{ws}{ws}{p.Input}{result.Input}{p.Reset}";

                    var inputOutcome = result.Outcome == TargetInputOutcome.Failed ? $"{p.Failed}Failed!{p.Reset}" : $"{p.Succeeded}Succeeded{p.Reset}";

                    var inputDuration = result.Duration.HasValue
                        ? $"{(index < item.Value.InputResults.Count - 1 ? p.TreeFork : p.TreeCorner)}{p.Timing}{result.Duration.Humanize()}{p.Reset}"
                        : "";

                    var inputPercentage = result.Duration.HasValue && totalDuration.HasValue && totalDuration.Value > TimeSpan.Zero
                        ? $"{(index < item.Value.InputResults.Count - 1 ? p.TreeFork : p.TreeCorner)}{p.Timing}{100 * result.Duration.Value.TotalMilliseconds / totalDuration.Value.TotalMilliseconds:N1}%{p.Reset}"
                        : "";

                    rows.Add(new SummaryRow {
                        TargetOrInput = input, Outcome = inputOutcome, Duration = inputDuration, Percentage = inputPercentage
                    });

                    ++index;
                }
            }

            // target or input column width
            var tarW = rows.Max(row => Palette.StripColours(row.TargetOrInput).Length);

            // outcome column width
            var outW = rows.Max(row => Palette.StripColours(row.Outcome).Length);

            // duration column width
            var durW = rows.Count > 1 ? rows.Skip(1).Max(row => Palette.StripColours(row.Duration).Length) : 0;

            // percentage column width
            var perW = rows.Max(row => Palette.StripColours(row.Percentage).Length);

            // timing column width (duration and percentage)
            var timW = Max(Palette.StripColours(rows[0].Duration).Length, durW + 2 + perW);

            // expand percentage column width to ensure time and percentage are as wide as duration
            perW = Max(timW - durW - 2, perW);

            // summary start separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Default}{"".Prp(tarW + 2 + outW + 2 + timW, p.Dash)}{p.Reset}").Tax();

            // header
            await this.writer.WriteLineAsync($"{GetPrefix()}{rows[0].TargetOrInput.Prp(tarW, ws)}{ws}{ws}{rows[0].Outcome.Prp(outW, ws)}{ws}{ws}{rows[0].Duration.Prp(timW, ws)}").Tax();

            // header separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Default}{"".Prp(tarW, p.Dash)}{p.Reset}{ws}{ws}{p.Default}{"".Prp(outW, p.Dash)}{p.Reset}{ws}{ws}{p.Default}{"".Prp(timW, p.Dash)}{p.Reset}").Tax();

            // targets
            foreach (var row in rows.Skip(1))
            {
                await this.writer.WriteLineAsync($"{GetPrefix()}{row.TargetOrInput.Prp(tarW, ws)}{p.Reset}{ws}{ws}{row.Outcome.Prp(outW, ws)}{p.Reset}{ws}{ws}{row.Duration.Prp(durW, ws)}{p.Reset}{ws}{ws}{row.Percentage.Prp(perW, ws)}{p.Reset}").Tax();
            }

            // summary end separator
            await this.writer.WriteLineAsync($"{GetPrefix()}{p.Default}{"".Prp(tarW + 2 + outW + 2 + timW, p.Dash)}{p.Reset}").Tax();
        }
예제 #8
0
 public Logger(IConsole console, bool skipDependencies, bool dryRun, bool parallel, Palette palette, bool verbose)
 {
     this.console          = console;
     this.skipDependencies = skipDependencies;
     this.dryRun           = dryRun;
     this.parallel         = parallel;
     this.p       = palette;
     this.verbose = verbose;
 }
예제 #9
0
        public static async Task <Logger> Initialize(Options options)
        {
            if (options.Clear)
            {
                Console.Clear();
            }

            var operatingSystem = OperatingSystem.Unknown;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                operatingSystem = OperatingSystem.Windows;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                operatingSystem = OperatingSystem.Linux;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                operatingSystem = OperatingSystem.MacOS;
            }

            if (!options.NoColor && operatingSystem == OperatingSystem.Windows)
            {
                await WindowsConsole.TryEnableVirtualTerminalProcessing(Console.Out, options.Verbose).Tax();
            }

            var isHostDetected = false;

            if (options.Host == Host.Unknown)
            {
                isHostDetected = true;

                if (Environment.GetEnvironmentVariable("APPVEYOR")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.Appveyor;
                }
                else if (Environment.GetEnvironmentVariable("TF_BUILD")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.AzurePipelines;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TRAVIS_OS_NAME")))
                {
                    options.Host = Host.Travis;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME")))
                {
                    options.Host = Host.TeamCity;
                }
            }

            var palette = new Palette(options.NoColor, options.Host, operatingSystem);
            var log     = new Logger(Console.Out, options.SkipDependencies, options.DryRun, options.Parallel, palette, options.Verbose);

            await log.Version().Tax();

            await log.Verbose($"Host: {options.Host}{(options.Host != Host.Unknown ? $" ({(isHostDetected ? "detected" : "forced")})" : "")}").Tax();

            await log.Verbose($"OS: {operatingSystem}").Tax();

            return(log);
        }
예제 #10
0
        public static async Task <(Output, Logger)> Initialize(Options options, string logPrefix)
        {
            if (logPrefix == null)
            {
                logPrefix = "Bullseye";
                var entryAssembly = Assembly.GetEntryAssembly();
                if (entryAssembly == null)
                {
                    await Console.Error.WriteLineAsync($"{logPrefix}: Failed to get the entry assembly. Using default log prefix \"{logPrefix}\".").Tax();
                }
                else
                {
                    logPrefix = entryAssembly.GetName().Name;
                }
            }

            if (options.Clear)
            {
                try
                {
                    Console.Clear();
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
                {
                    await Console.Error.WriteLineAsync($"{logPrefix}: Failed to clear the console: {ex}").Tax();
                }
            }

            var operatingSystem = OperatingSystem.Unknown;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                operatingSystem = OperatingSystem.Windows;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                operatingSystem = OperatingSystem.Linux;
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                operatingSystem = OperatingSystem.MacOS;
            }

            if (!options.NoColor && operatingSystem == OperatingSystem.Windows)
            {
                await WindowsConsole.TryEnableVirtualTerminalProcessing(options.Verbose?Console.Error : NullTextWriter.Instance, logPrefix).Tax();
            }

            var isHostDetected = false;
            if (options.Host == Host.Unknown)
            {
                isHostDetected = true;

                if (Environment.GetEnvironmentVariable("APPVEYOR")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.Appveyor;
                }
                else if (Environment.GetEnvironmentVariable("TF_BUILD")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.AzurePipelines;
                }
                else if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.GitHubActions;
                }
                else if (Environment.GetEnvironmentVariable("GITLAB_CI")?.ToUpperInvariant() == "TRUE")
                {
                    options.Host = Host.GitLabCI;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TRAVIS_OS_NAME")))
                {
                    options.Host = Host.Travis;
                }
                else if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME")))
                {
                    options.Host = Host.TeamCity;
                }
                else if (Environment.GetEnvironmentVariable("TERM_PROGRAM")?.ToUpperInvariant() == "VSCODE")
                {
                    options.Host = Host.VisualStudioCode;
                }
            }

            var palette = new Palette(options.NoColor, options.Host, operatingSystem);
            var output  = new Output(Console.Out, palette);
            var log     = new Logger(Console.Error, logPrefix, options.SkipDependencies, options.DryRun, options.Parallel, palette, options.Verbose);

            await log.Version().Tax();

            await log.Verbose($"Host: {options.Host}{(options.Host != Host.Unknown ? $" ({(isHostDetected ? "detected" : "forced")})" : "")}").Tax();

            await log.Verbose($"OS: {operatingSystem}").Tax();

            return(output, log);
        }