Run from docker
This commit is contained in:
116
src/Alma.App/Command/Diag/DiagCommand.cs
Normal file
116
src/Alma.App/Command/Diag/DiagCommand.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.Reflection;
|
||||
using Alma.Logging;
|
||||
|
||||
namespace Alma.Command.Diag;
|
||||
|
||||
public class DiagCommand : ICommand
|
||||
{
|
||||
private readonly ILogger<DiagCommand> _logger;
|
||||
public string CommandString => "diag";
|
||||
private readonly Lazy<IReadOnlyList<MethodInfo>> _diagnosticHelpersLazy;
|
||||
|
||||
public DiagCommand(ILogger<DiagCommand> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_diagnosticHelpersLazy = new Lazy<IReadOnlyList<MethodInfo>>(FindDiagnosticHelpers);
|
||||
}
|
||||
|
||||
public Task Run(List<string> parameters)
|
||||
{
|
||||
if (parameters.Count < 1)
|
||||
{
|
||||
_logger.LogInformation("No diagnostic helper specified.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var command = parameters[0];
|
||||
|
||||
if (command == "list")
|
||||
{
|
||||
ListDiagnosticHelpers();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var diagnosticHelpers = _diagnosticHelpersLazy.Value;
|
||||
|
||||
var helper = diagnosticHelpers.FirstOrDefault(
|
||||
h => GetDiagnosticHelper(h) is { } attr && attr.Command == command
|
||||
);
|
||||
|
||||
if (helper is null)
|
||||
{
|
||||
_logger.LogInformation($"Diagnostic helper {command} is not found.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!helper.IsStatic)
|
||||
{
|
||||
_logger.LogInformation($"Diagnostic helper {helper.Name} is not static.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
HandleHelper(helper, parameters.Skip(1));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ListDiagnosticHelpers()
|
||||
{
|
||||
var diagnosticHelpers = _diagnosticHelpersLazy.Value;
|
||||
var commands = diagnosticHelpers
|
||||
.Select(h => GetDiagnosticHelper(h)?.Command)
|
||||
.OfType<string>()
|
||||
.ToList();
|
||||
|
||||
_logger.LogInformation("Available diagnostic helpers:");
|
||||
foreach (var command in commands)
|
||||
{
|
||||
_logger.LogInformation(command);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleHelper(MethodInfo helper, IEnumerable<string> parameters)
|
||||
{
|
||||
var helperParameters = helper.GetParameters();
|
||||
var helperArguments = new object[helperParameters.Length];
|
||||
for (var i = 0; i < helperParameters.Length; i++)
|
||||
{
|
||||
var parameterType = helperParameters[i].ParameterType;
|
||||
if (parameterType == typeof(IEnumerable<string>))
|
||||
{
|
||||
helperArguments[i] = parameters;
|
||||
}
|
||||
else if (parameterType == typeof(ILogger))
|
||||
{
|
||||
helperArguments[i] = _logger;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Diagnostic helper {helper.Name} has wrong parameter type, could not resolve '{parameterType}'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
helper.Invoke(null, helperArguments);
|
||||
}
|
||||
|
||||
private IReadOnlyList<MethodInfo> FindDiagnosticHelpers()
|
||||
{
|
||||
var assemblies = new[]
|
||||
{
|
||||
Assembly.GetExecutingAssembly(),
|
||||
};
|
||||
|
||||
return assemblies.SelectMany(
|
||||
a =>
|
||||
a
|
||||
.GetTypes()
|
||||
.SelectMany(t => t.GetMethods())
|
||||
.Where(t => t.GetCustomAttributes(typeof(DiagnosticHelper)).Any()))
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
private DiagnosticHelper? GetDiagnosticHelper(MethodInfo o)
|
||||
=> o.GetCustomAttributes(typeof(DiagnosticHelper)).FirstOrDefault() as DiagnosticHelper;
|
||||
}
|
||||
12
src/Alma.App/Command/Diag/DiagnosticHelper.cs
Normal file
12
src/Alma.App/Command/Diag/DiagnosticHelper.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Alma.Command.Diag;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class DiagnosticHelper : Attribute
|
||||
{
|
||||
public string Command { get; }
|
||||
|
||||
public DiagnosticHelper(string command)
|
||||
{
|
||||
Command = command;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,70 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Alma.Command.Diag;
|
||||
using Alma.Logging;
|
||||
|
||||
namespace Alma.Services;
|
||||
|
||||
public class FolderService : IFolderService
|
||||
{
|
||||
private readonly Dictionary<string, Func<string?>> _configHomeProviders;
|
||||
private readonly Dictionary<string, Func<string?>> _appDataProviders;
|
||||
public string? ConfigRoot { get; }
|
||||
public string AppData { get; }
|
||||
|
||||
public string ApplicationSubfolderName => "alma";
|
||||
|
||||
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(FolderService))]
|
||||
public FolderService()
|
||||
{
|
||||
_configHomeProviders = ConfigHomeProviders();
|
||||
_appDataProviders = AppDataProviders();
|
||||
|
||||
ConfigRoot = GetConfigHomePath();
|
||||
AppData = GetAppDataPath();
|
||||
|
||||
if (!Directory.Exists(AppData)) Directory.CreateDirectory(AppData);
|
||||
}
|
||||
|
||||
public string GetPreferredConfigurationFolder()
|
||||
private static Dictionary<string, Func<string?>> ConfigHomeProviders()
|
||||
{
|
||||
return new Dictionary<string, Func<string?>>
|
||||
{
|
||||
{"ALMA_CONFIG", () => Environment.GetEnvironmentVariable("ALMA_CONFIG")},
|
||||
{"XDG_CONFIG_HOME", () => Environment.GetEnvironmentVariable("XDG_CONFIG_HOME")},
|
||||
{"UserProfile", GetPreferredConfigurationFolder},
|
||||
{"ALMA_CONFIG_FALLBACK", () => Environment.GetEnvironmentVariable("ALMA_CONFIG_FALLBACK")},
|
||||
};
|
||||
}
|
||||
|
||||
private static Dictionary<string, Func<string?>> AppDataProviders()
|
||||
{
|
||||
return new Dictionary<string, Func<string?>>
|
||||
{
|
||||
{"ALMA_APP_DATA", () => Environment.GetEnvironmentVariable("ALMA_APP_DATA")},
|
||||
{"LocalApplicationData", () => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)},
|
||||
{"ALMA_APP_DATA_FALLBACK", () => Environment.GetEnvironmentVariable("ALMA_APP_DATA_FALLBACK")},
|
||||
};
|
||||
}
|
||||
|
||||
string IFolderService.GetPreferredConfigurationFolder()
|
||||
=> GetPreferredConfigurationFolder();
|
||||
|
||||
public static string GetPreferredConfigurationFolder()
|
||||
=> Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
|
||||
|
||||
private string? GetConfigHomePath()
|
||||
{
|
||||
var configHomeProviders = new List<Func<string?>>
|
||||
{
|
||||
() => Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"),
|
||||
() => GetPreferredConfigurationFolder()
|
||||
};
|
||||
|
||||
var configHome = EnumerateProviders(configHomeProviders);
|
||||
var configHome = EnumerateProviders(_configHomeProviders.Values);
|
||||
return configHome == null ? null : Path.Combine(configHome, ApplicationSubfolderName);
|
||||
}
|
||||
|
||||
private string GetAppDataPath()
|
||||
{
|
||||
var appDataProviders = new List<Func<string?>>
|
||||
{
|
||||
() => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
|
||||
};
|
||||
|
||||
var appData = EnumerateProviders(appDataProviders) ?? Environment.CurrentDirectory;
|
||||
var appData = EnumerateProviders(_appDataProviders.Values) ?? Environment.CurrentDirectory;
|
||||
return Path.Combine(appData, ApplicationSubfolderName);
|
||||
}
|
||||
|
||||
private static string? EnumerateProviders(List<Func<string?>> providers)
|
||||
private static string? EnumerateProviders(IEnumerable<Func<string?>> providers)
|
||||
{
|
||||
string? result = null;
|
||||
|
||||
@@ -53,4 +76,26 @@ public class FolderService : IFolderService
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[DiagnosticHelper("config-home-providers")]
|
||||
public static void ConfigHomeProviderDiag(ILogger logger)
|
||||
{
|
||||
var configHomeProviders = ConfigHomeProviders();
|
||||
logger.LogInformation($"There are {configHomeProviders.Count} config home providers:");
|
||||
foreach (var configHome in configHomeProviders)
|
||||
{
|
||||
logger.LogInformation($"{configHome.Key} => {configHome.Value() ?? "<null>"}");
|
||||
}
|
||||
}
|
||||
|
||||
[DiagnosticHelper("app-data-providers")]
|
||||
public static void AppDataProviderDiag(ILogger logger)
|
||||
{
|
||||
var appDataProviders = AppDataProviders();
|
||||
logger.LogInformation($"There are {appDataProviders.Count} app data providers:");
|
||||
foreach (var appData in appDataProviders)
|
||||
{
|
||||
logger.LogInformation($"{appData.Key} => {appData.Value() ?? "<null>"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using Alma.Data;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Alma.Command.Diag;
|
||||
using Alma.Data;
|
||||
using Alma.Logging;
|
||||
|
||||
namespace Alma.Services;
|
||||
|
||||
@@ -10,6 +13,7 @@ public class PathHelperService : IPathHelperService
|
||||
new("%DOCUMENTS%", () => Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)),
|
||||
};
|
||||
|
||||
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PathHelperService))]
|
||||
public string ResolvePath(string path, string? currentDirectory = null)
|
||||
{
|
||||
var skipCombiningCurrentDirectory = false;
|
||||
@@ -30,4 +34,14 @@ public class PathHelperService : IPathHelperService
|
||||
? path
|
||||
: Path.Combine(currentDirectory, path);
|
||||
}
|
||||
|
||||
[DiagnosticHelper("special-path-resolver")]
|
||||
public static void SpecialPathResolverDiag(ILogger logger)
|
||||
{
|
||||
logger.LogInformation($"There are {_specialPathResolvers.Count} special path resolvers:");
|
||||
foreach (var specialPathResolver in _specialPathResolvers)
|
||||
{
|
||||
logger.LogInformation($"{specialPathResolver.PathName} => {specialPathResolver.Resolver()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Alma.Logging;
|
||||
|
||||
public interface ILogger<T>
|
||||
public interface ILogger
|
||||
{
|
||||
LogLevel DefaultLogLevel { get; }
|
||||
void LogInformation(string logMessage);
|
||||
@@ -9,4 +9,9 @@ public interface ILogger<T>
|
||||
void Log(string logMessage, LogLevel logLevel);
|
||||
void LogError(string logMessage);
|
||||
void LogCritical(string logMessage);
|
||||
}
|
||||
|
||||
public interface ILogger<T> : ILogger
|
||||
{
|
||||
|
||||
}
|
||||
@@ -3,5 +3,6 @@
|
||||
public interface ILoggerFactory
|
||||
{
|
||||
ILogger<T> CreateLogger<T>();
|
||||
ILogger CreateLogger(Type t);
|
||||
LogLevel DefaultLogLevel { get; }
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
namespace Alma.Logging;
|
||||
|
||||
public class Logger<T> : ILogger<T>
|
||||
|
||||
public class Logger : ILogger
|
||||
{
|
||||
public LogLevel DefaultLogLevel { get; }
|
||||
|
||||
public Logger(LogLevel defaultLogLevel)
|
||||
public Logger(LogLevel defaultLogLevel, string topicName)
|
||||
{
|
||||
DefaultLogLevel = defaultLogLevel;
|
||||
}
|
||||
@@ -32,4 +33,12 @@ public class Logger<T> : ILogger<T>
|
||||
{
|
||||
Log(logMessage, LogLevel.Critical);
|
||||
}
|
||||
}
|
||||
|
||||
public class Logger<T> : Logger, ILogger<T>
|
||||
{
|
||||
public Logger(LogLevel defaultLogLevel) : base(defaultLogLevel, typeof(T).Name)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,9 @@ public class LoggerFactory : ILoggerFactory
|
||||
{
|
||||
return new Logger<T>(DefaultLogLevel);
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(Type t)
|
||||
{
|
||||
return new Logger(DefaultLogLevel, t.Name);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using Alma.Command;
|
||||
using System.IO;
|
||||
using System;
|
||||
using Alma.Command;
|
||||
using Alma.Command.Configure;
|
||||
using Alma.Command.Diag;
|
||||
using Alma.Command.Help;
|
||||
using Alma.Command.Info;
|
||||
using Alma.Command.Install;
|
||||
@@ -19,6 +22,14 @@ public static class Program
|
||||
{
|
||||
InitLogging();
|
||||
|
||||
var logger = AlmaLoggerFactory.CreateLogger(typeof(Program));
|
||||
|
||||
var workdir = GetWorkdir(logger);
|
||||
if (workdir != null)
|
||||
{
|
||||
Environment.CurrentDirectory = workdir;
|
||||
}
|
||||
|
||||
var services = new AlmaServiceProvider();
|
||||
|
||||
var repositoryConfiguration = services.GetService<IRepositoryConfiguration>();
|
||||
@@ -28,6 +39,33 @@ public static class Program
|
||||
await application.Run(args);
|
||||
}
|
||||
|
||||
private static string? GetWorkdir(ILogger logger)
|
||||
{
|
||||
var workdirProviders = new Dictionary<string, Func<string?>>
|
||||
{
|
||||
{"ALMA_WORKDIR", () => Environment.GetEnvironmentVariable("ALMA_WORKDIR")},
|
||||
{"WORKDIR", () => Environment.GetEnvironmentVariable("WORKDIR")},
|
||||
};
|
||||
|
||||
foreach (var workdirProvider in workdirProviders)
|
||||
{
|
||||
var workdir = workdirProvider.Value();
|
||||
if (workdir != null)
|
||||
{
|
||||
if (Directory.Exists(workdir))
|
||||
{
|
||||
return workdir;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation($"{workdirProvider.Key} is set to {workdir} but this directory does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void InitLogging()
|
||||
{
|
||||
AlmaLoggerFactory = new LoggerFactory();
|
||||
@@ -49,6 +87,7 @@ public static class Program
|
||||
[Singleton(typeof(ICommand), typeof(InstallCommand))]
|
||||
[Singleton(typeof(ICommand), typeof(HelpCommand))]
|
||||
[Singleton(typeof(ICommand), typeof(ConfigureCommand))]
|
||||
[Singleton(typeof(ICommand), typeof(DiagCommand))]
|
||||
[Singleton(typeof(IModuleConfigurationResolver), typeof(ModuleConfigurationResolver))]
|
||||
[Singleton(typeof(IMetadataHandler), typeof(MetadataHandler))]
|
||||
[Singleton(typeof(IShellService), typeof(ShellService))]
|
||||
|
||||
Reference in New Issue
Block a user