diff --git a/GitLfsCache.Tests/Tool/StoreFlagTests.cs b/GitLfsCache.Tests/Tool/StoreFlagTests.cs new file mode 100644 index 0000000..e5a0944 --- /dev/null +++ b/GitLfsCache.Tests/Tool/StoreFlagTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace ktsu.GitLfsCache.Tests.Tool; + +using ktsu.GitLfsCache.Tool; + +[TestClass] +public class StoreFlagTests +{ + [TestMethod] + public void Store_RelativeDirectory_ResolvesAgainstTheWorkingDirectory() + { + // The options validator refuses a root that is not fully qualified, so passing `--store ./cache` + // through verbatim aborted startup with a validation stack trace. + string resolved = Program.ResolveStoreRoot("./cache"); + + Assert.IsTrue(Path.IsPathFullyQualified(resolved), resolved); + Assert.AreEqual(Path.Combine(Environment.CurrentDirectory, "cache"), resolved); + } + + [TestMethod] + public async Task Store_RelativeDirectory_IsAcceptedByTheCommandLine() + { + // The invalid upstream stops the run after the flags are read and before a server starts, so a + // relative --store reaches the same one-line failure path as any other flag mistake. + int exitCode = await Program.Main(["--store", "./cache", "--upstream", "not-a-name-url-pair"]); + + Assert.AreEqual(1, exitCode); + } + + [TestMethod] + public void Store_FullyQualifiedDirectory_IsKeptAsGiven() + { + string root = Path.Combine( + Path.GetPathRoot(Path.GetTempPath()) ?? Path.DirectorySeparatorChar.ToString(), + "gitlfscache"); + + Assert.AreEqual(root, Program.ResolveStoreRoot(root)); + } +} diff --git a/GitLfsCache.Tool/Program.cs b/GitLfsCache.Tool/Program.cs index 94825bb..d7c0655 100644 --- a/GitLfsCache.Tool/Program.cs +++ b/GitLfsCache.Tool/Program.cs @@ -29,7 +29,7 @@ internal static class Program /// The configuration key the --token-key flag overrides. private const string TokenKeyKey = "GitLfsCache:TokenKeys:0"; - private static async Task Main(string[] args) + internal static async Task Main(string[] args) { Option port = new("--port", "-p") { @@ -110,7 +110,7 @@ private static async Task Main(string[] args) if (parseResult.GetValue(store) is string storeRoot) { - overrides[StoreRootKey] = storeRoot; + overrides[StoreRootKey] = ResolveStoreRoot(storeRoot); } if (parseResult.GetValue(maxSize) is string budget) @@ -285,6 +285,19 @@ private static bool TryApplyUpstreams( return true; } + /// + /// Resolves the --store flag against the working directory. + /// + /// + /// The options validator requires a fully qualified root, which keeps configuration files and + /// environment variables strict for container deployments. A relative directory is what someone + /// running the tool locally types, though, so the flag resolves it the way --config does + /// rather than letting startup fail with a validation stack trace. + /// + /// The directory as given on the command line. + /// The fully qualified directory. + internal static string ResolveStoreRoot(string storeRoot) => Path.GetFullPath(storeRoot); + /// /// Groups every --allow flag by the upstream it names. ///