Skip to content

Commit

Permalink
CommandLine WiP
Browse files Browse the repository at this point in the history
  • Loading branch information
scottt732 committed Oct 30, 2023
1 parent 2aa8633 commit c612408
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 254 deletions.
8 changes: 4 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
</PropertyGroup>

<ItemGroup Label="Package References">
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" Version="2022.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="All" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="All" Version="17.3.44" />
<PackageReference Include="MinVer" PrivateAssets="All" Version="4.2.0" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" Version="2023.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="All" Version="7.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="All" Version="17.7.30" />
<PackageReference Include="MinVer" PrivateAssets="All" Version="4.3.0" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.1.118" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<Target Name="Versioning" BeforeTargets="MinVer">
<PropertyGroup Label="Build">
<MinVerDefaultPreReleasePhase>preview</MinVerDefaultPreReleasePhase>
<MinVerDefaultPreReleaseIdentifiers>preview.0</MinVerDefaultPreReleaseIdentifiers>
<!-- Tag your repository with the semantic version e.g. '1.0.0' to version all NuGet packages. If you have
multiple NuGet packages in your solution and want to version them separately, then uncomment this line
and tag your repository with the name of the package followed by the semantic version e.g.
Expand Down
9 changes: 6 additions & 3 deletions Sholo.ruleset
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="General rules" Description="Style + Rules - Laziness" ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp" RuleNamespace="Microsoft.CodeAnalysis.CSharp">
<Rule Id="CS1591" Action="None" />
<Rule Id="IDE0290" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1030" Action="None" />
Expand All @@ -13,7 +14,7 @@
<Rule Id="CA1812" Action="None" />
<Rule Id="CA1819" Action="None" />
<Rule Id="CA1822" Action="None" />
<Rule Id="CA2007" Action="Info" />
<Rule Id="CA2007" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" />
Expand All @@ -26,9 +27,11 @@
<Rule Id="VSTHRD200" Action="Info" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1000" Action="None" />
<Rule Id="SA1009" Action="None" />
<Rule Id="SA1101" Action="None" />
<Rule Id="SA1111" Action="None" />
<Rule Id="SA1118" Action="None" />
<Rule Id="SA1200" Action="None" />
<Rule Id="SA1201" Action="None" />
<Rule Id="SA1309" Action="None" />
Expand All @@ -43,4 +46,4 @@
<Rule Id="SA1629" Action="None" />
<Rule Id="SA1633" Action="None" />
</Rules>
</RuleSet>
</RuleSet>
15 changes: 9 additions & 6 deletions Source/Sholo.CommandLine.Containers/ApplicationNameAndVersion.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace Sholo.CommandLine.Containers
using System.ComponentModel.DataAnnotations;

namespace Sholo.CommandLine.Containers;

public class ApplicationNameAndVersion
{
public class ApplicationNameAndVersion
{
public string Name { get; set; }
public string Version { get; set; }
}
[Required]
public string Name { get; set; }

public string Version { get; set; }
}
11 changes: 5 additions & 6 deletions Source/Sholo.CommandLine.Containers/ContainerizedAppOptions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using CommandLine;

namespace Sholo.CommandLine.Containers
namespace Sholo.CommandLine.Containers;

public class ContainerizedAppOptions
{
public class ContainerizedAppOptions
{
[Option('c', "configfile", Required = false, HelpText = "The config file to load (.yaml or .json extension)")]
public string ConfigFile { get; set; }
}
[Option('c', "configfile", Required = false, HelpText = "The config file to load (.yaml or .json extension)")]
public string ConfigFile { get; set; }
}
183 changes: 109 additions & 74 deletions Source/Sholo.CommandLine.Containers/ContainerizedHostBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,94 +1,129 @@
using System;
using System.IO;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Sholo.CommandLine.Containers.SplashService;
using Sholo.Utils.Logging;
using Sholo.Utils.Validation;

namespace Sholo.CommandLine.Containers
namespace Sholo.CommandLine.Containers;

