From b34798eabba5c95e68f55a349700e58d23a61d68 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Thu, 17 Nov 2022 17:55:16 -0600 Subject: [PATCH] feat!: Remove LibGit2Sharp Library This library was causing numerous issues related to git operations. The straw that broke the camel's back is that it does not do automatic garbage collection (`git gc --auto`). So a user's repo directory continues to grow in size. The replacement is CliWrap, which is just a simple wrapper library that allows easy execution of shell commands. Specifically, `git` commands. BREAKING CHANGE: This change now requires the `git` executable to be installed by the user if run on a host system. The git executable will be provided automatically for the docker image. --- CHANGELOG.md | 5 ++ src/Directory.Packages.props | 7 +- src/Recyclarr.Gui/Recyclarr.Gui.csproj | 1 - .../IntegrationFixture.cs | 4 +- src/Recyclarr.Tests/CompositionRootTest.cs | 2 +- src/Recyclarr.sln | 12 --- src/Recyclarr/Command/ServiceCommand.cs | 11 ++- src/Recyclarr/CompositionRoot.cs | 2 +- .../Repo/VersionControl/GitPathTest.cs | 39 +++++++++ .../Config/Settings/SettingsValues.cs | 1 + src/TrashLib/Repo/IRepoUpdater.cs | 2 +- src/TrashLib/Repo/RepoUpdater.cs | 21 +++-- .../Repo/VersionControl/GitCmdException.cs | 17 ++++ src/TrashLib/Repo/VersionControl/GitPath.cs | 16 ++++ .../Repo/VersionControl/GitRepository.cs | 84 +++++++++++++++++++ .../VersionControl/GitRepositoryFactory.cs | 52 ++++++++++++ src/TrashLib/Repo/VersionControl/IGitPath.cs | 6 ++ .../Repo/VersionControl/IGitRepository.cs | 13 +++ .../VersionControl/IGitRepositoryFactory.cs | 6 ++ .../VersionControlAutofacModule.cs | 5 +- src/TrashLib/TrashLib.csproj | 2 +- .../GitRepositoryFactoryTest.cs | 59 ------------- .../VersionControl.Tests.csproj | 9 -- src/VersionControl/GitRepository.cs | 50 ----------- src/VersionControl/GitRepositoryFactory.cs | 56 ------------- src/VersionControl/IGitRepository.cs | 9 -- src/VersionControl/IGitRepositoryFactory.cs | 6 -- src/VersionControl/VersionControl.csproj | 8 -- .../Wrappers/IRepositoryStaticWrapper.cs | 9 -- .../LibGit2SharpRepositoryStaticWrapper.cs | 12 --- 30 files changed, 265 insertions(+), 261 deletions(-) create mode 100644 src/TrashLib.Tests/Repo/VersionControl/GitPathTest.cs create mode 100644 src/TrashLib/Repo/VersionControl/GitCmdException.cs create mode 100644 src/TrashLib/Repo/VersionControl/GitPath.cs create mode 100644 src/TrashLib/Repo/VersionControl/GitRepository.cs create mode 100644 src/TrashLib/Repo/VersionControl/GitRepositoryFactory.cs create mode 100644 src/TrashLib/Repo/VersionControl/IGitPath.cs create mode 100644 src/TrashLib/Repo/VersionControl/IGitRepository.cs create mode 100644 src/TrashLib/Repo/VersionControl/IGitRepositoryFactory.cs rename src/{ => TrashLib/Repo}/VersionControl/VersionControlAutofacModule.cs (67%) delete mode 100644 src/VersionControl.Tests/GitRepositoryFactoryTest.cs delete mode 100644 src/VersionControl.Tests/VersionControl.Tests.csproj delete mode 100644 src/VersionControl/GitRepository.cs delete mode 100644 src/VersionControl/GitRepositoryFactory.cs delete mode 100644 src/VersionControl/IGitRepository.cs delete mode 100644 src/VersionControl/IGitRepositoryFactory.cs delete mode 100644 src/VersionControl/VersionControl.csproj delete mode 100644 src/VersionControl/Wrappers/IRepositoryStaticWrapper.cs delete mode 100644 src/VersionControl/Wrappers/LibGit2SharpRepositoryStaticWrapper.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bfed6af..4150e7d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,14 @@ changes you need to make. `api_key` and `base_url` in a `secrets.yml` file. See [the secrets docs][secrets] for more info. Huge thanks to @voltron4lyfe for this one. (#105, #139) - Named instances are now supported in configuration YAML. +- New optional setting `repository.git_path` may be used to specify the path to a `git` executable. + If not used, `PATH` will be searched. ### Changed +- **BREAKING**: Recyclarr now requires `git` to be installed on host systems when using manual + installation. If using Docker, there is no breaking change since git will be bundled with the + image. - Deprecated array-style instances in configuration YAML. Read more about this in the v3.0 Upgrade Guide. diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 3b5c69d3..4040406a 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -2,7 +2,6 @@ true - @@ -12,12 +11,12 @@ + - @@ -33,7 +32,6 @@ - @@ -54,11 +52,10 @@ - - + \ No newline at end of file diff --git a/src/Recyclarr.Gui/Recyclarr.Gui.csproj b/src/Recyclarr.Gui/Recyclarr.Gui.csproj index ea021c2f..4b10ac4d 100644 --- a/src/Recyclarr.Gui/Recyclarr.Gui.csproj +++ b/src/Recyclarr.Gui/Recyclarr.Gui.csproj @@ -17,7 +17,6 @@ - diff --git a/src/Recyclarr.TestLibrary/IntegrationFixture.cs b/src/Recyclarr.TestLibrary/IntegrationFixture.cs index e1273218..fbad9df8 100644 --- a/src/Recyclarr.TestLibrary/IntegrationFixture.cs +++ b/src/Recyclarr.TestLibrary/IntegrationFixture.cs @@ -11,9 +11,8 @@ using Recyclarr.Command; using Serilog; using Serilog.Events; using TrashLib; +using TrashLib.Repo.VersionControl; using TrashLib.Startup; -using VersionControl; -using VersionControl.Wrappers; namespace Recyclarr.TestLibrary; @@ -35,7 +34,6 @@ public abstract class IntegrationFixture : IDisposable RegisterMockFor(builder); RegisterMockFor(builder); RegisterMockFor(builder); - RegisterMockFor(builder); RegisterExtraTypes(builder); diff --git a/src/Recyclarr.Tests/CompositionRootTest.cs b/src/Recyclarr.Tests/CompositionRootTest.cs index 2bdad295..2bceb4c0 100644 --- a/src/Recyclarr.Tests/CompositionRootTest.cs +++ b/src/Recyclarr.Tests/CompositionRootTest.cs @@ -13,8 +13,8 @@ using Recyclarr.Command; using Serilog; using TrashLib; using TrashLib.Config.Services; +using TrashLib.Repo.VersionControl; using TrashLib.Startup; -using VersionControl; namespace Recyclarr.Tests; diff --git a/src/Recyclarr.sln b/src/Recyclarr.sln index 9f19b0df..85831cf4 100644 --- a/src/Recyclarr.sln +++ b/src/Recyclarr.sln @@ -29,10 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrashLib", "TrashLib\TrashL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrashLib.Tests", "TrashLib.Tests\TrashLib.Tests.csproj", "{A4EC7E0D-C591-4874-B9AC-EB12A96F3E83}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionControl", "VersionControl\VersionControl.csproj", "{CF5BB1A7-3D21-48CB-B6B0-526612B2D94D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionControl.Tests", "VersionControl.Tests\VersionControl.Tests.csproj", "{F81C7EA3-4ACA-4171-8A60-531F129A33C5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclarr.TestLibrary", "Recyclarr.TestLibrary\Recyclarr.TestLibrary.csproj", "{77D1C695-94D4-46A9-8F12-41E54AF97750}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclarr.Gui", "Recyclarr.Gui\Recyclarr.Gui.csproj", "{53EECBC0-E0EA-4D6C-925C-5DB8C42CCB85}" @@ -81,14 +77,6 @@ Global {A4EC7E0D-C591-4874-B9AC-EB12A96F3E83}.Debug|Any CPU.Build.0 = Debug|Any CPU {A4EC7E0D-C591-4874-B9AC-EB12A96F3E83}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4EC7E0D-C591-4874-B9AC-EB12A96F3E83}.Release|Any CPU.Build.0 = Release|Any CPU - {CF5BB1A7-3D21-48CB-B6B0-526612B2D94D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CF5BB1A7-3D21-48CB-B6B0-526612B2D94D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CF5BB1A7-3D21-48CB-B6B0-526612B2D94D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CF5BB1A7-3D21-48CB-B6B0-526612B2D94D}.Release|Any CPU.Build.0 = Release|Any CPU - {F81C7EA3-4ACA-4171-8A60-531F129A33C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F81C7EA3-4ACA-4171-8A60-531F129A33C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F81C7EA3-4ACA-4171-8A60-531F129A33C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F81C7EA3-4ACA-4171-8A60-531F129A33C5}.Release|Any CPU.Build.0 = Release|Any CPU {77D1C695-94D4-46A9-8F12-41E54AF97750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77D1C695-94D4-46A9-8F12-41E54AF97750}.Debug|Any CPU.Build.0 = Debug|Any CPU {77D1C695-94D4-46A9-8F12-41E54AF97750}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Recyclarr/Command/ServiceCommand.cs b/src/Recyclarr/Command/ServiceCommand.cs index 4c466cba..c83286ba 100644 --- a/src/Recyclarr/Command/ServiceCommand.cs +++ b/src/Recyclarr/Command/ServiceCommand.cs @@ -13,6 +13,7 @@ using Serilog; using TrashLib.Config.Settings; using TrashLib.Extensions; using TrashLib.Repo; +using TrashLib.Repo.VersionControl; using YamlDotNet.Core; namespace Recyclarr.Command; @@ -62,13 +63,17 @@ public abstract class ServiceCommand : BaseCommand, IServiceCommand throw new CommandException( $"HTTP error while communicating with {Name}: {e.SanitizedExceptionMessage()}"); } + catch (GitCmdException e) + { + await console.Output.WriteLineAsync(e.ToString()); + } catch (Exception e) when (e is not CommandException) { throw new CommandException(e.ToString()); } } - public override Task Process(ILifetimeScope container) + public override async Task Process(ILifetimeScope container) { var log = container.Resolve(); var settingsProvider = container.Resolve(); @@ -81,9 +86,7 @@ public abstract class ServiceCommand : BaseCommand, IServiceCommand migration.CheckNeededMigrations(); SetupHttp(log, settingsProvider); - repoUpdater.UpdateRepo(); - - return Task.CompletedTask; + await repoUpdater.UpdateRepo(); } private static void SetupHttp(ILogger log, ISettingsProvider settingsProvider) diff --git a/src/Recyclarr/CompositionRoot.cs b/src/Recyclarr/CompositionRoot.cs index 70de5324..10e2e4ff 100644 --- a/src/Recyclarr/CompositionRoot.cs +++ b/src/Recyclarr/CompositionRoot.cs @@ -14,13 +14,13 @@ using TrashLib.Cache; using TrashLib.Config; using TrashLib.Config.Services; using TrashLib.Repo; +using TrashLib.Repo.VersionControl; using TrashLib.Services.Common; using TrashLib.Services.CustomFormat; using TrashLib.Services.Radarr; using TrashLib.Services.Sonarr; using TrashLib.Services.System; using TrashLib.Startup; -using VersionControl; using YamlDotNet.Serialization; using YamlDotNet.Serialization.ObjectFactories; diff --git a/src/TrashLib.Tests/Repo/VersionControl/GitPathTest.cs b/src/TrashLib.Tests/Repo/VersionControl/GitPathTest.cs new file mode 100644 index 00000000..b7156ba3 --- /dev/null +++ b/src/TrashLib.Tests/Repo/VersionControl/GitPathTest.cs @@ -0,0 +1,39 @@ +using AutoFixture.NUnit3; +using FluentAssertions; +using NSubstitute; +using NUnit.Framework; +using TestLibrary.AutoFixture; +using TrashLib.Config.Settings; +using TrashLib.Repo.VersionControl; + +namespace TrashLib.Tests.Repo.VersionControl; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class GitPathTest +{ + [Test, AutoMockData] + public void Default_path_used_when_setting_is_null( + [Frozen] ISettingsProvider settings, + GitPath sut) + { + settings.Settings.Returns(new SettingsValues {Repository = new TrashRepository {GitPath = null}}); + + var result = sut.Path; + + result.Should().Be(GitPath.Default); + } + + [Test, AutoMockData] + public void User_specified_path_used_instead_of_default( + [Frozen] ISettingsProvider settings, + GitPath sut) + { + var expectedPath = "/usr/local/bin/git"; + settings.Settings.Returns(new SettingsValues {Repository = new TrashRepository {GitPath = expectedPath}}); + + var result = sut.Path; + + result.Should().Be(expectedPath); + } +} diff --git a/src/TrashLib/Config/Settings/SettingsValues.cs b/src/TrashLib/Config/Settings/SettingsValues.cs index 7ed72403..5b394308 100644 --- a/src/TrashLib/Config/Settings/SettingsValues.cs +++ b/src/TrashLib/Config/Settings/SettingsValues.cs @@ -7,6 +7,7 @@ public record TrashRepository public string CloneUrl { get; [UsedImplicitly] init; } = "https://github.com/TRaSH-/Guides.git"; public string Branch { get; [UsedImplicitly] init; } = "master"; public string? Sha1 { get; [UsedImplicitly] init; } + public string? GitPath { get; [UsedImplicitly] init; } } public record LogJanitorSettings diff --git a/src/TrashLib/Repo/IRepoUpdater.cs b/src/TrashLib/Repo/IRepoUpdater.cs index 4aba8784..ceeee954 100644 --- a/src/TrashLib/Repo/IRepoUpdater.cs +++ b/src/TrashLib/Repo/IRepoUpdater.cs @@ -5,5 +5,5 @@ namespace TrashLib.Repo; public interface IRepoUpdater { IDirectoryInfo RepoPath { get; } - void UpdateRepo(); + Task UpdateRepo(); } diff --git a/src/TrashLib/Repo/RepoUpdater.cs b/src/TrashLib/Repo/RepoUpdater.cs index dd0d9b70..5a810957 100644 --- a/src/TrashLib/Repo/RepoUpdater.cs +++ b/src/TrashLib/Repo/RepoUpdater.cs @@ -1,10 +1,9 @@ using System.IO.Abstractions; using Common; -using LibGit2Sharp; using Serilog; using TrashLib.Config.Settings; +using TrashLib.Repo.VersionControl; using TrashLib.Startup; -using VersionControl; namespace TrashLib.Repo; @@ -32,17 +31,17 @@ public class RepoUpdater : IRepoUpdater public IDirectoryInfo RepoPath => _paths.RepoDirectory; - public void UpdateRepo() + public async Task UpdateRepo() { // Retry only once if there's a failure. This gives us an opportunity to delete the git repository and start // fresh. - var exception = CheckoutAndUpdateRepo(); + var exception = await CheckoutAndUpdateRepo(); if (exception is not null) { _log.Information("Deleting local git repo and retrying git operation..."); _fileUtils.DeleteReadOnlyDirectory(RepoPath.FullName); - exception = CheckoutAndUpdateRepo(); + exception = await CheckoutAndUpdateRepo(); if (exception is not null) { throw exception; @@ -50,7 +49,7 @@ public class RepoUpdater : IRepoUpdater } } - private Exception? CheckoutAndUpdateRepo() + private async Task CheckoutAndUpdateRepo() { var repoSettings = _settingsProvider.Settings.Repository; var cloneUrl = repoSettings.CloneUrl; @@ -64,12 +63,12 @@ public class RepoUpdater : IRepoUpdater try { - using var repo = _repositoryFactory.CreateAndCloneIfNeeded(cloneUrl, RepoPath.FullName, branch); - repo.ForceCheckout(branch); - repo.Fetch(); - repo.ResetHard(repoSettings.Sha1 ?? $"origin/{branch}"); + using var repo = await _repositoryFactory.CreateAndCloneIfNeeded(cloneUrl, RepoPath.FullName, branch); + await repo.ForceCheckout(branch); + await repo.Fetch(); + await repo.ResetHard(repoSettings.Sha1 ?? $"origin/{branch}"); } - catch (LibGit2SharpException e) + catch (GitCmdException e) { _log.Error(e, "An exception occurred during git operations on path: {RepoPath}", RepoPath); return e; diff --git a/src/TrashLib/Repo/VersionControl/GitCmdException.cs b/src/TrashLib/Repo/VersionControl/GitCmdException.cs new file mode 100644 index 00000000..c5a485f6 --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/GitCmdException.cs @@ -0,0 +1,17 @@ +namespace TrashLib.Repo.VersionControl; + +public class GitCmdException : Exception +{ + // ReSharper disable UnusedAutoPropertyAccessor.Global + public string Error { get; } + + public int ExitCode { get; } + // ReSharper restore UnusedAutoPropertyAccessor.Global + + public GitCmdException(int exitCode, string error) + : base("Git command failed with a non-zero exit code") + { + Error = error; + ExitCode = exitCode; + } +} diff --git a/src/TrashLib/Repo/VersionControl/GitPath.cs b/src/TrashLib/Repo/VersionControl/GitPath.cs new file mode 100644 index 00000000..19d7cc43 --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/GitPath.cs @@ -0,0 +1,16 @@ +using TrashLib.Config.Settings; + +namespace TrashLib.Repo.VersionControl; + +public class GitPath : IGitPath +{ + private readonly ISettingsProvider _settings; + + public GitPath(ISettingsProvider settings) + { + _settings = settings; + } + + public static string Default => "git"; + public string Path => _settings.Settings.Repository.GitPath ?? Default; +} diff --git a/src/TrashLib/Repo/VersionControl/GitRepository.cs b/src/TrashLib/Repo/VersionControl/GitRepository.cs new file mode 100644 index 00000000..61437391 --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/GitRepository.cs @@ -0,0 +1,84 @@ +using System.IO.Abstractions; +using System.Text; +using CliWrap; +using Serilog; +using TrashLib.Startup; + +namespace TrashLib.Repo.VersionControl; + +public sealed class GitRepository : IGitRepository +{ + private readonly ILogger _log; + private readonly IAppPaths _paths; + private readonly IGitPath _gitPath; + + public GitRepository(ILogger log, IAppPaths paths, IGitPath gitPath) + { + _log = log; + _paths = paths; + _gitPath = gitPath; + } + + private async Task RunGitCmd(string args) + { + _log.Debug("Executing command: git {Args}", args); + + var output = new StringBuilder(); + var error = new StringBuilder(); + + var result = await Cli.Wrap(_gitPath.Path) + .WithArguments(args) + .WithValidation(CommandResultValidation.None) + .WithStandardOutputPipe(PipeTarget.ToStringBuilder(output)) + .WithStandardErrorPipe(PipeTarget.ToStringBuilder(error)) + .WithWorkingDirectory(_paths.RepoDirectory.FullName) + .ExecuteAsync(); + + _log.Debug("{Output}", output.ToString()); + + if (result.ExitCode != 0) + { + throw new GitCmdException(result.ExitCode, error.ToString()); + } + } + + public IDirectoryInfo Path => _paths.RepoDirectory; + + public void Dispose() + { + // Nothing to do here + } + + public async Task ForceCheckout(string branch) + { + await RunGitCmd($"checkout -f {branch}"); + } + + public async Task Fetch(string remote = "origin") + { + await RunGitCmd($"fetch {remote}"); + } + + public async Task ResetHard(string toBranchOrSha1) + { + await RunGitCmd($"reset --hard {toBranchOrSha1}"); + } + + public async Task SetRemote(string name, string newUrl) + { + await RunGitCmd($"remote set-url {name} {newUrl}"); + } + + public async Task Clone(string cloneUrl, string? branch = null) + { + var args = new StringBuilder("clone"); + if (branch is not null) + { + args.Append($" -b {branch}"); + } + + _paths.RepoDirectory.Create(); + args.Append($" {cloneUrl} ."); + await RunGitCmd(args.ToString()); + } +} diff --git a/src/TrashLib/Repo/VersionControl/GitRepositoryFactory.cs b/src/TrashLib/Repo/VersionControl/GitRepositoryFactory.cs new file mode 100644 index 00000000..64a88fb5 --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/GitRepositoryFactory.cs @@ -0,0 +1,52 @@ +using System.IO.Abstractions; +using CliWrap; +using Common; + +namespace TrashLib.Repo.VersionControl; + +public class GitRepositoryFactory : IGitRepositoryFactory +{ + private readonly IFileUtilities _fileUtils; + private readonly Func _repoFactory; + private readonly IGitPath _gitPath; + + public GitRepositoryFactory( + IFileUtilities fileUtils, + Func repoFactory, + IGitPath gitPath) + { + _fileUtils = fileUtils; + _repoFactory = repoFactory; + _gitPath = gitPath; + } + + private async Task IsValid(IDirectoryInfo repoPath) + { + var result = await Cli.Wrap(_gitPath.Path) + .WithArguments("status") + .WithValidation(CommandResultValidation.None) + .WithWorkingDirectory(repoPath.FullName) + .ExecuteAsync(); + + return result.ExitCode == 0; + } + + public async Task CreateAndCloneIfNeeded(string repoUrl, string repoPath, string branch) + { + var repo = _repoFactory(repoPath); + + if (!await IsValid(repo.Path)) + { + await DeleteAndCloneRepo(repo, repoUrl, branch); + } + + await repo.SetRemote("origin", repoUrl); + return repo; + } + + private async Task DeleteAndCloneRepo(IGitRepository repo, string repoUrl, string branch) + { + _fileUtils.DeleteReadOnlyDirectory(repo.Path.FullName); + await repo.Clone(repoUrl, branch); + } +} diff --git a/src/TrashLib/Repo/VersionControl/IGitPath.cs b/src/TrashLib/Repo/VersionControl/IGitPath.cs new file mode 100644 index 00000000..42c50d6b --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/IGitPath.cs @@ -0,0 +1,6 @@ +namespace TrashLib.Repo.VersionControl; + +public interface IGitPath +{ + string Path { get; } +} diff --git a/src/TrashLib/Repo/VersionControl/IGitRepository.cs b/src/TrashLib/Repo/VersionControl/IGitRepository.cs new file mode 100644 index 00000000..bc894ab9 --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/IGitRepository.cs @@ -0,0 +1,13 @@ +using System.IO.Abstractions; + +namespace TrashLib.Repo.VersionControl; + +public interface IGitRepository : IDisposable +{ + Task ForceCheckout(string branch); + Task Fetch(string remote = "origin"); + Task ResetHard(string toBranchOrSha1); + Task SetRemote(string name, string newUrl); + IDirectoryInfo Path { get; } + Task Clone(string cloneUrl, string? branch = null); +} diff --git a/src/TrashLib/Repo/VersionControl/IGitRepositoryFactory.cs b/src/TrashLib/Repo/VersionControl/IGitRepositoryFactory.cs new file mode 100644 index 00000000..fe17a38b --- /dev/null +++ b/src/TrashLib/Repo/VersionControl/IGitRepositoryFactory.cs @@ -0,0 +1,6 @@ +namespace TrashLib.Repo.VersionControl; + +public interface IGitRepositoryFactory +{ + Task CreateAndCloneIfNeeded(string repoUrl, string repoPath, string branch); +} diff --git a/src/VersionControl/VersionControlAutofacModule.cs b/src/TrashLib/Repo/VersionControl/VersionControlAutofacModule.cs similarity index 67% rename from src/VersionControl/VersionControlAutofacModule.cs rename to src/TrashLib/Repo/VersionControl/VersionControlAutofacModule.cs index beedf248..d12e1754 100644 --- a/src/VersionControl/VersionControlAutofacModule.cs +++ b/src/TrashLib/Repo/VersionControl/VersionControlAutofacModule.cs @@ -1,15 +1,14 @@ using Autofac; -using VersionControl.Wrappers; -namespace VersionControl; +namespace TrashLib.Repo.VersionControl; public class VersionControlAutofacModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As(); - builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); base.Load(builder); } } diff --git a/src/TrashLib/TrashLib.csproj b/src/TrashLib/TrashLib.csproj index 2d3bd0c1..10ec1d06 100644 --- a/src/TrashLib/TrashLib.csproj +++ b/src/TrashLib/TrashLib.csproj @@ -5,6 +5,7 @@ + @@ -19,6 +20,5 @@ - diff --git a/src/VersionControl.Tests/GitRepositoryFactoryTest.cs b/src/VersionControl.Tests/GitRepositoryFactoryTest.cs deleted file mode 100644 index 5791f894..00000000 --- a/src/VersionControl.Tests/GitRepositoryFactoryTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using AutoFixture.NUnit3; -using Common; -using FluentAssertions; -using LibGit2Sharp; -using NSubstitute; -using NUnit.Framework; -using TestLibrary.AutoFixture; -using TestLibrary.NSubstitute; -using VersionControl.Wrappers; - -namespace VersionControl.Tests; - -[TestFixture] -[Parallelizable(ParallelScope.All)] -public class GitRepositoryFactoryTest -{ - [Test, AutoMockData] - public void Delete_and_clone_when_repo_is_not_valid( - [Frozen] IRepositoryStaticWrapper wrapper, - [Frozen] IFileUtilities fileUtils, - GitRepositoryFactory sut) - { - wrapper.IsValid(Arg.Any()).Returns(false); - - sut.CreateAndCloneIfNeeded("repo_url", "repo_path", "branch"); - - Received.InOrder(() => - { - wrapper.IsValid("repo_path"); - fileUtils.DeleteReadOnlyDirectory("repo_path"); - wrapper.Clone("repo_url", "repo_path", - Verify.That(x => x.BranchName.Should().Be("branch"))); - }); - } - - [Test, AutoMockData] - public void No_delete_and_clone_when_repo_is_valid( - [Frozen] IRepositoryStaticWrapper wrapper, - [Frozen] IFileUtilities fileUtils, - GitRepositoryFactory sut) - { - wrapper.IsValid(Arg.Any()).Returns(true); - - sut.CreateAndCloneIfNeeded("repo_url", "repo_path", "branch"); - - wrapper.Received().IsValid("repo_path"); - fileUtils.DidNotReceiveWithAnyArgs().DeleteReadOnlyDirectory(default!); - wrapper.DidNotReceiveWithAnyArgs().Clone(default!, default!, default!); - } - - [Test, AutoMockData] - public void Set_remote_when_creating_repository( - [Frozen] IGitRepository repo, - GitRepositoryFactory sut) - { - sut.CreateAndCloneIfNeeded("repo_url", "repo_path", "branch"); - repo.Received().SetRemote("origin", "repo_url"); - } -} diff --git a/src/VersionControl.Tests/VersionControl.Tests.csproj b/src/VersionControl.Tests/VersionControl.Tests.csproj deleted file mode 100644 index 3001b8a1..00000000 --- a/src/VersionControl.Tests/VersionControl.Tests.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - false - - - - - - diff --git a/src/VersionControl/GitRepository.cs b/src/VersionControl/GitRepository.cs deleted file mode 100644 index fd6f4c71..00000000 --- a/src/VersionControl/GitRepository.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Common.Extensions; -using LibGit2Sharp; - -namespace VersionControl; - -public sealed class GitRepository : IGitRepository -{ - private readonly Lazy _repo; - - public GitRepository(string repoPath) - { - // Lazily construct the Repository object because it does too much work in its constructor - // We want to keep our own constructor here as thin as possible for DI and testability. - _repo = new Lazy(() => new Repository(repoPath)); - } - - public void Dispose() - { - if (_repo.IsValueCreated) - { - _repo.Value.Dispose(); - } - } - - public void ForceCheckout(string branch) - { - Commands.Checkout(_repo.Value, branch, new CheckoutOptions - { - CheckoutModifiers = CheckoutModifiers.Force - }); - } - - public void Fetch(string remote = "origin") - { - var origin = _repo.Value.Network.Remotes[remote]; - Commands.Fetch(_repo.Value, origin.Name, origin.FetchRefSpecs.Select(s => s.Specification), null, ""); - } - - public void ResetHard(string toBranchOrSha1) - { - var branch = _repo.Value.Branches.FirstOrDefault(b => b.FriendlyName.ContainsIgnoreCase(toBranchOrSha1)); - var commit = branch is not null ? branch.Tip : _repo.Value.Lookup(toBranchOrSha1); - _repo.Value.Reset(ResetMode.Hard, commit); - } - - public void SetRemote(string name, string newUrl) - { - _repo.Value.Network.Remotes.Update(name, updater => updater.Url = newUrl); - } -} diff --git a/src/VersionControl/GitRepositoryFactory.cs b/src/VersionControl/GitRepositoryFactory.cs deleted file mode 100644 index 0886c58a..00000000 --- a/src/VersionControl/GitRepositoryFactory.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Common; -using LibGit2Sharp; -using VersionControl.Wrappers; - -namespace VersionControl; - -public class GitRepositoryFactory : IGitRepositoryFactory -{ - private readonly IFileUtilities _fileUtils; - private readonly IRepositoryStaticWrapper _staticWrapper; - private readonly Func _repoFactory; - private readonly Func _progressBarFactory; - - public GitRepositoryFactory( - IFileUtilities fileUtils, - IRepositoryStaticWrapper staticWrapper, - Func repoFactory, - Func progressBarFactory) - { - _fileUtils = fileUtils; - _staticWrapper = staticWrapper; - _repoFactory = repoFactory; - _progressBarFactory = progressBarFactory; - } - - public IGitRepository CreateAndCloneIfNeeded(string repoUrl, string repoPath, string branch) - { - if (!_staticWrapper.IsValid(repoPath)) - { - DeleteAndCloneRepo(repoUrl, repoPath, branch); - } - - var repo = _repoFactory(repoPath); - repo.SetRemote("origin", repoUrl); - return repo; - } - - private void DeleteAndCloneRepo(string repoUrl, string repoPath, string branch) - { - _fileUtils.DeleteReadOnlyDirectory(repoPath); - - var progress = _progressBarFactory(); - progress.Description = "Fetching guide data\n"; - - _staticWrapper.Clone(repoUrl, repoPath, new CloneOptions - { - RecurseSubmodules = false, - BranchName = branch, - OnTransferProgress = gitProgress => - { - progress.ReportProgress.OnNext((float) gitProgress.ReceivedObjects / gitProgress.TotalObjects); - return true; - } - }); - } -} diff --git a/src/VersionControl/IGitRepository.cs b/src/VersionControl/IGitRepository.cs deleted file mode 100644 index 9c9c7e3a..00000000 --- a/src/VersionControl/IGitRepository.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VersionControl; - -public interface IGitRepository : IDisposable -{ - void ForceCheckout(string branch); - void Fetch(string remote = "origin"); - void ResetHard(string toBranchOrSha1); - void SetRemote(string name, string newUrl); -} diff --git a/src/VersionControl/IGitRepositoryFactory.cs b/src/VersionControl/IGitRepositoryFactory.cs deleted file mode 100644 index 028454d3..00000000 --- a/src/VersionControl/IGitRepositoryFactory.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace VersionControl; - -public interface IGitRepositoryFactory -{ - IGitRepository CreateAndCloneIfNeeded(string repoUrl, string repoPath, string branch); -} diff --git a/src/VersionControl/VersionControl.csproj b/src/VersionControl/VersionControl.csproj deleted file mode 100644 index 5e937997..00000000 --- a/src/VersionControl/VersionControl.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/VersionControl/Wrappers/IRepositoryStaticWrapper.cs b/src/VersionControl/Wrappers/IRepositoryStaticWrapper.cs deleted file mode 100644 index 31faa559..00000000 --- a/src/VersionControl/Wrappers/IRepositoryStaticWrapper.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LibGit2Sharp; - -namespace VersionControl.Wrappers; - -public interface IRepositoryStaticWrapper -{ - string Clone(string sourceUrl, string workdirPath, CloneOptions options); - bool IsValid(string path); -} diff --git a/src/VersionControl/Wrappers/LibGit2SharpRepositoryStaticWrapper.cs b/src/VersionControl/Wrappers/LibGit2SharpRepositoryStaticWrapper.cs deleted file mode 100644 index b523ad9b..00000000 --- a/src/VersionControl/Wrappers/LibGit2SharpRepositoryStaticWrapper.cs +++ /dev/null @@ -1,12 +0,0 @@ -using LibGit2Sharp; - -namespace VersionControl.Wrappers; - -public class LibGit2SharpRepositoryStaticWrapper : IRepositoryStaticWrapper -{ - public string Clone(string sourceUrl, string workdirPath, CloneOptions options) - => Repository.Clone(sourceUrl, workdirPath, options); - - public bool IsValid(string path) - => Repository.IsValid(path); -}