Clean up profile enrichment

This commit is contained in:
Patrik Svensson 2021-01-29 17:03:04 +01:00 committed by Patrik Svensson
parent 953008b5e3
commit e20f6284f9
24 changed files with 254 additions and 158 deletions

View File

@ -9,7 +9,7 @@ namespace InfoExample
var grid = new Grid()
.AddColumn(new GridColumn().NoWrap().PadRight(4))
.AddColumn()
.AddRow("[b]Profile[/]", $"{AnsiConsole.Console.Profile.Name}")
.AddRow("[b]Enrichers[/]", string.Join(", ", AnsiConsole.Profile.Enrichers))
.AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Ansi)}")
.AddRow("[b]Supports links?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Links)}")

View File

@ -26,12 +26,16 @@ namespace Spectre.Console.Testing
{
_writer = new StringWriter();
var factory = AnsiConsoleFactory.NoEnrichers();
var factory = new AnsiConsoleFactory();
_console = factory.Create(new AnsiConsoleSettings
{
Ansi = ansi,
ColorSystem = (ColorSystemSupport)system,
Out = _writer,
Enrichment = new ProfileEnrichment
{
UseDefaultEnrichers = false,
},
});
_console.Profile.Width = width;

View File

@ -11,18 +11,11 @@ namespace Spectre.Console.Testing
{
public Profile Profile { get; }
public IAnsiConsoleCursor Cursor => new FakeAnsiConsoleCursor();
public FakeConsoleInput Input { get; }
IAnsiConsoleInput IAnsiConsole.Input => Input;
public RenderPipeline Pipeline { get; }
public Decoration Decoration { get; set; }
public Color Foreground { get; set; }
public Color Background { get; set; }
public string Link { get; set; }
public StringWriter Writer { get; }
public string Output => Writer.ToString();
public FakeConsoleInput Input { get; }
public string Output => Profile.Out.ToString();
public IReadOnlyList<string> Lines => Output.TrimEnd('\n').Split(new char[] { '\n' });
public FakeConsole(
@ -30,11 +23,10 @@ namespace Spectre.Console.Testing
bool supportsAnsi = true, ColorSystem colorSystem = ColorSystem.Standard,
bool legacyConsole = false, bool interactive = true)
{
Writer = new StringWriter();
Input = new FakeConsoleInput();
Pipeline = new RenderPipeline();
Profile = new Profile("Fake console", Writer, encoding ?? Encoding.UTF8);
Profile = new Profile(new StringWriter(), encoding ?? Encoding.UTF8);
Profile.Width = width;
Profile.Height = height;
Profile.ColorSystem = colorSystem;
@ -46,7 +38,7 @@ namespace Spectre.Console.Testing
public void Dispose()
{
Writer.Dispose();
Profile.Out.Dispose();
}
public void Clear(bool home)
@ -62,7 +54,7 @@ namespace Spectre.Console.Testing
foreach (var segment in segments)
{
Writer.Write(segment.Text);
Profile.Out.Write(segment.Text);
}
}

View File

@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Spectre.Console.Enrichment;
namespace Spectre.Console
{
@ -11,49 +10,6 @@ namespace Spectre.Console
/// </summary>
public sealed class AnsiConsoleFactory
{
private readonly List<IProfileEnricher> _enrichers;
/// <summary>
/// Initializes a new instance of the <see cref="AnsiConsoleFactory"/> class.
/// </summary>
public AnsiConsoleFactory()
{
_enrichers = new List<IProfileEnricher>
{
new AppVeyorProfile(),
new BambooProfile(),
new BitbucketProfile(),
new BitriseProfile(),
new ContinuaCIProfile(),
new GitHubProfile(),
new GitLabProfile(),
new GoCDProfile(),
new JenkinsProfile(),
new MyGetProfile(),
new TeamCityProfile(),
new TfsProfile(),
new TravisProfile(),
};
}
/// <summary>
/// Initializes a new instance of the <see cref="AnsiConsoleFactory"/> class.
/// </summary>
/// <param name="enrichers">The profile enrichers to use.</param>
public AnsiConsoleFactory(IEnumerable<IProfileEnricher> enrichers)
{
_enrichers = new List<IProfileEnricher>(enrichers ?? Enumerable.Empty<IProfileEnricher>());
}
/// <summary>
/// Creates a new <see cref="AnsiConsoleFactory"/> without default profile enrichers.
/// </summary>
/// <returns>A new <see cref="AnsiConsoleFactory"/> without default profile enrichers.</returns>
public static AnsiConsoleFactory NoEnrichers()
{
return new AnsiConsoleFactory(Enumerable.Empty<IProfileEnricher>());
}
/// <summary>
/// Creates an ANSI console.
/// </summary>
@ -86,7 +42,7 @@ namespace Spectre.Console
interactive = Environment.UserInteractive;
}
var profile = new Profile("Default", buffer, encoding)
var profile = new Profile(buffer, encoding)
{
ColorSystem = colorSystem,
};
@ -97,43 +53,14 @@ namespace Spectre.Console
profile.Capabilities.Interactive = interactive;
// Enrich the profile
var variables = GetEnvironmentVariables(settings);
var customEnrichers = settings.Enrichers ?? Enumerable.Empty<IProfileEnricher>();
foreach (var enricher in _enrichers.Concat(customEnrichers))
{
if (enricher.Enabled(variables))
{
enricher.Enrich(profile);
}
}
ProfileEnricher.Enrich(
profile,
settings.Enrichment,
settings.EnvironmentVariables);
return new AnsiConsoleFacade(profile);
}
private static IDictionary<string, string> GetEnvironmentVariables(AnsiConsoleSettings settings)
{
if (settings.EnvironmentVariables != null)
{
return new Dictionary<string, string>(settings.EnvironmentVariables, StringComparer.OrdinalIgnoreCase);
}
return Environment.GetEnvironmentVariables()
.Cast<System.Collections.DictionaryEntry>()
.Aggregate(
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase),
(dictionary, entry) =>
{
var key = (string)entry.Key;
if (!dictionary.TryGetValue(key, out _))
{
dictionary.Add(key, entry.Value as string ?? string.Empty);
}
return dictionary;
},
dictionary => dictionary);
}
private static (bool Ansi, bool Legacy) DetectAnsi(AnsiConsoleSettings settings, System.IO.TextWriter buffer)
{
var supportsAnsi = settings.Ansi == AnsiSupport.Yes;

View File

@ -30,6 +30,11 @@ namespace Spectre.Console
/// </summary>
public InteractionSupport Interactive { get; set; }
/// <summary>
/// Gets or sets the profile enrichments settings.
/// </summary>
public ProfileEnrichment Enrichment { get; set; }
/// <summary>
/// Gets or sets the environment variables.
/// If not value is provided the default environment variables will be used.
@ -37,8 +42,11 @@ namespace Spectre.Console
public Dictionary<string, string>? EnvironmentVariables { get; set; }
/// <summary>
/// Gets or sets the profile enrichers to use.
/// Initializes a new instance of the <see cref="AnsiConsoleSettings"/> class.
/// </summary>
public List<IProfileEnricher>? Enrichers { get; set; }
public AnsiConsoleSettings()
{
Enrichment = new ProfileEnrichment();
}
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Spectre.Console.Rendering;

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class AppVeyorProfile : IProfileEnricher
internal sealed class AppVeyorEnricher : IProfileEnricher
{
public string Name => "AppVeyor";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("APPVEYOR");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "AppVeyor";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class BambooProfile : IProfileEnricher
internal sealed class BambooEnricher : IProfileEnricher
{
public string Name => "Bamboo";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("bamboo_buildNumber");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "Bamboo";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class BitbucketProfile : IProfileEnricher
internal sealed class BitbucketEnricher : IProfileEnricher
{
public string Name => "Bitbucket";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("BITBUCKET_REPO_OWNER") ||
@ -13,7 +15,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "BitBucket";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class BitriseProfile : IProfileEnricher
internal sealed class BitriseEnricher : IProfileEnricher
{
public string Name => "Bitrise";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("BITRISE_BUILD_URL");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "Bitrise";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class ContinuaCIProfile : IProfileEnricher
internal sealed class ContinuaEnricher : IProfileEnricher
{
public string Name => "ContinuaCI";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("ContinuaCI.Version");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "Continua CI";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class GitHubProfile : IProfileEnricher
internal sealed class GitHubEnricher : IProfileEnricher
{
public string Name => "GitHub";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
if (environmentVariables.TryGetValue("GITHUB_ACTIONS", out var value))
@ -17,7 +19,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "GitHub Actions";
profile.Capabilities.Ansi = true;
profile.Capabilities.Legacy = false;
profile.Capabilities.Interactive = false;

View File

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class GitLabProfile : IProfileEnricher
internal sealed class GitLabEnricher : IProfileEnricher
{
public string Name => "GitLab";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
if (environmentVariables.TryGetValue("CI_SERVER", out var value))
@ -17,7 +19,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "GitLab";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class GoCDProfile : IProfileEnricher
internal sealed class GoCDEnricher : IProfileEnricher
{
public string Name => "GoCD";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("GO_SERVER_URL");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "GoCD";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class JenkinsProfile : IProfileEnricher
internal sealed class JenkinsEnricher : IProfileEnricher
{
public string Name => "Jenkins";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("JENKINS_URL");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "Jenkins";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,10 +1,12 @@
using System;
using System;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class MyGetProfile : IProfileEnricher
internal sealed class MyGetEnricher : IProfileEnricher
{
public string Name => "MyGet";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
if (environmentVariables.TryGetValue("BuildRunner", out var value))
@ -17,7 +19,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "MyGet";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class TeamCityProfile : IProfileEnricher
internal sealed class TeamCityEnricher : IProfileEnricher
{
public string Name => "TeamCity";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("TEAMCITY_VERSION");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "TeamCity";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class TfsProfile : IProfileEnricher
internal sealed class TfsEnricher : IProfileEnricher
{
public string Name => "TFS";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("TF_BUILD");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "TFS";
profile.Capabilities.Interactive = false;
}
}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Spectre.Console
namespace Spectre.Console.Enrichment
{
internal sealed class TravisProfile : IProfileEnricher
internal sealed class TravisEnricher : IProfileEnricher
{
public string Name => "Travis";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("TRAVIS");
@ -11,7 +13,6 @@ namespace Spectre.Console
public void Enrich(Profile profile)
{
profile.Name = "Travis";
profile.Capabilities.Interactive = false;
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Spectre.Console.Enrichment
{
internal static class ProfileEnricher
{
private static readonly List<IProfileEnricher> _defaultEnrichers = new List<IProfileEnricher>
{
new WindowsTerminalEnricher(),
new AppVeyorEnricher(),
new BambooEnricher(),
new BitbucketEnricher(),
new BitriseEnricher(),
new ContinuaEnricher(),
new GitHubEnricher(),
new GitLabEnricher(),
new GoCDEnricher(),
new JenkinsEnricher(),
new MyGetEnricher(),
new TeamCityEnricher(),
new TfsEnricher(),
new TravisEnricher(),
};
public static void Enrich(
Profile profile,
ProfileEnrichment settings,
IDictionary<string, string>? environmentVariables)
{
if (profile is null)
{
throw new ArgumentNullException(nameof(profile));
}
settings ??= new ProfileEnrichment();
var variables = GetEnvironmentVariables(environmentVariables);
foreach (var enricher in GetEnrichers(settings))
{
if (string.IsNullOrWhiteSpace(enricher.Name))
{
throw new InvalidOperationException($"Profile enricher of type '{enricher.GetType().FullName}' does not have a name.");
}
if (enricher.Enabled(variables))
{
enricher.Enrich(profile);
profile.AddEnricher(enricher.Name);
}
}
}
private static List<IProfileEnricher> GetEnrichers(ProfileEnrichment settings)
{
var enrichers = new List<IProfileEnricher>();
if (settings.UseDefaultEnrichers)
{
enrichers.AddRange(_defaultEnrichers);
}
if (settings.Enrichers?.Count > 0)
{
enrichers.AddRange(settings.Enrichers);
}
return enrichers;
}
private static IDictionary<string, string> GetEnvironmentVariables(IDictionary<string, string>? variables)
{
if (variables != null)
{
return new Dictionary<string, string>(variables, StringComparer.OrdinalIgnoreCase);
}
return Environment.GetEnvironmentVariables()
.Cast<System.Collections.DictionaryEntry>()
.Aggregate(
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase),
(dictionary, entry) =>
{
var key = (string)entry.Key;
if (!dictionary.TryGetValue(key, out _))
{
dictionary.Add(key, entry.Value as string ?? string.Empty);
}
return dictionary;
},
dictionary => dictionary);
}
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Spectre.Console
{
/// <summary>
/// Contains settings for profile enrichment.
/// </summary>
public sealed class ProfileEnrichment
{
/// <summary>
/// Gets or sets a value indicating whether or not
/// any default enrichers should be added.
/// </summary>
/// <remarks>Defaults to <c>true</c>.</remarks>
public bool UseDefaultEnrichers { get; set; } = true;
/// <summary>
/// Gets or sets the list of custom enrichers to use.
/// </summary>
public List<IProfileEnricher>? Enrichers { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Spectre.Console.Enrichment
{
internal sealed class WindowsTerminalEnricher : IProfileEnricher
{
public string Name => "Windows Terminal";
public bool Enabled(IDictionary<string, string> environmentVariables)
{
return environmentVariables.ContainsKey("WT_SESSION");
}
public void Enrich(Profile profile)
{
profile.Capabilities.Links = true;
}
}
}

View File

@ -7,6 +7,11 @@ namespace Spectre.Console
/// </summary>
public interface IProfileEnricher
{
/// <summary>
/// Gets the name of the enricher.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets whether or not this enricher is enabled.
/// </summary>

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@ -9,7 +10,9 @@ namespace Spectre.Console
/// </summary>
public sealed class Profile
{
private string _name;
private readonly HashSet<string> _enrichers;
private static readonly string[] _defaultEnricher = new[] { "Default" };
private TextWriter _out;
private Encoding _encoding;
private Capabilities _capabilities;
@ -17,19 +20,18 @@ namespace Spectre.Console
private int? _height;
/// <summary>
/// Gets or sets the profile name.
/// Gets the enrichers used to build this profile.
/// </summary>
public string Name
public IReadOnlyCollection<string> Enrichers
{
get => _name;
set
get
{
if (value == null)
if (_enrichers.Count > 0)
{
throw new InvalidOperationException("Profile name cannot be null");
return _enrichers;
}
_name = value;
return _defaultEnricher;
}
}
@ -139,12 +141,11 @@ namespace Spectre.Console
/// <summary>
/// Initializes a new instance of the <see cref="Profile"/> class.
/// </summary>
/// <param name="name">The profile name.</param>
/// <param name="out">The output buffer.</param>
/// <param name="encoding">The output encoding.</param>
public Profile(string name, TextWriter @out, Encoding encoding)
public Profile(TextWriter @out, Encoding encoding)
{
_name = name ?? throw new ArgumentNullException(nameof(name));
_enrichers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_out = @out ?? throw new ArgumentNullException(nameof(@out));
_encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
_capabilities = new Capabilities(this);
@ -161,6 +162,16 @@ namespace Spectre.Console
return (int)colorSystem <= (int)ColorSystem;
}
internal void AddEnricher(string name)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
_enrichers.Add(name);
}
private int GetWidth()
{
if (_width != null)