diff --git a/Directory.Packages.props b/Directory.Packages.props
index 20d604e4..3beca96a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,9 +21,9 @@
-
+
-
+
@@ -54,7 +54,7 @@
-
+
diff --git a/src/Recyclarr.Cli/CompositionRoot.cs b/src/Recyclarr.Cli/CompositionRoot.cs
index 59a8a01b..11f22db0 100644
--- a/src/Recyclarr.Cli/CompositionRoot.cs
+++ b/src/Recyclarr.Cli/CompositionRoot.cs
@@ -4,6 +4,7 @@ using Autofac;
using Autofac.Extras.Ordering;
using AutoMapper.Contrib.Autofac.DependencyInjection;
using Recyclarr.Cli.Cache;
+using Recyclarr.Cli.Console.Interceptors;
using Recyclarr.Cli.Console.Setup;
using Recyclarr.Cli.Logging;
using Recyclarr.Cli.Migration;
@@ -63,7 +64,7 @@ public static class CompositionRoot
builder.RegisterAutoMapper(thisAssembly);
- CommandRegistrations(builder);
+ CliRegistrations(builder);
PipelineRegistrations(builder);
}
@@ -93,28 +94,24 @@ public static class CompositionRoot
private static void RegisterLogger(ContainerBuilder builder)
{
builder.RegisterType().As();
+ builder.RegisterType().SingleInstance();
builder.RegisterType();
builder.Register(c => c.Resolve().Create()).As().SingleInstance();
}
- private static void CommandRegistrations(ContainerBuilder builder)
+ private static void CliRegistrations(ContainerBuilder builder)
{
+ builder.RegisterType().As();
+ builder.RegisterType().As();
+ builder.RegisterType().As();
+
builder.RegisterTypes(
typeof(AppPathSetupTask),
typeof(JanitorCleanupTask))
- .As()
+ .As()
.OrderByRegistration();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo();
}
-
- public static void RegisterExternal(
- ContainerBuilder builder,
- LoggingLevelSwitch logLevelSwitch,
- AppDataPathProvider appDataPathProvider)
- {
- builder.RegisterInstance(logLevelSwitch);
- builder.RegisterInstance(appDataPathProvider);
- }
}
diff --git a/src/Recyclarr.Cli/Console/Helpers/AutofacTypeRegistrar.cs b/src/Recyclarr.Cli/Console/Helpers/AutofacTypeRegistrar.cs
index f4eec4c1..96d3213e 100644
--- a/src/Recyclarr.Cli/Console/Helpers/AutofacTypeRegistrar.cs
+++ b/src/Recyclarr.Cli/Console/Helpers/AutofacTypeRegistrar.cs
@@ -3,7 +3,7 @@ using Spectre.Console.Cli;
namespace Recyclarr.Cli.Console.Helpers;
-internal class AutofacTypeRegistrar(ContainerBuilder builder, Action assignScope)
+internal class AutofacTypeRegistrar(ContainerBuilder builder)
: ITypeRegistrar
{
public void Register(Type service, Type implementation)
@@ -23,8 +23,6 @@ internal class AutofacTypeRegistrar(ContainerBuilder builder, Action _interceptedSubject = new();
private readonly ConsoleAppCancellationTokenSource _ct = new();
- public IObservable OnIntercepted => _interceptedSubject.AsObservable();
-
public void Intercept(CommandContext context, CommandSettings settings)
{
switch (settings)
@@ -30,22 +24,17 @@ public class CliInterceptor(LoggingLevelSwitch loggingLevelSwitch, AppDataPathPr
HandleBaseCommand(cmd);
break;
}
-
- _interceptedSubject.OnNext(Unit.Default);
- _interceptedSubject.OnCompleted();
}
private void HandleServiceCommand(ServiceCommandSettings cmd)
{
HandleBaseCommand(cmd);
-
- appDataPathProvider.AppDataPath = cmd.AppData;
+ appDataSetup.AppDataDirectoryOverride = cmd.AppData;
}
private void HandleBaseCommand(BaseCommandSettings cmd)
{
cmd.CancellationToken = _ct.Token;
-
loggingLevelSwitch.MinimumLevel = cmd.Debug switch
{
true => LogEventLevel.Debug,
diff --git a/src/Recyclarr.Cli/Console/Interceptors/GlobalTaskInterceptor.cs b/src/Recyclarr.Cli/Console/Interceptors/GlobalTaskInterceptor.cs
new file mode 100644
index 00000000..5aba1fdc
--- /dev/null
+++ b/src/Recyclarr.Cli/Console/Interceptors/GlobalTaskInterceptor.cs
@@ -0,0 +1,17 @@
+using Recyclarr.Cli.Console.Setup;
+using Spectre.Console.Cli;
+
+namespace Recyclarr.Cli.Console.Interceptors;
+
+public class GlobalTaskInterceptor(IOrderedEnumerable tasks) : ICommandInterceptor
+{
+ public void Intercept(CommandContext context, CommandSettings settings)
+ {
+ tasks.ForEach(x => x.OnStart());
+ }
+
+ public void InterceptResult(CommandContext context, CommandSettings settings, ref int result)
+ {
+ tasks.Reverse().ForEach(x => x.OnFinish());
+ }
+}
diff --git a/src/Recyclarr.Cli/Console/Interceptors/VersionLogInterceptor.cs b/src/Recyclarr.Cli/Console/Interceptors/VersionLogInterceptor.cs
new file mode 100644
index 00000000..2e42efc3
--- /dev/null
+++ b/src/Recyclarr.Cli/Console/Interceptors/VersionLogInterceptor.cs
@@ -0,0 +1,11 @@
+using Spectre.Console.Cli;
+
+namespace Recyclarr.Cli.Console.Interceptors;
+
+public class VersionLogInterceptor(ILogger log) : ICommandInterceptor
+{
+ public void Intercept(CommandContext context, CommandSettings settings)
+ {
+ log.Debug("Recyclarr Version: {Version}", GitVersionInformation.InformationalVersion);
+ }
+}
diff --git a/src/Recyclarr.Cli/Console/Setup/AppPathSetupTask.cs b/src/Recyclarr.Cli/Console/Setup/AppPathSetupTask.cs
index 07eeaed5..d15a2be1 100644
--- a/src/Recyclarr.Cli/Console/Setup/AppPathSetupTask.cs
+++ b/src/Recyclarr.Cli/Console/Setup/AppPathSetupTask.cs
@@ -2,7 +2,7 @@ using Recyclarr.Platform;
namespace Recyclarr.Cli.Console.Setup;
-public class AppPathSetupTask(ILogger log, IAppPaths paths) : IBaseCommandSetupTask
+public class AppPathSetupTask(ILogger log, IAppPaths paths) : IGlobalSetupTask
{
public void OnStart()
{
diff --git a/src/Recyclarr.Cli/Console/Setup/IBaseCommandSetupTask.cs b/src/Recyclarr.Cli/Console/Setup/IGlobalSetupTask.cs
similarity index 68%
rename from src/Recyclarr.Cli/Console/Setup/IBaseCommandSetupTask.cs
rename to src/Recyclarr.Cli/Console/Setup/IGlobalSetupTask.cs
index 99ffbee3..7c45c7cd 100644
--- a/src/Recyclarr.Cli/Console/Setup/IBaseCommandSetupTask.cs
+++ b/src/Recyclarr.Cli/Console/Setup/IGlobalSetupTask.cs
@@ -1,6 +1,6 @@
namespace Recyclarr.Cli.Console.Setup;
-public interface IBaseCommandSetupTask
+public interface IGlobalSetupTask
{
void OnStart();
void OnFinish();
diff --git a/src/Recyclarr.Cli/Console/Setup/JanitorCleanupTask.cs b/src/Recyclarr.Cli/Console/Setup/JanitorCleanupTask.cs
index df7e4b45..2d236b3b 100644
--- a/src/Recyclarr.Cli/Console/Setup/JanitorCleanupTask.cs
+++ b/src/Recyclarr.Cli/Console/Setup/JanitorCleanupTask.cs
@@ -4,7 +4,7 @@ using Recyclarr.Settings;
namespace Recyclarr.Cli.Console.Setup;
public class JanitorCleanupTask(ILogJanitor janitor, ILogger log, ISettingsProvider settingsProvider)
- : IBaseCommandSetupTask
+ : IGlobalSetupTask
{
public void OnStart()
{
diff --git a/src/Recyclarr.Cli/Program.cs b/src/Recyclarr.Cli/Program.cs
index f53d93f2..e1681deb 100644
--- a/src/Recyclarr.Cli/Program.cs
+++ b/src/Recyclarr.Cli/Program.cs
@@ -2,9 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using Autofac;
using Recyclarr.Cli.Console;
using Recyclarr.Cli.Console.Helpers;
-using Recyclarr.Cli.Console.Setup;
-using Recyclarr.Platform;
-using Serilog.Core;
using Spectre.Console;
using Spectre.Console.Cli;
@@ -12,21 +9,13 @@ namespace Recyclarr.Cli;
internal static class Program
{
- private static ILifetimeScope? _scope;
- private static IBaseCommandSetupTask[] _tasks = Array.Empty();
- private static ILogger? _log;
-
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
public static int Main(string[] args)
{
var builder = new ContainerBuilder();
CompositionRoot.Setup(builder);
- var logLevelSwitch = new LoggingLevelSwitch();
- var appDataPathProvider = new AppDataPathProvider();
- CompositionRoot.RegisterExternal(builder, logLevelSwitch, appDataPathProvider);
-
- var app = new CommandApp(new AutofacTypeRegistrar(builder, s => _scope = s));
+ var app = new CommandApp(new AutofacTypeRegistrar(builder));
app.Configure(config =>
{
#if DEBUG
@@ -34,60 +23,29 @@ internal static class Program
config.ValidateExamples();
#endif
- config.Settings.PropagateExceptions = true;
+ // config.Settings.PropagateExceptions = true;
config.Settings.StrictParsing = true;
config.SetApplicationName("recyclarr");
config.SetApplicationVersion(
$"v{GitVersionInformation.SemVer} ({GitVersionInformation.FullBuildMetaData})");
- var interceptor = new CliInterceptor(logLevelSwitch, appDataPathProvider);
- interceptor.OnIntercepted.Subscribe(_ => OnAppInitialized());
- config.SetInterceptor(interceptor);
+ config.SetExceptionHandler((ex, resolver) =>
+ {
+ var log = (ILogger?) resolver?.Resolve(typeof(ILogger));
+ if (log is null)
+ {
+ AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
+ }
+ else
+ {
+ log.Error(ex, "Non-recoverable Exception");
+ }
+ });
CliSetup.Commands(config);
});
- var result = 1;
- try
- {
- result = app.Run(args);
- }
- catch (Exception ex)
- {
- if (_log is null)
- {
- AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
- }
- else
- {
- _log.Error(ex, "Non-recoverable Exception");
- }
- }
- finally
- {
- OnAppCleanup();
- }
-
- return result;
- }
-
- private static void OnAppInitialized()
- {
- if (_scope is null)
- {
- throw new InvalidProgramException("Composition root is not initialized");
- }
-
- _log = _scope.Resolve();
- _log.Debug("Recyclarr Version: {Version}", GitVersionInformation.InformationalVersion);
-
- _tasks = _scope.Resolve>().ToArray();
- _tasks.ForEach(x => x.OnStart());
- }
-
- private static void OnAppCleanup()
- {
- _tasks.Reverse().ForEach(x => x.OnFinish());
+ return app.Run(args);
}
}
diff --git a/src/Recyclarr.Platform/AppDataPathProvider.cs b/src/Recyclarr.Platform/AppDataPathProvider.cs
deleted file mode 100644
index 3bf11039..00000000
--- a/src/Recyclarr.Platform/AppDataPathProvider.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Recyclarr.Platform;
-
-public class AppDataPathProvider
-{
- public string? AppDataPath { get; set; }
-}
diff --git a/src/Recyclarr.Platform/DefaultAppDataSetup.cs b/src/Recyclarr.Platform/DefaultAppDataSetup.cs
index 7e547469..975914a1 100644
--- a/src/Recyclarr.Platform/DefaultAppDataSetup.cs
+++ b/src/Recyclarr.Platform/DefaultAppDataSetup.cs
@@ -5,11 +5,13 @@ namespace Recyclarr.Platform;
public class DefaultAppDataSetup(
IEnvironment env,
IFileSystem fs,
- IRuntimeInformation runtimeInfo)
+ IRuntimeInformation runtimeInfo) : IAppDataSetup
{
- public IAppPaths CreateAppPaths(string? appDataDirectoryOverride = null)
+ public string? AppDataDirectoryOverride { get; set; }
+
+ public IAppPaths CreateAppPaths()
{
- var appDir = GetAppDataDirectory(appDataDirectoryOverride);
+ var appDir = GetAppDataDirectory(AppDataDirectoryOverride);
return new AppPaths(fs.DirectoryInfo.New(appDir));
}
diff --git a/src/Recyclarr.Platform/IAppDataSetup.cs b/src/Recyclarr.Platform/IAppDataSetup.cs
new file mode 100644
index 00000000..a6100ac3
--- /dev/null
+++ b/src/Recyclarr.Platform/IAppDataSetup.cs
@@ -0,0 +1,6 @@
+namespace Recyclarr.Platform;
+
+public interface IAppDataSetup
+{
+ public string? AppDataDirectoryOverride { get; set; }
+}
diff --git a/src/Recyclarr.Platform/PlatformAutofacModule.cs b/src/Recyclarr.Platform/PlatformAutofacModule.cs
index 0ec06399..39bc5d9c 100644
--- a/src/Recyclarr.Platform/PlatformAutofacModule.cs
+++ b/src/Recyclarr.Platform/PlatformAutofacModule.cs
@@ -12,16 +12,11 @@ public class PlatformAutofacModule : Module
private static void RegisterAppPaths(ContainerBuilder builder)
{
- builder.RegisterType();
+ builder.RegisterType().As().AsSelf().SingleInstance();
builder.RegisterType().As();
builder.RegisterType().As();
- builder.Register(c =>
- {
- var appData = c.Resolve();
- var dataSetup = c.Resolve();
- return dataSetup.CreateAppPaths(appData.AppDataPath);
- })
+ builder.Register(c => c.Resolve().CreateAppPaths())
.As()
.SingleInstance();
}
diff --git a/tests/Recyclarr.Cli.IntegrationTests/BaseCommandSetupIntegrationTest.cs b/tests/Recyclarr.Cli.IntegrationTests/BaseCommandSetupIntegrationTest.cs
index 5993ab29..9b97e8b4 100644
--- a/tests/Recyclarr.Cli.IntegrationTests/BaseCommandSetupIntegrationTest.cs
+++ b/tests/Recyclarr.Cli.IntegrationTests/BaseCommandSetupIntegrationTest.cs
@@ -11,7 +11,7 @@ internal class BaseCommandSetupIntegrationTest : CliIntegrationFixture
[Test]
public void Base_command_startup_tasks_are_registered()
{
- var registrations = Resolve>();
+ var registrations = Resolve>();
registrations.Select(x => x.GetType()).Should().BeEquivalentTo(new[]
{
typeof(JanitorCleanupTask),
diff --git a/tests/Recyclarr.Cli.IntegrationTests/CompositionRootTest.cs b/tests/Recyclarr.Cli.IntegrationTests/CompositionRootTest.cs
index 8f28574f..b510187e 100644
--- a/tests/Recyclarr.Cli.IntegrationTests/CompositionRootTest.cs
+++ b/tests/Recyclarr.Cli.IntegrationTests/CompositionRootTest.cs
@@ -3,9 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using Autofac;
using Autofac.Core;
using NUnit.Framework.Internal;
-using Recyclarr.Platform;
using Recyclarr.TestLibrary.Autofac;
-using Serilog.Core;
using Spectre.Console;
namespace Recyclarr.Cli.IntegrationTests;
@@ -22,7 +20,6 @@ public class CompositionRootTest
{
var builder = new ContainerBuilder();
CompositionRoot.Setup(builder);
- CompositionRoot.RegisterExternal(builder, new LoggingLevelSwitch(), new AppDataPathProvider());
// These are things that Spectre.Console normally registers for us, so they won't explicitly be
// in the CompositionRoot. Register mocks/stubs here.
diff --git a/tests/Recyclarr.Tests/Platform/DefaultAppDataSetupTest.cs b/tests/Recyclarr.Tests/Platform/DefaultAppDataSetupTest.cs
index 283e36de..7039e6c3 100644
--- a/tests/Recyclarr.Tests/Platform/DefaultAppDataSetupTest.cs
+++ b/tests/Recyclarr.Tests/Platform/DefaultAppDataSetupTest.cs
@@ -34,7 +34,8 @@ public class DefaultAppDataSetupTest
.SubDirectory("override")
.SubDirectory("path");
- var paths = sut.CreateAppPaths(overridePath.FullName);
+ sut.AppDataDirectoryOverride = overridePath.FullName;
+ var paths = sut.CreateAppPaths();
paths.AppDataDirectory.FullName.Should().Be(overridePath.FullName);
}
@@ -88,7 +89,8 @@ public class DefaultAppDataSetupTest
.SubDirectory("var")
.SubDirectory("path").FullName;
- sut.CreateAppPaths(expectedPath);
+ sut.AppDataDirectoryOverride = expectedPath;
+ sut.CreateAppPaths();
env.DidNotReceiveWithAnyArgs().GetEnvironmentVariable(default!);
fs.AllDirectories.Should().Contain(expectedPath);