Run from docker

This commit is contained in:
2023-07-17 17:24:25 +02:00
parent 82f5a990f3
commit 5f495c8390
10 changed files with 291 additions and 20 deletions

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine as BUILD
RUN apk add -U bash build-base clang icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib-dev
WORKDIR /build
COPY . .
RUN dotnet publish src/Alma/Alma.csproj -c Release -r linux-musl-x64 -p:PublishAot=true -o /app
FROM alpine:edge as FINAL
RUN mkdir /data
WORKDIR /data
ENV ALMA_APP_DATA_FALLBACK=/appdata
ENV ALMA_CONFIG_FALLBACK=/appconfig
RUN mkdir /appdata
RUN mkdir /appconfig
RUN apk add -U icu-libs libstdc++
COPY --from=BUILD /app/Alma /alma
ENTRYPOINT ["/alma"]
CMD ["help"]

View 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;
}

View 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;
}
}

View File

@@ -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>"}");
}
}
}

View File

@@ -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()}");
}
}
}

View File

@@ -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
{
}

View File

@@ -3,5 +3,6 @@
public interface ILoggerFactory
{
ILogger<T> CreateLogger<T>();
ILogger CreateLogger(Type t);
LogLevel DefaultLogLevel { get; }
}

View File

@@ -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)
{
}
}

View File

@@ -13,4 +13,9 @@ public class LoggerFactory : ILoggerFactory
{
return new Logger<T>(DefaultLogLevel);
}
public ILogger CreateLogger(Type t)
{
return new Logger(DefaultLogLevel, t.Name);
}
}

View File

@@ -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))]