diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e34b08..220b533d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docker support! Image name is `ghcr.io/recyclarr/recyclarr`. See the [Docker] wiki page for more information. +- Global app data path support via environment variable named `RECYCLARR_APP_DATA`. The path + specified here will be used as the app data path for every invocation of `recyclarr` as if + `--app-data` were specified. ### Fixed diff --git a/src/Common/DefaultEnvironment.cs b/src/Common/DefaultEnvironment.cs index e77d052d..140f4af0 100644 --- a/src/Common/DefaultEnvironment.cs +++ b/src/Common/DefaultEnvironment.cs @@ -11,4 +11,9 @@ internal class DefaultEnvironment : IEnvironment { return Environment.GetFolderPath(folder, option); } + + public string? GetEnvironmentVariable(string variable) + { + return Environment.GetEnvironmentVariable(variable); + } } diff --git a/src/Common/IEnvironment.cs b/src/Common/IEnvironment.cs index 51fd387d..8cfbb902 100644 --- a/src/Common/IEnvironment.cs +++ b/src/Common/IEnvironment.cs @@ -4,4 +4,5 @@ public interface IEnvironment { public string GetFolderPath(Environment.SpecialFolder folder); string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option); + string? GetEnvironmentVariable(string variable); } diff --git a/src/Recyclarr.Tests/Command/Initialization/DefaultAppDataSetupTest.cs b/src/Recyclarr.Tests/Command/Initialization/DefaultAppDataSetupTest.cs index 8e58ea15..5fd898ec 100644 --- a/src/Recyclarr.Tests/Command/Initialization/DefaultAppDataSetupTest.cs +++ b/src/Recyclarr.Tests/Command/Initialization/DefaultAppDataSetupTest.cs @@ -1,3 +1,5 @@ +using System.IO.Abstractions; +using System.IO.Abstractions.Extensions; using System.IO.Abstractions.TestingHelpers; using AutoFixture.NUnit3; using Common; @@ -22,6 +24,8 @@ public class DefaultAppDataSetupTest [Frozen] IAppPaths paths, DefaultAppDataSetup sut) { + env.GetEnvironmentVariable(default!).ReturnsForAnyArgs((string?) null); + paths.DefaultAppDataDirectoryName.Returns("app_data"); env.GetFolderPath(Arg.Any(), Arg.Any()) .Returns(FileUtils.NormalizePath("base/path")); @@ -49,6 +53,8 @@ public class DefaultAppDataSetupTest [Frozen] IEnvironment env, DefaultAppDataSetup sut) { + env.GetEnvironmentVariable(default!).ReturnsForAnyArgs((string?) null); + sut.SetupDefaultPath(null, false); env.Received().GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.None); @@ -59,8 +65,46 @@ public class DefaultAppDataSetupTest [Frozen] IEnvironment env, DefaultAppDataSetup sut) { + env.GetEnvironmentVariable(default!).ReturnsForAnyArgs((string?) null); + sut.SetupDefaultPath(null, true); env.Received().GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create); } + + [Test, AutoMockData] + public void Use_environment_variable_if_override_not_specified( + [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, + [Frozen] IEnvironment env, + DefaultAppDataSetup sut) + { + var expectedPath = fs.CurrentDirectory() + .SubDirectory("env") + .SubDirectory("var") + .SubDirectory("path").FullName; + + env.GetEnvironmentVariable(default!).ReturnsForAnyArgs(expectedPath); + + sut.SetupDefaultPath(null, true); + + env.Received().GetEnvironmentVariable("RECYCLARR_APP_DATA"); + fs.AllDirectories.Should().Contain(expectedPath); + } + + [Test, AutoMockData] + public void Explicit_override_takes_precedence_over_environment_variable( + [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, + [Frozen] IEnvironment env, + DefaultAppDataSetup sut) + { + var expectedPath = fs.CurrentDirectory() + .SubDirectory("env") + .SubDirectory("var") + .SubDirectory("path").FullName; + + sut.SetupDefaultPath(expectedPath, true); + + env.DidNotReceiveWithAnyArgs().GetEnvironmentVariable(default!); + fs.AllDirectories.Should().Contain(expectedPath); + } } diff --git a/src/Recyclarr/Command/Initialization/DefaultAppDataSetup.cs b/src/Recyclarr/Command/Initialization/DefaultAppDataSetup.cs index 4aed0e08..cd57cd22 100644 --- a/src/Recyclarr/Command/Initialization/DefaultAppDataSetup.cs +++ b/src/Recyclarr/Command/Initialization/DefaultAppDataSetup.cs @@ -20,6 +20,9 @@ public class DefaultAppDataSetup : IDefaultAppDataSetup public void SetupDefaultPath(string? appDataDirectoryOverride, bool forceCreate) { + // If a specific app data directory is not provided, use the following environment variable to find the path. + appDataDirectoryOverride ??= _env.GetEnvironmentVariable("RECYCLARR_APP_DATA"); + // If the user did not explicitly specify an app data directory, perform some system introspection to verify if // the user has a home directory. if (string.IsNullOrEmpty(appDataDirectoryOverride)) @@ -49,8 +52,8 @@ public class DefaultAppDataSetup : IDefaultAppDataSetup else { // Ensure user-specified app data directory is created and use it. - _fs.Directory.CreateDirectory(appDataDirectoryOverride); - _paths.SetAppDataPath(appDataDirectoryOverride); + var dir = _fs.Directory.CreateDirectory(appDataDirectoryOverride); + _paths.SetAppDataPath(dir.FullName); } } } diff --git a/wiki/Command-Line-Reference.md b/wiki/Command-Line-Reference.md index 66848251..b04b3836 100644 --- a/wiki/Command-Line-Reference.md +++ b/wiki/Command-Line-Reference.md @@ -97,6 +97,10 @@ Overrides the normal, default location of the [[application data directory|File- that this option is mainly intended for usage in the official Docker image. It is not intended for normal use outside of that. +If you'd like this behavior globally for all commands without having to specify this option, define +an environment variable named `RECYCLARR_APP_DATA` with the same path. Note that if you have both +set, `--app-data` always takes precedence. + ## Sonarr ### `--list-release-profiles`