public Output(TextWriter writer, Palette palette) { this.writer = writer; this.p = palette; }
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; }
// pad right printed public static string Prp(this string text, int totalWidth, char paddingChar) => text.PadRight(totalWidth + (text.Length - Palette.StripColours(text).Length), paddingChar);
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}"); } } } }
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(); }
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); }
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(); }
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; }
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); }
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); }