Skip to content

Commit 31ea516

Browse files
authored
Improve configuration initialization (#1875)
* Improve configuration initialization * Never default to ConfigurationSource.Init * fix tests compilation * Ensure we can clone remote as local
1 parent ec3d29b commit 31ea516

File tree

23 files changed

+217
-110
lines changed

23 files changed

+217
-110
lines changed

aspire/AppHost.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,9 @@
55

66
using ConsoleAppFramework;
77
using Elastic.Documentation;
8-
using Microsoft.Extensions.Logging;
98
using static Elastic.Documentation.Aspire.ResourceNames;
109

11-
// ReSharper disable UnusedVariable
12-
// ReSharper disable RedundantAssignment
13-
// ReSharper disable NotAccessedVariable
14-
15-
var logLevel = LogLevel.Information;
16-
GlobalCommandLine.Process(ref args, ref logLevel, out var skipPrivateRepositories, out _);
17-
var globalArguments = new List<string>();
18-
if (skipPrivateRepositories)
19-
globalArguments.Add("--skip-private-repositories");
20-
21-
if (logLevel != LogLevel.Information)
22-
{
23-
globalArguments.Add("--log-level");
24-
globalArguments.Add(logLevel.ToString());
25-
}
10+
GlobalCli.Process(ref args, out _, out var globalArguments);
2611

2712
await ConsoleApp.RunAsync(args, BuildAspireHost);
2813
return;
@@ -32,7 +17,6 @@
3217
async Task BuildAspireHost(bool startElasticsearch, bool assumeCloned, bool skipPrivateRepositories, Cancel ctx)
3318
{
3419
var builder = DistributedApplication.CreateBuilder(args);
35-
skipPrivateRepositories = globalArguments.Contains("--skip-private-repositories");
3620

3721
var llmUrl = builder.AddParameter("LlmGatewayUrl", secret: true);
3822
var llmServiceAccountPath = builder.AddParameter("LlmGatewayServiceAccountPath", secret: true);
@@ -63,6 +47,7 @@ async Task BuildAspireHost(bool startElasticsearch, bool assumeCloned, bool skip
6347
.WithEnvironment("LLM_GATEWAY_SERVICE_ACCOUNT_KEY_PATH", llmServiceAccountPath)
6448
.WithExplicitStart();
6549

50+
// ReSharper disable once RedundantAssignment
6651
api = startElasticsearch
6752
? api
6853
.WithReference(elasticsearchLocal)
@@ -78,6 +63,8 @@ async Task BuildAspireHost(bool startElasticsearch, bool assumeCloned, bool skip
7863
.WithArgs(["assembler", "build", "--exporters", "elasticsearch", .. globalArguments])
7964
.WithExplicitStart()
8065
.WaitForCompletion(cloneAll);
66+
67+
// ReSharper disable once RedundantAssignment
8168
indexElasticsearch = startElasticsearch
8269
? indexElasticsearch
8370
.WaitFor(elasticsearchLocal)
@@ -97,6 +84,8 @@ async Task BuildAspireHost(bool startElasticsearch, bool assumeCloned, bool skip
9784
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal.Resource.PasswordParameter)
9885
.WithExplicitStart()
9986
.WaitForCompletion(cloneAll);
87+
88+
// ReSharper disable once RedundantAssignment
10089
indexElasticsearchSemantic = startElasticsearch
10190
? indexElasticsearchSemantic
10291
.WaitFor(elasticsearchLocal)
@@ -130,6 +119,7 @@ async Task BuildAspireHost(bool startElasticsearch, bool assumeCloned, bool skip
130119
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey);
131120

132121

122+
// ReSharper disable once RedundantAssignment
133123
serveStatic = startElasticsearch ? serveStatic.WaitFor(elasticsearchLocal) : serveStatic.WaitFor(buildAll);
134124

135125
await builder.Build().RunAsync(ctx);

src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,70 @@
66
using System.Text.RegularExpressions;
77
using Elastic.Documentation.Configuration.Assembler;
88
using Microsoft.Extensions.DependencyInjection;
9-
using NetEscapades.EnumGenerators;
9+
using Microsoft.Extensions.Logging;
1010

1111
namespace Elastic.Documentation.Configuration;
1212

13-
[EnumExtensions]
14-
public enum ConfigurationSource
15-
{
16-
Local,
17-
Checkout,
18-
Embedded
19-
}
20-
2113
public partial class ConfigurationFileProvider
2214
{
2315
private readonly IFileSystem _fileSystem;
2416
private readonly string _assemblyName;
17+
private readonly ILogger<ConfigurationFileProvider> _logger;
2518

26-
public ConfigurationSource ConfigurationSource { get; private set; } = ConfigurationSource.Embedded;
19+
public ConfigurationSource ConfigurationSource { get; }
2720
public string? GitReference { get; }
2821

29-
public ConfigurationFileProvider(IFileSystem fileSystem, bool skipPrivateRepositories = false)
22+
public ConfigurationFileProvider(
23+
ILoggerFactory logFactory,
24+
IFileSystem fileSystem,
25+
bool skipPrivateRepositories = false,
26+
ConfigurationSource? configurationSource = null
27+
)
3028
{
29+
_logger = logFactory.CreateLogger<ConfigurationFileProvider>();
3130
_fileSystem = fileSystem;
3231
_assemblyName = typeof(ConfigurationFileProvider).Assembly.GetName().Name!;
3332
SkipPrivateRepositories = skipPrivateRepositories;
3433
TemporaryDirectory = fileSystem.Directory.CreateTempSubdirectory("docs-builder-config");
3534

35+
ConfigurationSource = configurationSource ?? (
36+
fileSystem.Directory.Exists(LocalConfigurationDirectory)
37+
? ConfigurationSource.Local : ConfigurationSource.Embedded
38+
);
39+
40+
if (ConfigurationSource == ConfigurationSource.Local && !fileSystem.Directory.Exists(LocalConfigurationDirectory))
41+
throw new Exception($"Required directory form {nameof(ConfigurationSource)}.{nameof(ConfigurationSource.Local)} directory {LocalConfigurationDirectory} does not exist.");
42+
43+
if (ConfigurationSource == ConfigurationSource.Remote && !fileSystem.Directory.Exists(AppDataConfigurationDirectory))
44+
throw new Exception($"Required directory form {nameof(ConfigurationSource)}.{nameof(ConfigurationSource.Remote)} directory {AppDataConfigurationDirectory} does not exist.");
45+
46+
var path = GetAppDataPath("git-ref.txt");
47+
if (_fileSystem.File.Exists(path))
48+
GitReference = _fileSystem.File.ReadAllText(path);
49+
else if (ConfigurationSource == ConfigurationSource.Remote)
50+
throw new Exception($"Can not read git-ref.txt in directory {LocalConfigurationDirectory}");
51+
52+
if (ConfigurationSource == ConfigurationSource.Remote)
53+
{
54+
_logger.LogInformation("{ConfigurationSource}: git ref '{GitReference}', in {Directory}",
55+
$"{nameof(ConfigurationSource)}.{nameof(ConfigurationSource.Remote)}", GitReference, AppDataConfigurationDirectory);
56+
}
57+
58+
if (ConfigurationSource == ConfigurationSource.Local)
59+
{
60+
_logger.LogInformation("{ConfigurationSource}: located {Directory}",
61+
$"{nameof(ConfigurationSource)}.{nameof(ConfigurationSource.Local)}", LocalConfigurationDirectory);
62+
}
63+
if (ConfigurationSource == ConfigurationSource.Embedded)
64+
{
65+
_logger.LogInformation("{ConfigurationSource} using embedded in binary configuration",
66+
$"{nameof(ConfigurationSource)}.{nameof(ConfigurationSource.Embedded)}");
67+
}
68+
3669
VersionFile = CreateTemporaryConfigurationFile("versions.yml");
3770
AssemblerFile = CreateTemporaryConfigurationFile("assembler.yml");
3871
NavigationFile = CreateTemporaryConfigurationFile("navigation.yml");
3972
LegacyUrlMappingsFile = CreateTemporaryConfigurationFile("legacy-url-mappings.yml");
40-
var path = GetAppDataPath("git-ref.txt");
41-
if (ConfigurationSource == ConfigurationSource.Checkout && _fileSystem.File.Exists(path))
42-
GitReference = _fileSystem.File.ReadAllText(path);
4373
}
4474

4575
public bool SkipPrivateRepositories { get; }
@@ -66,6 +96,8 @@ public IFileInfo CreateNavigationFile(AssemblyConfiguration configuration)
6696
if (_fileSystem.File.Exists(tempFile))
6797
return NavigationFile;
6898

99+
_logger.LogInformation("Filtering navigation file to remove private repositories");
100+
69101
// This routine removes `toc: `'s linking to private repositories and reindents any later lines if needed.
70102
// This will make any public children in the nav move up one place.
71103
var spacing = -1;
@@ -139,19 +171,22 @@ private IFileInfo CreateTemporaryConfigurationFile(string fileName)
139171
private StreamReader GetLocalOrEmbedded(string fileName)
140172
{
141173
var localPath = GetLocalPath(fileName);
142-
var appDataPath = GetAppDataPath(fileName);
143-
if (_fileSystem.File.Exists(localPath))
174+
if (ConfigurationSource == ConfigurationSource.Local && _fileSystem.File.Exists(localPath))
144175
{
145-
ConfigurationSource = ConfigurationSource.Local;
146176
var reader = _fileSystem.File.OpenText(localPath);
147177
return reader;
148178
}
149-
if (_fileSystem.File.Exists(appDataPath))
179+
if (ConfigurationSource == ConfigurationSource.Local)
180+
throw new Exception($"Can not read {fileName} in directory {LocalConfigurationDirectory}");
181+
182+
var appDataPath = GetAppDataPath(fileName);
183+
if (ConfigurationSource == ConfigurationSource.Remote && _fileSystem.File.Exists(appDataPath))
150184
{
151-
ConfigurationSource = ConfigurationSource.Checkout;
152185
var reader = _fileSystem.File.OpenText(appDataPath);
153186
return reader;
154187
}
188+
if (ConfigurationSource == ConfigurationSource.Remote)
189+
throw new Exception($"Can not read {fileName} in directory {AppDataConfigurationDirectory}");
155190
return GetEmbeddedStream(fileName);
156191
}
157192

@@ -163,24 +198,26 @@ private StreamReader GetEmbeddedStream(string fileName)
163198
return reader;
164199
}
165200

166-
private static string AppDataConfigurationDirectory { get; } = Path.Combine(Paths.ApplicationData.FullName, "config-clone", "config");
167-
private static string LocalConfigurationDirectory { get; } = Path.Combine(Paths.WorkingDirectoryRoot.FullName, "config");
201+
public static string AppDataConfigurationDirectory { get; } = Path.Combine(Paths.ApplicationData.FullName, "config-clone", "config");
202+
public static string LocalConfigurationDirectory { get; } = Path.Combine(Paths.WorkingDirectoryRoot.FullName, "config");
168203

169204
private static string GetLocalPath(string file) => Path.Combine(LocalConfigurationDirectory, file);
170205
private static string GetAppDataPath(string file) => Path.Combine(AppDataConfigurationDirectory, file);
206+
171207
[GeneratedRegex(@"^\s+-?\s?toc:\s?")]
172208
private static partial Regex TocPrefixRegex();
173209
}
174210

175211
public static class ConfigurationFileProviderServiceCollectionExtensions
176212
{
177-
public static IServiceCollection AddConfigurationFileProvider(
178-
this IServiceCollection services,
213+
public static IServiceCollection AddConfigurationFileProvider(this IServiceCollection services,
179214
bool skipPrivateRepositories,
180-
Action<IServiceCollection, ConfigurationFileProvider> configure
181-
)
215+
ConfigurationSource? configurationSource,
216+
Action<IServiceCollection, ConfigurationFileProvider> configure)
182217
{
183-
var provider = new ConfigurationFileProvider(new FileSystem(), skipPrivateRepositories);
218+
using var sp = services.BuildServiceProvider();
219+
var logFactory = sp.GetRequiredService<ILoggerFactory>();
220+
var provider = new ConfigurationFileProvider(logFactory, new FileSystem(), skipPrivateRepositories, configurationSource);
184221
_ = services.AddSingleton(provider);
185222
configure(services, provider);
186223
return services;

src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,28 @@
1313

1414
namespace Elastic.Documentation.ServiceDefaults;
1515

16-
public record CliInvocation(bool IsHelpOrVersion);
17-
1816
public static class AppDefaultsExtensions
1917
{
2018
public static TBuilder AddDocumentationServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
2119
{
2220
var args = Array.Empty<string>();
2321
return builder.AddDocumentationServiceDefaults(ref args);
2422
}
25-
public static TBuilder AddDocumentationServiceDefaults<TBuilder>(this TBuilder builder, ref string[] args, Action<IServiceCollection, ConfigurationFileProvider> configure) where TBuilder : IHostApplicationBuilder =>
26-
builder.AddDocumentationServiceDefaults(ref args, null, configure);
27-
28-
public static TBuilder AddDocumentationServiceDefaults<TBuilder>(this TBuilder builder, ref string[] args, LogLevel? defaultLogLevel = null, Action<IServiceCollection, ConfigurationFileProvider>? configure = null) where TBuilder : IHostApplicationBuilder
23+
public static TBuilder AddDocumentationServiceDefaults<TBuilder>(this TBuilder builder, ref string[] args, Action<IServiceCollection, ConfigurationFileProvider>? configure = null)
24+
where TBuilder : IHostApplicationBuilder
2925
{
30-
var logLevel = defaultLogLevel ?? LogLevel.Information;
31-
GlobalCommandLine.Process(ref args, ref logLevel, out var skipPrivateRepositories, out var isHelpOrVersion);
26+
GlobalCli.Process(ref args, out var globalArgs);
3227

3328
var services = builder.Services;
29+
_ = builder.Services.AddElasticDocumentationLogging(globalArgs.LogLevel);
3430
_ = services
35-
.AddConfigurationFileProvider(skipPrivateRepositories, (s, p) =>
31+
.AddConfigurationFileProvider(globalArgs.SkipPrivateRepositories, globalArgs.ConfigurationSource, (s, p) =>
3632
{
3733
_ = s.AddSingleton(p.CreateVersionConfiguration());
3834
configure?.Invoke(s, p);
3935
});
40-
_ = builder.Services.AddElasticDocumentationLogging(logLevel);
41-
_ = services.AddSingleton(new CliInvocation(isHelpOrVersion));
36+
_ = builder.Services.AddElasticDocumentationLogging(globalArgs.LogLevel);
37+
_ = services.AddSingleton(globalArgs);
4238

4339
return builder.AddServiceDefaults();
4440
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using NetEscapades.EnumGenerators;
6+
7+
namespace Elastic.Documentation;
8+
9+
[EnumExtensions]
10+
public enum ConfigurationSource
11+
{
12+
Local,
13+
Remote,
14+
Embedded
15+
}

src/Elastic.Documentation/GlobalCommandLine.cs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,61 @@
66

77
namespace Elastic.Documentation;
88

9-
public static class GlobalCommandLine
9+
public record GlobalCliArgs
1010
{
11-
public static void Process(
12-
ref string[] args,
13-
ref LogLevel defaultLogLevel,
14-
out bool skipPrivateRepositories,
15-
out bool isHelpOrVersion
16-
)
11+
public LogLevel LogLevel { get; init; } = LogLevel.Information;
12+
public ConfigurationSource? ConfigurationSource { get; init; }
13+
public bool SkipPrivateRepositories { get; init; }
14+
public bool IsHelpOrVersion { get; init; }
15+
}
16+
public static class GlobalCli
17+
{
18+
public static void Process(ref string[] args, out GlobalCliArgs cli) => Process(ref args, out cli, out _);
19+
public static void Process(ref string[] args, out GlobalCliArgs cli, out string[] globalArguments)
1720
{
18-
skipPrivateRepositories = false;
19-
isHelpOrVersion = false;
20-
var newArgs = new List<string>();
21+
cli = new GlobalCliArgs();
22+
globalArguments = [];
23+
var globalArgs = new List<string>();
24+
var filteredArguments = new List<string>();
2125
for (var i = 0; i < args.Length; i++)
2226
{
2327
if (args[i] == "--log-level")
2428
{
2529
if (args.Length > i + 1)
26-
defaultLogLevel = GetLogLevel(args[i + 1]);
30+
{
31+
cli = cli with { LogLevel = GetLogLevel(args[i + 1]) };
32+
globalArgs.Add("--log-level");
33+
globalArgs.Add(args[i + 1]);
34+
}
35+
i++;
36+
}
37+
else if (args[i] is "--config-source" or "--configuration-source" or "-c")
38+
{
39+
if (args.Length > i + 1 && ConfigurationSourceExtensions.TryParse(args[i + 1], out var cs, true, true))
40+
{
41+
cli = cli with { ConfigurationSource = cs };
42+
globalArgs.Add("--config-source");
43+
globalArgs.Add(args[i + 1]);
44+
}
2745
i++;
2846
}
2947
else if (args[i] == "--skip-private-repositories")
30-
skipPrivateRepositories = true;
48+
{
49+
cli = cli with { SkipPrivateRepositories = true };
50+
globalArgs.Add("--skip-private-repositories");
51+
}
3152
else if (args[i] is "--help" or "--version")
3253
{
33-
isHelpOrVersion = true;
34-
newArgs.Add(args[i]);
54+
cli = cli with { IsHelpOrVersion = true };
55+
globalArgs.Add(args[i]);
56+
filteredArguments.Add(args[i]);
3557
}
3658
else
37-
newArgs.Add(args[i]);
59+
filteredArguments.Add(args[i]);
3860
}
3961

40-
args = [.. newArgs];
62+
args = [.. filteredArguments];
63+
globalArguments = [.. globalArgs];
4164
}
4265

4366
private static LogLevel GetLogLevel(string? logLevel) => logLevel switch

0 commit comments

Comments
 (0)