[PublicAPI]
public static class ContainerizedHostBuilder
{
[PublicAPI]
public static class ContainerizedHostBuilder
{
public static IHostBuilder Create(string[] args, string applicationName = null)
=> ConfigureHostBuilder<Splash>(new HostBuilder().UseOptions<ContainerizedAppOptions>(args), applicationName);
public static IHostBuilder Create(string[] args, string applicationName = null)
=> ConfigureHostBuilder<Splash>(new HostBuilder().UseOptions<ContainerizedAppOptions>(args), applicationName);

public static IHostBuilder Create<TOptions>(string[] args, string applicationName = null)
where TOptions : ContainerizedAppOptions
=> ConfigureHostBuilder<Splash>(new HostBuilder().UseOptions<TOptions>(args), applicationName);
public static IHostBuilder Create<TOptions>(string[] args, string applicationName = null)
where TOptions : ContainerizedAppOptions
=> ConfigureHostBuilder<Splash>(new HostBuilder().UseOptions<TOptions>(args), applicationName);

public static IHostBuilder Create<TOptions, TSplash>(string[] args, string applicationName = null)
where TOptions : ContainerizedAppOptions
where TSplash : class, ISplash
=> ConfigureHostBuilder<TSplash>(new HostBuilder().UseOptions<TOptions>(args), applicationName);
public static IHostBuilder Create<TOptions, TSplash>(string[] args, string applicationName = null)
where TOptions : ContainerizedAppOptions
where TSplash : class, ISplash
=> ConfigureHostBuilder<TSplash>(new HostBuilder().UseOptions<TOptions>(args), applicationName);

public static IHostBuilder ConfigureHostBuilder<TSplash>(IHostBuilder hostBuilder, string applicationName)
where TSplash : class, ISplash
=> hostBuilder
.UseVersionResource(applicationName)
.UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production")
.ConfigureAppConfiguration((ctx, cb) =>
public static IHostBuilder ConfigureHostBuilder<TSplash>(IHostBuilder hostBuilder, string applicationName)
where TSplash : class, ISplash
=> hostBuilder
.UseVersionResource(applicationName)
.UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production")
.UseDefaultServiceProvider((ctx, options) => { options.ValidateScopes = ctx.HostingEnvironment.IsDevelopment(); })
.ConfigureHostConfiguration(cb =>
{
if (hostBuilder.Properties.TryGetValue(HostBuilderExtensions.ContextPropertyName, out var optionsObj) && optionsObj is ContainerizedAppOptions options && !string.IsNullOrEmpty(options.ConfigFile))
{
var configFile = options.ConfigFile;
if (!File.Exists(configFile))
{
if (ctx.TryGetOptions<ContainerizedAppOptions>(out var options) && !string.IsNullOrEmpty(options?.ConfigFile))
{
var configFile = options.ConfigFile;
if (!File.Exists(configFile))
{
throw new Exception("The config file specified does not exist");
}
throw new Exception("The config file specified does not exist");
}
var configFileExtension = Path.GetExtension(configFile);
var configFileExtension = Path.GetExtension(configFile);
if (configFileExtension.Equals(".yaml", StringComparison.OrdinalIgnoreCase))
{
cb.AddYamlFile(configFile, false);
}
else if (configFileExtension.Equals(".json", StringComparison.OrdinalIgnoreCase))
{
cb.AddJsonFile(configFile, false);
}
else
{
throw new Exception($"Unsupported config file extension: {configFileExtension}. Expecting .yaml or .json");
}
}
else
{
cb.AddYamlFile("config.yaml", true);
cb.AddYamlFile("config.yml", true);
cb.AddJsonFile("config.json", true);
}
if (configFileExtension.Equals(".yaml", StringComparison.OrdinalIgnoreCase))
{
cb.AddYamlFile(configFile, false);
}
else if (configFileExtension.Equals(".json", StringComparison.OrdinalIgnoreCase))
{
cb.AddJsonFile(configFile, false);
}
else
{
throw new Exception($"Unsupported config file extension: {configFileExtension}. Expecting .yaml or .json");
}
}
else
{
cb.AddYamlFile("config.yaml", true);
cb.AddYamlFile("config.yml", true);
cb.AddJsonFile("config.json", true);
cb.AddEnvironmentVariables();
cb.AddYamlFile("secrets.yaml", true);
cb.AddYamlFile("secrets.yml", true);
cb.AddJsonFile("secrets.json", true);
}
if (ctx.HostingEnvironment.IsDevelopment())
{
try
{
cb.AddUserSecrets(Assembly.GetEntryAssembly());
}
catch
{
// We tried
}
}
})
.ConfigureServices((ctx, services) =>
cb.AddEnvironmentVariables();
})
.ConfigureAppConfiguration((ctx, cb) =>
{
if (ctx.TryGetOptions<ContainerizedAppOptions>(out var options) && !string.IsNullOrEmpty(options?.ConfigFile))
{
var configFile = options.ConfigFile;
if (!File.Exists(configFile))
{
throw new Exception("The config file specified does not exist");
}
var configFileExtension = Path.GetExtension(configFile);
if (configFileExtension.Equals(".yaml", StringComparison.OrdinalIgnoreCase) || configFileExtension.Equals(".yml", StringComparison.OrdinalIgnoreCase))
{
cb.AddYamlFile(configFile, false);
}
else if (configFileExtension.Equals(".json", StringComparison.OrdinalIgnoreCase))
{
services.AddOptions<ApplicationNameAndVersion>()
.Configure(opt =>
{
opt.Name = ctx.Properties?["ApplicationName"]?.ToString() ?? string.Empty;
opt.Version = ctx.Properties?["ApplicationVersion"]?.ToString() ?? string.Empty;
});
cb.AddJsonFile(configFile, false);
}
else
{
throw new Exception($"Unsupported config file extension: {configFileExtension}. Expecting .yaml, .yml, or .json");
}
}
else
{
cb.AddYamlFile("config.yaml", true, true);
cb.AddYamlFile("config.yml", true, true);
cb.AddJsonFile("config.json", true, true);
}
services.AddSingleton<ISplash, TSplash>();
services.AddHostedService<SplashService.SplashService>();
services.AddLogging();
cb.AddEnvironmentVariables();
})
.ConfigureServices((ctx, services) =>
{
services.AddOptions<ApplicationNameAndVersion>()
.Configure(opt =>
{
opt.Name = ctx.Properties?["ApplicationName"]?.ToString() ?? string.Empty;
opt.Version = ctx.Properties?["ApplicationVersion"]?.ToString() ?? string.Empty;
})
.ConfigureLogging((ctx, lb) => { lb.AddSerilogConsole(); })
.UseConsoleLifetime(c => { c.SuppressStatusMessages = true; });
}
.PostConfigure(opt =>
{
opt.Name ??= Assembly.GetEntryAssembly()?.GetName().Name ?? "Unknown";
})
.ValidateDataAnnotations(true)
.ValidateOnStart();
services.AddSingleton<ISplash, TSplash>();
services.AddHostedService<SplashService>();
services.AddLogging();
})
.ConfigureLogging((c, lb) =>
{
lb.AddSerilogConsole(c.Configuration);
})
.UseConsoleLifetime(c => { c.SuppressStatusMessages = true; });
}
3 changes: 3 additions & 0 deletions Source/Sholo.CommandLine.Containers/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Global using directives

global using JetBrains.Annotations;
25 changes: 12 additions & 13 deletions Source/Sholo.CommandLine.Containers/HostBuilderContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
using Microsoft.Extensions.Hosting;

namespace Sholo.CommandLine.Containers
namespace Sholo.CommandLine.Containers;

public static class HostBuilderContextExtensions
{
public static class HostBuilderContextExtensions
public static bool TryGetOptions<TOptions>(this HostBuilderContext context, out TOptions options)
where TOptions : ContainerizedAppOptions
{
public static bool TryGetOptions<TOptions>(this HostBuilderContext context, out TOptions options)
where TOptions : ContainerizedAppOptions
if (context.Properties.TryGetValue(HostBuilderExtensions.ContextPropertyName, out var optionsObject))
{
if (context.Properties.TryGetValue(HostBuilderExtensions.ContextPropertyName, out var optionsObject))
if (optionsObject is TOptions optionsResult)
{
if (optionsObject is TOptions optionsResult)
{
options = optionsResult;
return true;
}
options = optionsResult;
return true;
}

options = default;
return false;
}

options = default;
return false;
}
}
Loading

0 comments on commit c612408

Please sign in to comment.