From 59e9207f0a35f35b3bbee9852102bfd424c8dbf2 Mon Sep 17 00:00:00 2001 From: sharpSteff <30927510+sharpSteff@users.noreply.github.com> Date: Fri, 28 Jun 2019 14:09:39 +0200 Subject: [PATCH] Feature: Optional Git cmd support (#800) * Fix uri issue with subdomain addresses like https://yourserver/git/YourOrga/Repo.git * removed System.IO using * Starting GitCmdDriver implementation. Works with gitea. additional input parameter is still needed * Starting GitCmdDriver implementation. Works with gitea. additional input parameter is still needed * added --gitclipath, -git as input parameter to specificy the path to git.exe. when setting this value GitCmdDriver will be initialied instead of Lib2SharpDriver. Added credentials handling into git clone * fix nullreference and integrate gitpath setting injection * disable warning for all type of configuration and plattform * cleanup * Replace executing external process with nukeeper implementation. Fixed the issue that remote add might have bad credentials * implemented the async approach in IGitDriver * implemented async approach in IGitDiscoveryDriver and IGitDriver Added GitCmdDiscovery Added Nukeeper.Git.Tests assembly for general git unit tests * fixed some unittests * fix build * try catch issue in linux * fixed CloneRepo for Linux * finish gitcmddiscoverydriver implementation * add documentation --- .../ICollaborationFactory.cs | 3 +- .../CollaborationPlatform/ISettingsReader.cs | 5 +- .../Configuration/FileSettings.cs | 2 + .../Configuration/UserSettings.cs | 2 + NuKeeper.Abstractions/Formats/UriFormats.cs | 7 +- .../Git/IGitDiscoveryDriver.cs | 11 +- NuKeeper.Abstractions/Git/IGitDriver.cs | 17 +- NuKeeper.Abstractions/LinqAsync.cs | 23 +++ .../AzureDevOpsSettingsReader.cs | 19 ++- NuKeeper.AzureDevOps/BaseSettingsReader.cs | 5 +- NuKeeper.AzureDevOps/TfsSettingsReader.cs | 19 ++- NuKeeper.AzureDevOps/VSTSSettingsReader.cs | 19 ++- NuKeeper.BitBucket/BitbucketSettingsReader.cs | 11 +- .../GitCmdDiscoveryDriverTest.cs | 63 +++++++ NuKeeper.Git.Tests/GitCmdDriverTest.cs | 52 ++++++ NuKeeper.Git.Tests/NuKeeper.Git.Tests.csproj | 28 ++++ NuKeeper.Git.Tests/TestDirectoryHelper.cs | 144 ++++++++++++++++ NuKeeper.Git/GitCmdDiscoveryDriver.cs | 131 +++++++++++++++ NuKeeper.Git/GitCmdDriver.cs | 106 ++++++++++++ NuKeeper.Git/LibGit2SharpDiscoveryDriver.cs | 34 ++-- NuKeeper.Git/LibGit2SharpDriver.cs | 158 ++++++++++-------- NuKeeper.Git/NuKeeper.Git.csproj | 5 + .../GitHubSettingsReaderTests.cs | 15 +- NuKeeper.GitHub/GitHubSettingsReader.cs | 19 ++- .../GiteaSettingsReaderTests.cs | 12 +- NuKeeper.Gitea/GiteaSettingsReader.cs | 13 +- .../GitlabSettingsReaderTests.cs | 9 +- .../NuKeeper.Gitlab.Tests.csproj | 4 + NuKeeper.Gitlab/GitlabSettingsReader.cs | 13 +- .../Commands/RepositoryCommandTests.cs | 6 +- .../Engine/CollaborationFactoryTests.cs | 21 +-- .../Engine/RepositoryUpdaterTests.cs | 2 +- NuKeeper.Tests/MockedGitDiscoveryDriver.cs | 23 +-- NuKeeper.sln | 6 + .../Collaboration/CollaborationFactory.cs | 11 +- .../Commands/CollaborationPlatformCommand.cs | 6 +- NuKeeper/Commands/CommandBase.cs | 16 +- NuKeeper/Commands/GlobalCommand.cs | 5 +- NuKeeper/Commands/InspectCommand.cs | 4 +- NuKeeper/Commands/LocalNuKeeperCommand.cs | 5 +- .../Commands/MultipleRepositoryCommand.cs | 5 +- NuKeeper/Commands/OrganisationCommand.cs | 5 +- NuKeeper/Commands/RepositoryCommand.cs | 9 +- NuKeeper/Commands/UpdateCommand.cs | 4 +- NuKeeper/Engine/GitRepositoryEngine.cs | 5 +- NuKeeper/Engine/Packages/PackageUpdater.cs | 10 +- NuKeeper/Engine/RepositoryUpdater.cs | 12 +- .../AzureDevOpsSettingsReaderTests.cs | 23 +-- .../TfsSettingsReaderTests.cs | 23 +-- .../VstsSettingsReaderTests.cs | 19 ++- .../BitBucketLocalSettingsReader.cs | 13 +- site/content/basics/configuration.md | 2 + 52 files changed, 908 insertions(+), 276 deletions(-) create mode 100644 NuKeeper.Git.Tests/GitCmdDiscoveryDriverTest.cs create mode 100644 NuKeeper.Git.Tests/GitCmdDriverTest.cs create mode 100644 NuKeeper.Git.Tests/NuKeeper.Git.Tests.csproj create mode 100644 NuKeeper.Git.Tests/TestDirectoryHelper.cs create mode 100644 NuKeeper.Git/GitCmdDiscoveryDriver.cs create mode 100644 NuKeeper.Git/GitCmdDriver.cs diff --git a/NuKeeper.Abstractions/CollaborationPlatform/ICollaborationFactory.cs b/NuKeeper.Abstractions/CollaborationPlatform/ICollaborationFactory.cs index 17b33bc08..582d554a7 100644 --- a/NuKeeper.Abstractions/CollaborationPlatform/ICollaborationFactory.cs +++ b/NuKeeper.Abstractions/CollaborationPlatform/ICollaborationFactory.cs @@ -1,11 +1,12 @@ using System; +using System.Threading.Tasks; using NuKeeper.Abstractions.Configuration; namespace NuKeeper.Abstractions.CollaborationPlatform { public interface ICollaborationFactory { - ValidationResult Initialise(Uri apiUri, string token, + Task Initialise(Uri apiUri, string token, ForkMode? forkModeFromSettings, Platform? platformFromSettings); ICommitWorder CommitWorder { get; } diff --git a/NuKeeper.Abstractions/CollaborationPlatform/ISettingsReader.cs b/NuKeeper.Abstractions/CollaborationPlatform/ISettingsReader.cs index 050cdf024..24b4a378d 100644 --- a/NuKeeper.Abstractions/CollaborationPlatform/ISettingsReader.cs +++ b/NuKeeper.Abstractions/CollaborationPlatform/ISettingsReader.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NuKeeper.Abstractions.Configuration; namespace NuKeeper.Abstractions.CollaborationPlatform @@ -7,9 +8,9 @@ public interface ISettingsReader { Platform Platform { get; } - bool CanRead(Uri repositoryUri); + Task CanRead(Uri repositoryUri); - RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch = null); + Task RepositorySettings(Uri repositoryUri, string targetBranch = null); void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings settings); } diff --git a/NuKeeper.Abstractions/Configuration/FileSettings.cs b/NuKeeper.Abstractions/Configuration/FileSettings.cs index 9dde5ffe8..f862d11f3 100644 --- a/NuKeeper.Abstractions/Configuration/FileSettings.cs +++ b/NuKeeper.Abstractions/Configuration/FileSettings.cs @@ -42,6 +42,8 @@ public class FileSettings public string BranchNamePrefix { get; set; } public bool? DeleteBranchAfterMerge { get; set; } + public string GitCliPath { get; set; } + public static FileSettings Empty() { return new FileSettings(); diff --git a/NuKeeper.Abstractions/Configuration/UserSettings.cs b/NuKeeper.Abstractions/Configuration/UserSettings.cs index 38a356447..7cd139b32 100644 --- a/NuKeeper.Abstractions/Configuration/UserSettings.cs +++ b/NuKeeper.Abstractions/Configuration/UserSettings.cs @@ -21,5 +21,7 @@ public class UserSettings public string OutputFileName { get; set; } public string Directory { get; set; } + + public string GitPath { get; set; } } } diff --git a/NuKeeper.Abstractions/Formats/UriFormats.cs b/NuKeeper.Abstractions/Formats/UriFormats.cs index c27625052..265516232 100644 --- a/NuKeeper.Abstractions/Formats/UriFormats.cs +++ b/NuKeeper.Abstractions/Formats/UriFormats.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using NuKeeper.Abstractions.Git; namespace NuKeeper.Abstractions.Formats @@ -23,12 +24,12 @@ public static Uri EnsureTrailingSlash(Uri uri) return new Uri(path + "/"); } - public static Uri GetRemoteUriFromLocalRepo(this Uri repositoryUri, IGitDiscoveryDriver discoveryDriver, string shouldMatchTo) + public static async Task GetRemoteUriFromLocalRepo(this Uri repositoryUri, IGitDiscoveryDriver discoveryDriver, string shouldMatchTo) { - if (discoveryDriver.IsGitRepo(repositoryUri)) + if (await discoveryDriver.IsGitRepo(repositoryUri)) { // Check the origin remotes - var origin = discoveryDriver.GetRemoteForPlatform(repositoryUri, shouldMatchTo); + var origin = await discoveryDriver.GetRemoteForPlatform(repositoryUri, shouldMatchTo); if (origin != null) { diff --git a/NuKeeper.Abstractions/Git/IGitDiscoveryDriver.cs b/NuKeeper.Abstractions/Git/IGitDiscoveryDriver.cs index 604965dde..efc1390c6 100644 --- a/NuKeeper.Abstractions/Git/IGitDiscoveryDriver.cs +++ b/NuKeeper.Abstractions/Git/IGitDiscoveryDriver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace NuKeeper.Abstractions.Git { @@ -10,18 +11,18 @@ public interface IGitDiscoveryDriver /// /// /// - bool IsGitRepo(Uri repositoryUri); + Task IsGitRepo(Uri repositoryUri); /// /// Get all the configured remotes /// /// - IEnumerable GetRemotes(Uri repositoryUri); + Task> GetRemotes(Uri repositoryUri); - Uri DiscoverRepo(Uri repositoryUri); + Task DiscoverRepo(Uri repositoryUri); - string GetCurrentHead(Uri repositoryUri); + Task GetCurrentHead(Uri repositoryUri); - GitRemote GetRemoteForPlatform(Uri repositoryUri, string platformHost); + Task GetRemoteForPlatform(Uri repositoryUri, string platformHost); } } diff --git a/NuKeeper.Abstractions/Git/IGitDriver.cs b/NuKeeper.Abstractions/Git/IGitDriver.cs index 27e78cd12..b38df0bfe 100644 --- a/NuKeeper.Abstractions/Git/IGitDriver.cs +++ b/NuKeeper.Abstractions/Git/IGitDriver.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NuKeeper.Abstractions.Inspections.Files; namespace NuKeeper.Abstractions.Git @@ -7,21 +8,21 @@ public interface IGitDriver { IFolder WorkingFolder { get; } - void Clone(Uri pullEndpoint); + Task Clone(Uri pullEndpoint); - void Clone(Uri pullEndpoint, string branchName); + Task Clone(Uri pullEndpoint, string branchName); - void AddRemote(string name, Uri endpoint); + Task AddRemote(string name, Uri endpoint); - void Checkout(string branchName); + Task Checkout(string branchName); - void CheckoutNewBranch(string branchName); + Task CheckoutNewBranch(string branchName); - void Commit(string message); + Task Commit(string message); - void Push(string remoteName, string branchName); + Task Push(string remoteName, string branchName); - string GetCurrentHead(); + Task GetCurrentHead(); } } diff --git a/NuKeeper.Abstractions/LinqAsync.cs b/NuKeeper.Abstractions/LinqAsync.cs index 20c7cdb06..9bfa6b17e 100644 --- a/NuKeeper.Abstractions/LinqAsync.cs +++ b/NuKeeper.Abstractions/LinqAsync.cs @@ -27,5 +27,28 @@ public static async Task> WhereAsync(this IEnumerable items .Where(x => x.PredTask.Result) .Select(x => x.Item); } + + /// + /// Async first or default implementation + /// + /// + /// + /// + /// + public static async Task FirstOrDefaultAsync(this IEnumerable items, Func> predicate) + { + var itemTaskList = items + .Select(item => new { Item = item, PredTask = predicate.Invoke(item) }) + .ToList(); + + await Task.WhenAll(itemTaskList.Select(x => x.PredTask)); + var firstOrDefault = itemTaskList.FirstOrDefault(x => x.PredTask.Result); + if (firstOrDefault == null) + { + return await Task.FromResult(default(T)); + } + + return firstOrDefault.Item; + } } } diff --git a/NuKeeper.AzureDevOps/AzureDevOpsSettingsReader.cs b/NuKeeper.AzureDevOps/AzureDevOpsSettingsReader.cs index 9be3bf2df..451c41efa 100644 --- a/NuKeeper.AzureDevOps/AzureDevOpsSettingsReader.cs +++ b/NuKeeper.AzureDevOps/AzureDevOpsSettingsReader.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.Configuration; using NuKeeper.Abstractions.Formats; @@ -20,7 +21,7 @@ public AzureDevOpsSettingsReader(IGitDiscoveryDriver gitDriver, IEnvironmentVari _gitDriver = gitDriver; } - public override bool CanRead(Uri repositoryUri) + public override async Task CanRead(Uri repositoryUri) { if (repositoryUri == null) { @@ -30,14 +31,14 @@ public override bool CanRead(Uri repositoryUri) // Is the specified folder already a git repository? if (repositoryUri.IsFile) { - repositoryUri = repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); + repositoryUri = await repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); } // Did we specify a Azure DevOps url? return repositoryUri?.Host.Contains(PlatformHost, StringComparison.OrdinalIgnoreCase) == true; } - public override RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch) + public override async Task RepositorySettings(Uri repositoryUri, string targetBranch) { if (repositoryUri == null) { @@ -45,7 +46,7 @@ public override RepositorySettings RepositorySettings(Uri repositoryUri, string } var settings = repositoryUri.IsFile - ? CreateSettingsFromLocal(repositoryUri, targetBranch) + ? await CreateSettingsFromLocal(repositoryUri, targetBranch) : CreateSettingsFromRemote(repositoryUri); if (settings == null) @@ -77,22 +78,22 @@ private RepositorySettings CreateSettingsFromRemote(Uri repositoryUri) return CreateRepositorySettings(org, repositoryUri, project, repoName); } - private RepositorySettings CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) + private async Task CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) { var remoteInfo = new RemoteInfo(); var localCopy = repositoryUri; - if (_gitDriver.IsGitRepo(repositoryUri)) + if (await _gitDriver.IsGitRepo(repositoryUri)) { // Check the origin remotes - var origin = _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); + var origin = await _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); if (origin != null) { - remoteInfo.LocalRepositoryUri = _gitDriver.DiscoverRepo(localCopy); // Set to the folder, because we found a remote git repository + remoteInfo.LocalRepositoryUri = await _gitDriver.DiscoverRepo(localCopy); // Set to the folder, because we found a remote git repository repositoryUri = origin.Url; remoteInfo.WorkingFolder = localCopy; - remoteInfo.BranchName = targetBranch ?? _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); + remoteInfo.BranchName = targetBranch ?? await _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); remoteInfo.RemoteName = origin.Name; } } diff --git a/NuKeeper.AzureDevOps/BaseSettingsReader.cs b/NuKeeper.AzureDevOps/BaseSettingsReader.cs index ea9ce3392..7d9b02cc8 100644 --- a/NuKeeper.AzureDevOps/BaseSettingsReader.cs +++ b/NuKeeper.AzureDevOps/BaseSettingsReader.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -16,7 +17,7 @@ public BaseSettingsReader(IEnvironmentVariablesProvider environmentVariablesProv public Platform Platform => Platform.AzureDevOps; - public abstract bool CanRead(Uri repositoryUri); + public abstract Task CanRead(Uri repositoryUri); public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings settings) { @@ -26,6 +27,6 @@ public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings se settings.ForkMode = settings.ForkMode ?? ForkMode.SingleRepositoryOnly; } - public abstract RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch); + public abstract Task RepositorySettings(Uri repositoryUri, string targetBranch); } } diff --git a/NuKeeper.AzureDevOps/TfsSettingsReader.cs b/NuKeeper.AzureDevOps/TfsSettingsReader.cs index 03da5cfd8..30b229ab0 100644 --- a/NuKeeper.AzureDevOps/TfsSettingsReader.cs +++ b/NuKeeper.AzureDevOps/TfsSettingsReader.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.Configuration; using NuKeeper.Abstractions.Formats; @@ -20,7 +21,7 @@ public TfsSettingsReader(IGitDiscoveryDriver gitDriver, IEnvironmentVariablesPro _gitDriver = gitDriver; } - public override bool CanRead(Uri repositoryUri) + public override async Task CanRead(Uri repositoryUri) { if (repositoryUri == null) { @@ -30,7 +31,7 @@ public override bool CanRead(Uri repositoryUri) // Is the specified folder already a git repository? if (repositoryUri.IsFile) { - repositoryUri = repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); + repositoryUri = await repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); } var path = repositoryUri.AbsolutePath; @@ -43,7 +44,7 @@ public override bool CanRead(Uri repositoryUri) return tfsInPath || tfsInHost; } - public override RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch) + public override async Task RepositorySettings(Uri repositoryUri, string targetBranch) { if (repositoryUri == null) { @@ -51,7 +52,7 @@ public override RepositorySettings RepositorySettings(Uri repositoryUri, string } var settings = repositoryUri.IsFile - ? CreateSettingsFromLocal(repositoryUri, targetBranch) + ? await CreateSettingsFromLocal(repositoryUri, targetBranch) : CreateSettingsFromRemote(repositoryUri); if (settings == null) { @@ -66,21 +67,21 @@ private static RepositorySettings CreateSettingsFromRemote(Uri repositoryUri) return RepositorySettings(repositoryUri); } - private RepositorySettings CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) + private async Task CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) { var remoteInfo = new RemoteInfo(); var localFolder = repositoryUri; - if (_gitDriver.IsGitRepo(repositoryUri)) + if (await _gitDriver.IsGitRepo(repositoryUri)) { // Check the origin remotes - var origin = _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); + var origin = await _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); if (origin != null) { - remoteInfo.LocalRepositoryUri = _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository + remoteInfo.LocalRepositoryUri = await _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository repositoryUri = origin.Url; - remoteInfo.BranchName = targetBranch ?? _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); + remoteInfo.BranchName = targetBranch ?? await _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); remoteInfo.RemoteName = origin.Name; remoteInfo.WorkingFolder = localFolder; } diff --git a/NuKeeper.AzureDevOps/VSTSSettingsReader.cs b/NuKeeper.AzureDevOps/VSTSSettingsReader.cs index d4d6c52d8..45216d84f 100644 --- a/NuKeeper.AzureDevOps/VSTSSettingsReader.cs +++ b/NuKeeper.AzureDevOps/VSTSSettingsReader.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.Configuration; using NuKeeper.Abstractions.Formats; @@ -20,7 +21,7 @@ public VstsSettingsReader(IGitDiscoveryDriver gitDriver, IEnvironmentVariablesPr _gitDriver = gitDriver; } - public override bool CanRead(Uri repositoryUri) + public override async Task CanRead(Uri repositoryUri) { if (repositoryUri == null) { @@ -30,13 +31,13 @@ public override bool CanRead(Uri repositoryUri) // Is the specified folder already a git repository? if (repositoryUri.IsFile) { - repositoryUri = repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); + repositoryUri = await repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); } return repositoryUri?.Host.Contains(PlatformHost, StringComparison.OrdinalIgnoreCase) == true; } - public override RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch) + public override async Task RepositorySettings(Uri repositoryUri, string targetBranch) { if (repositoryUri == null) { @@ -44,7 +45,7 @@ public override RepositorySettings RepositorySettings(Uri repositoryUri, string } var settings = repositoryUri.IsFile - ? CreateSettingsFromLocal(repositoryUri, targetBranch) + ? await CreateSettingsFromLocal(repositoryUri, targetBranch) : CreateSettingsFromRemote(repositoryUri); if (settings == null) { @@ -80,21 +81,21 @@ private RepositorySettings CreateSettingsFromRemote(Uri repositoryUri) } - private RepositorySettings CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) + private async Task CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) { var remoteInfo = new RemoteInfo(); var localFolder = repositoryUri; - if (_gitDriver.IsGitRepo(repositoryUri)) + if (await _gitDriver.IsGitRepo(repositoryUri)) { // Check the origin remotes - var origin = _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); + var origin = await _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); if (origin != null) { - remoteInfo.LocalRepositoryUri = _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository + remoteInfo.LocalRepositoryUri = await _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository repositoryUri = origin.Url; - remoteInfo.BranchName = targetBranch ?? _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); + remoteInfo.BranchName = targetBranch ?? await _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); remoteInfo.RemoteName = origin.Name; remoteInfo.WorkingFolder = localFolder; } diff --git a/NuKeeper.BitBucket/BitbucketSettingsReader.cs b/NuKeeper.BitBucket/BitbucketSettingsReader.cs index d46a0c522..7da84ea34 100644 --- a/NuKeeper.BitBucket/BitbucketSettingsReader.cs +++ b/NuKeeper.BitBucket/BitbucketSettingsReader.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -20,9 +21,9 @@ public BitbucketSettingsReader(IEnvironmentVariablesProvider environmentVariable private string Username { get; set; } - public bool CanRead(Uri repositoryUri) + public Task CanRead(Uri repositoryUri) { - return repositoryUri?.Host.Contains("bitbucket.org", StringComparison.OrdinalIgnoreCase) == true; + return Task.FromResult(repositoryUri?.Host.Contains("bitbucket.org", StringComparison.OrdinalIgnoreCase) == true); } public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings settings) @@ -33,7 +34,7 @@ public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings se settings.ForkMode = settings.ForkMode ?? ForkMode.SingleRepositoryOnly; } - public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch) + public Task RepositorySettings(Uri repositoryUri, string targetBranch) { if (repositoryUri == null) { @@ -66,13 +67,13 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra : repoName; var owner = pathParts[0]; - return new RepositorySettings + return Task.FromResult(new RepositorySettings { ApiUri = new Uri("https://api.bitbucket.org/2.0/"), RepositoryUri = repositoryUri, RepositoryName = repoName, RepositoryOwner = owner - }; + }); } } } diff --git a/NuKeeper.Git.Tests/GitCmdDiscoveryDriverTest.cs b/NuKeeper.Git.Tests/GitCmdDiscoveryDriverTest.cs new file mode 100644 index 000000000..a3e46dd74 --- /dev/null +++ b/NuKeeper.Git.Tests/GitCmdDiscoveryDriverTest.cs @@ -0,0 +1,63 @@ +using NuKeeper.Abstractions.Logging; +using NuKeeper.Git; +using NuKeeper.Inspection.Files; +using NuKeeper.Inspection.Logging; +using NUnit.Framework; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace NuKeeper.Git.Tests +{ + public class GitCmdDiscoveryDriverTest + { + private INuKeeperLogger _logger; + private string _pathTogit; + private string _repoPath; + + private const string _origin = "https://github.com/NuKeeperDotNet/NuKeeper.git"; + + [OneTimeSetUp] + public void Setup() + { + _logger = new ConfigurableLogger(); + _pathTogit = TestDirectoryHelper.DiscoverPathToGit(); + if (_pathTogit == null) + { + Assert.Ignore("no git implementation found!"); + } + + // create a local repo to test against + var folder = TestDirectoryHelper.GenerateRandomSubFolder(TestContext.CurrentContext.WorkDirectory); + var gitDriver = new GitCmdDriver(_pathTogit, _logger, new Folder(_logger, folder), new Abstractions.Git.GitUsernamePasswordCredentials()); + Assert.DoesNotThrowAsync(() => gitDriver.Clone(new Uri(_origin))); + _repoPath = folder.FullName; + } + + [OneTimeTearDown] + public void TearDown() + { + TestDirectoryHelper.DeleteDirectory(_repoPath); + } + + [Test] + public async Task ShouldDiscoverLocalRepo() + { + var gitDiscoveryDriver = new GitCmdDiscoveryDriver(_pathTogit, _logger); + var repo = await gitDiscoveryDriver.DiscoverRepo(new Uri(_repoPath)); + Assert.AreEqual(_origin, repo.AbsoluteUri); + } + + [Test] + public async Task ShouldGetRemotes() + { + var gitDiscoveryDriver = new GitCmdDiscoveryDriver(_pathTogit, _logger); + var classicgitDiscoveryDriver = new LibGit2SharpDiscoveryDriver(_logger); + var remotes = await gitDiscoveryDriver.GetRemotes(new Uri(_repoPath)); + var classicRemotes = await classicgitDiscoveryDriver.GetRemotes(new Uri(_repoPath)); + + // Lib2Sharp and GitCmd should have the same results + } + } +} diff --git a/NuKeeper.Git.Tests/GitCmdDriverTest.cs b/NuKeeper.Git.Tests/GitCmdDriverTest.cs new file mode 100644 index 000000000..0ed2b0c8a --- /dev/null +++ b/NuKeeper.Git.Tests/GitCmdDriverTest.cs @@ -0,0 +1,52 @@ +using NuKeeper.Abstractions.Logging; +using NuKeeper.Inspection.Files; +using NuKeeper.Inspection.Logging; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NuKeeper.Git.Tests +{ + public class GitCmdDriverTest + { + private INuKeeperLogger _logger; + private string _pathTogit; + + [OneTimeSetUp] + public void Setup() + { + _logger = new ConfigurableLogger(); + _pathTogit = TestDirectoryHelper.DiscoverPathToGit(); + } + + [TestCase("https://github.com/NuKeeperDotNet/NuKeeper.git")] + public async Task CloneRepoAndCheckout(string path) + { + if (_pathTogit == null) + { + Assert.Ignore("no git implementation found!"); + } + + var folder = TestDirectoryHelper.GenerateRandomSubFolder(TestContext.CurrentContext.WorkDirectory); + var gitDriver = new GitCmdDriver(_pathTogit, _logger, new Folder(_logger, folder), new Abstractions.Git.GitUsernamePasswordCredentials()); + Assert.DoesNotThrowAsync(() => gitDriver.Clone(new Uri(path))); + Assert.IsTrue(Directory.Exists(Path.Combine(folder.FullName, ".git")), "Local git repo should exist in {0}", folder.FullName); + + // Checkout master branch + Assert.DoesNotThrowAsync(() => gitDriver.Checkout("master")); + var head = await gitDriver.GetCurrentHead(); + Assert.AreEqual(head, "master"); + + // Checkout new branch + Assert.DoesNotThrowAsync(() => gitDriver.CheckoutNewBranch("newBranch")); + head = await gitDriver.GetCurrentHead(); + Assert.AreEqual("newBranch", head); + + TestDirectoryHelper.DeleteDirectory(folder.FullName); + } + } +} diff --git a/NuKeeper.Git.Tests/NuKeeper.Git.Tests.csproj b/NuKeeper.Git.Tests/NuKeeper.Git.Tests.csproj new file mode 100644 index 000000000..a3e655ddf --- /dev/null +++ b/NuKeeper.Git.Tests/NuKeeper.Git.Tests.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.0 + + false + + + + 1701;1702;CA2007 + + + + ..\CodeAnalysisRules.ruleset + + + + + + + + + + + + + + diff --git a/NuKeeper.Git.Tests/TestDirectoryHelper.cs b/NuKeeper.Git.Tests/TestDirectoryHelper.cs new file mode 100644 index 000000000..a021884ab --- /dev/null +++ b/NuKeeper.Git.Tests/TestDirectoryHelper.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; + +namespace NuKeeper.Git.Tests +{ + public static class TestDirectoryHelper + { + private static readonly Dictionary toRename = new Dictionary + { + { "dot_git", ".git" }, + { "gitmodules", ".gitmodules" }, + }; + + private static readonly Type[] whitelist = { typeof(IOException), typeof(UnauthorizedAccessException) }; + + public static string DiscoverPathToGit() + { + var env = Environment.GetEnvironmentVariable("PATH") + .Split(System.IO.Path.PathSeparator); + + foreach (var path in env) + { + try + { + var files = Directory.GetFiles(path).Where(x => Path.GetFileNameWithoutExtension(x) == "git"); + if (files.Any()) + { + return files.FirstOrDefault(); + } + } + catch(DirectoryNotFoundException) + { + } + } + + return null; + } + + public static DirectoryInfo GenerateRandomSubFolder(string folder) + { + var random = new Random(21838); + var directoryInfo = new DirectoryInfo(folder); + + var directory = "Random" + random.Next(); + while (directoryInfo.GetDirectories().Any(x => x.Name == directory)) + { + directory = "Random" + random.Next(); + } + + var info = directoryInfo.CreateSubdirectory(directory); + return info; + } + + public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) + { + // From http://stackoverflow.com/questions/58744/best-way-to-copy-the-entire-contents-of-a-directory-in-c/58779#58779 + + foreach (DirectoryInfo dir in source.GetDirectories()) + { + CopyFilesRecursively(dir, target.CreateSubdirectory(Rename(dir.Name))); + } + foreach (FileInfo file in source.GetFiles()) + { + file.CopyTo(Path.Combine(target.FullName, Rename(file.Name))); + } + } + + private static string Rename(string name) + { + return toRename.ContainsKey(name) ? toRename[name] : name; + } + + public static void DeleteDirectory(string directoryPath) + { + // http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502 + if (!Directory.Exists(directoryPath)) + { + Console.WriteLine($"Directory '{directoryPath}' is missing and can't be removed."); + return; + } + + NormalizeAttributes(directoryPath); + DeleteDirectory(directoryPath, maxAttempts: 5, initialTimeout: 16, timeoutFactor: 2); + } + + private static void NormalizeAttributes(string directoryPath) + { + string[] filePaths = Directory.GetFiles(directoryPath); + string[] subdirectoryPaths = Directory.GetDirectories(directoryPath); + + foreach (string filePath in filePaths) + { + File.SetAttributes(filePath, FileAttributes.Normal); + } + + foreach (string subdirectoryPath in subdirectoryPaths) + { + NormalizeAttributes(subdirectoryPath); + } + + File.SetAttributes(directoryPath, FileAttributes.Normal); + } + + private static void DeleteDirectory(string directoryPath, int maxAttempts, int initialTimeout, int timeoutFactor) + { + for (int attempt = 1; attempt <= maxAttempts; attempt++) + { + try + { + Directory.Delete(directoryPath, true); + return; + } + catch (Exception ex) + { + var caughtExceptionType = ex.GetType(); + + if (!whitelist.Any(knownExceptionType => knownExceptionType.IsAssignableFrom(caughtExceptionType))) + { + throw; + } + + if (attempt < maxAttempts) + { + Thread.Sleep(initialTimeout * (int)Math.Pow(timeoutFactor, attempt - 1)); + continue; + } + + Console.WriteLine("{0}The directory '{1}' could not be deleted ({2} attempts were made) due to a {3}: {4}" + + "{0}Most of the time, this is due to an external process accessing the files in the temporary repositories created during the test runs, and keeping a handle on the directory, thus preventing the deletion of those files." + + "{0}Known and common causes include:" + + "{0}- Windows Search Indexer (go to the Indexing Options, in the Windows Control Panel, and exclude the bin folder of LibGit2Sharp.Tests)" + + "{0}- Antivirus (exclude the bin folder of LibGit2Sharp.Tests from the paths scanned by your real-time antivirus)" + + "{0}- TortoiseGit (change the 'Icon Overlays' settings, e.g., adding the bin folder of LibGit2Sharp.Tests to 'Exclude paths' and appending an '*' to exclude all subfolders as well)", + Environment.NewLine, Path.GetFullPath(directoryPath), maxAttempts, caughtExceptionType, ex.Message); + } + } + } + } +} diff --git a/NuKeeper.Git/GitCmdDiscoveryDriver.cs b/NuKeeper.Git/GitCmdDiscoveryDriver.cs new file mode 100644 index 000000000..c64ab870f --- /dev/null +++ b/NuKeeper.Git/GitCmdDiscoveryDriver.cs @@ -0,0 +1,131 @@ +using NuKeeper.Abstractions.Git; +using NuKeeper.Abstractions.Logging; +using NuKeeper.Update.ProcessRunner; +using NuKeeper.Abstractions.Formats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Text.RegularExpressions; + +namespace NuKeeper.Git +{ + public class GitCmdDiscoveryDriver : IGitDiscoveryDriver + { + private readonly INuKeeperLogger _logger; + private string _pathGit; + + public GitCmdDiscoveryDriver(string pathToGit, INuKeeperLogger logger) + { + if (string.IsNullOrWhiteSpace(pathToGit)) + { + throw new ArgumentNullException(nameof(pathToGit)); + } + + if (Path.GetFileNameWithoutExtension(pathToGit) != "git") + { + throw new InvalidOperationException($"Invalid path '{pathToGit}'. Path must point to 'git' cmd"); + } + + _logger = logger; + _pathGit = pathToGit; + } + + public async Task DiscoverRepo(Uri repositoryUri) + { + var result = await StartGitProzess("config --get remote.origin.url", true, repositoryUri.LocalPath); + return new Uri(result); + } + + public async Task GetCurrentHead(Uri repositoryUri) + { + var getBranchHead = await StartGitProzess($"symbolic-ref -q --short HEAD", true, repositoryUri.LocalPath); + return string.IsNullOrEmpty(getBranchHead) ? + await StartGitProzess($"rev-parse HEAD", true, repositoryUri.LocalPath) : + getBranchHead; + } + + public async Task GetRemoteForPlatform(Uri repositoryUri, string platformHost) + { + var remotes = await GetRemotes(repositoryUri); + return remotes.FirstOrDefault(rm => rm.Url.Host.Contains(platformHost, StringComparison.OrdinalIgnoreCase)); + } + + public async Task> GetRemotes(Uri repositoryUri) + { + if (!await IsGitRepo(repositoryUri)) + { + return Enumerable.Empty(); + } + + var result = await StartGitProzess("remote -v", true); + + // result should look like "origin\thttps://github.com/nukeeper/NuKeeper.git (fetch)\norigin\thttps://github.com/nukeeper/NuKeeper.git (push)" + if (!string.IsNullOrWhiteSpace(result)) + { + var remoteList = new List(); + var remotes = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + + foreach (var remote in remotes) + { + var gitRemote = CreateGitRemoteFromString(remote); + if (gitRemote != null && !remoteList.Any(x => x.Name == gitRemote.Name)) + { + remoteList.Add(gitRemote); + } + } + + return remoteList; + } + + return null; + } + + public async Task IsGitRepo(Uri repositoryUri) + { + var discovered = await DiscoverRepo(repositoryUri); + if (discovered == null) + { + return false; + } + + return true; + } + + internal async Task StartGitProzess(string arguments, bool ensureSuccess, string workingFolder = "") + { + var process = new ExternalProcess(_logger); + var output = await process.Run(workingFolder, _pathGit, arguments, ensureSuccess); + return output.Output.TrimEnd(Environment.NewLine.ToCharArray()); + } + + private GitRemote CreateGitRemoteFromString(string remote) + { + var linkParser = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); + var match = linkParser.Match(remote); + if (match.Success) + { + if (Uri.TryCreate(match.Value, UriKind.Absolute, out Uri repositoryUri)) + { + var remoteName = remote.Split(new [] { "\t"}, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(remoteName)) + { + return new GitRemote + { + Name = remoteName, + Url = repositoryUri + }; + } + } + else + { + _logger.Normal($"Cannot parse {match.Value} to URI. SSH remote is currently not supported"); + } + } + + return null; + } + } +} diff --git a/NuKeeper.Git/GitCmdDriver.cs b/NuKeeper.Git/GitCmdDriver.cs new file mode 100644 index 000000000..b1f37e147 --- /dev/null +++ b/NuKeeper.Git/GitCmdDriver.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using NuKeeper.Abstractions.CollaborationModels; +using NuKeeper.Abstractions.Git; +using NuKeeper.Abstractions.Inspections.Files; +using NuKeeper.Abstractions.Logging; +using NuKeeper.Update.ProcessRunner; + +namespace NuKeeper.Git +{ + public class GitCmdDriver : IGitDriver + { + + private GitUsernamePasswordCredentials _gitCredentials; + private string _pathGit; + private INuKeeperLogger _logger; + + public GitCmdDriver(string pathToGit, INuKeeperLogger logger, + IFolder workingFolder, GitUsernamePasswordCredentials credentials) + { + if (string.IsNullOrWhiteSpace(pathToGit)) + { + throw new ArgumentNullException(nameof(pathToGit)); + } + + if (Path.GetFileNameWithoutExtension(pathToGit) != "git") + { + throw new InvalidOperationException($"Invalid path '{pathToGit}'. Path must point to 'git' cmd"); + } + + _pathGit = pathToGit; + _logger = logger; + WorkingFolder = workingFolder ?? throw new ArgumentNullException(nameof(workingFolder)); + _gitCredentials = credentials ?? throw new ArgumentNullException(nameof(credentials)); + } + + public IFolder WorkingFolder { get; } + + public async Task AddRemote(string name, Uri endpoint) + { + await StartGitProzess($"remote add {name} {CreateCredentialsUri(endpoint, _gitCredentials)}", true); + } + + public async Task Checkout(string branchName) + { + await StartGitProzess($"checkout -b {branchName} origin/{branchName}", false); + } + + public async Task CheckoutNewBranch(string branchName) + { + await StartGitProzess($"checkout -b {branchName}", true); + } + + public async Task Clone(Uri pullEndpoint) + { + await Clone(pullEndpoint, null); + } + + public async Task Clone(Uri pullEndpoint, string branchName) + { + _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}"); + var branchparam = branchName == null ? "" : $" -b {branchName}"; + await StartGitProzess($"clone{branchparam} {CreateCredentialsUri(pullEndpoint, _gitCredentials)} .", true); // Clone into current folder + _logger.Detailed("Git clone complete"); + } + + public async Task Commit(string message) + { + _logger.Detailed($"Git commit with message '{message}'"); + await StartGitProzess($"commit -a -m \"{message}\"", true); + } + + public async Task GetCurrentHead() + { + var getBranchHead = await StartGitProzess($"symbolic-ref -q --short HEAD", true); + return string.IsNullOrEmpty(getBranchHead) ? + await StartGitProzess($"rev-parse HEAD", true) : + getBranchHead; + } + + public async Task Push(string remoteName, string branchName) + { + _logger.Detailed($"Git push to {remoteName}/{branchName}"); + await StartGitProzess($"push {remoteName} {branchName}", true); + } + + + private async Task StartGitProzess(string arguments, bool ensureSuccess) + { + var process = new ExternalProcess(_logger); + var output = await process.Run(WorkingFolder.FullPath, _pathGit, arguments, ensureSuccess); + return output.Output.TrimEnd(Environment.NewLine.ToCharArray()); + } + + private Uri CreateCredentialsUri(Uri pullEndpoint, GitUsernamePasswordCredentials gitCredentials) + { + if (_gitCredentials == null) + { + return pullEndpoint; + } + + return new UriBuilder(pullEndpoint) { UserName = gitCredentials.Username, Password = gitCredentials.Password }.Uri; + } + } +} diff --git a/NuKeeper.Git/LibGit2SharpDiscoveryDriver.cs b/NuKeeper.Git/LibGit2SharpDiscoveryDriver.cs index 43e64e6e2..5eef5e72c 100644 --- a/NuKeeper.Git/LibGit2SharpDiscoveryDriver.cs +++ b/NuKeeper.Git/LibGit2SharpDiscoveryDriver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading.Tasks; using LibGit2Sharp; using NuKeeper.Abstractions; using NuKeeper.Abstractions.Formats; @@ -19,9 +20,9 @@ public LibGit2SharpDiscoveryDriver(INuKeeperLogger logger) _logger = logger; } - public bool IsGitRepo(Uri repositoryUri) + public async Task IsGitRepo(Uri repositoryUri) { - var discovered = DiscoverRepo(repositoryUri); + var discovered = await DiscoverRepo(repositoryUri); if (discovered == null) { return false; @@ -30,9 +31,9 @@ public bool IsGitRepo(Uri repositoryUri) return Repository.IsValid(discovered.AbsolutePath); } - public IEnumerable GetRemotes(Uri repositoryUri) + public async Task> GetRemotes(Uri repositoryUri) { - if (!IsGitRepo(repositoryUri)) + if (!await IsGitRepo(repositoryUri)) { return Enumerable.Empty(); } @@ -65,21 +66,24 @@ public IEnumerable GetRemotes(Uri repositoryUri) } } - public Uri DiscoverRepo(Uri repositoryUri) + public Task DiscoverRepo(Uri repositoryUri) { - var discovery = Repository.Discover(repositoryUri.AbsolutePath); - - if (string.IsNullOrEmpty(discovery)) + return Task.Run(() => { - return null; - } + var discovery = Repository.Discover(repositoryUri.AbsolutePath); + + if (string.IsNullOrEmpty(discovery)) + { + return null; + } - return new Uri(discovery); + return new Uri(discovery); + }); } - public string GetCurrentHead(Uri repositoryUri) + public async Task GetCurrentHead(Uri repositoryUri) { - var repoRoot = DiscoverRepo(repositoryUri).AbsolutePath; + var repoRoot = (await DiscoverRepo(repositoryUri)).AbsolutePath; using (var repo = new Repository(repoRoot)) { var repoHeadBranch = repo.Branches. @@ -94,9 +98,9 @@ public string GetCurrentHead(Uri repositoryUri) } } - public GitRemote GetRemoteForPlatform(Uri repositoryUri, string platformHost) + public async Task GetRemoteForPlatform(Uri repositoryUri, string platformHost) { - var remotes = GetRemotes(repositoryUri); + var remotes = await GetRemotes(repositoryUri); return remotes .FirstOrDefault(rm => rm.Url.Host.Contains(platformHost, StringComparison.OrdinalIgnoreCase)); } diff --git a/NuKeeper.Git/LibGit2SharpDriver.cs b/NuKeeper.Git/LibGit2SharpDriver.cs index 7b2e49e4c..f848cbe6a 100644 --- a/NuKeeper.Git/LibGit2SharpDriver.cs +++ b/NuKeeper.Git/LibGit2SharpDriver.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using LibGit2Sharp; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationModels; @@ -40,24 +41,27 @@ public class LibGit2SharpDriver : IGitDriver _identity = GetUserIdentity(user); } - public void Clone(Uri pullEndpoint) + public async Task Clone(Uri pullEndpoint) { - Clone(pullEndpoint, null); + await Clone(pullEndpoint, null); } - public void Clone(Uri pullEndpoint, string branchName) + public Task Clone(Uri pullEndpoint, string branchName) { - _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}"); - - Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath, - new CloneOptions - { - CredentialsProvider = UsernamePasswordCredentials, - OnTransferProgress = OnTransferProgress, - BranchName = branchName - }); - - _logger.Detailed("Git clone complete"); + return Task.Run(() => + { + _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}"); + + Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath, + new CloneOptions + { + CredentialsProvider = UsernamePasswordCredentials, + OnTransferProgress = OnTransferProgress, + BranchName = branchName + }); + + _logger.Detailed("Git clone complete"); + }); } private bool OnTransferProgress(TransferProgress progress) @@ -71,46 +75,55 @@ private bool OnTransferProgress(TransferProgress progress) return true; } - public void AddRemote(string name, Uri endpoint) + public Task AddRemote(string name, Uri endpoint) { - using (var repo = MakeRepo()) + return Task.Run(() => { - repo.Network.Remotes.Add(name, endpoint.AbsoluteUri); - } + using (var repo = MakeRepo()) + { + repo.Network.Remotes.Add(name, endpoint.AbsoluteUri); + } + }); } - public void Checkout(string branchName) + public Task Checkout(string branchName) { - _logger.Detailed($"Git checkout '{branchName}'"); - using (var repo = MakeRepo()) + return Task.Run(() => { - if (BranchExists(branchName)) + _logger.Detailed($"Git checkout '{branchName}'"); + using (var repo = MakeRepo()) { - GitCommands.Checkout(repo, repo.Branches[branchName]); + if (BranchExists(branchName)) + { + GitCommands.Checkout(repo, repo.Branches[branchName]); + } + else + { + throw new NuKeeperException( + $"Git Cannot checkout branch: the branch named '{branchName}' doesn't exist"); + } } - else - { - throw new NuKeeperException( - $"Git Cannot checkout branch: the branch named '{branchName}' doesn't exist"); - } - } + }); } - public void CheckoutNewBranch(string branchName) + public Task CheckoutNewBranch(string branchName) { - var qualifiedBranchName = "origin/" + branchName; - if (BranchExists(qualifiedBranchName)) + return Task.Run(() => { - _logger.Normal($"Git Cannot checkout new branch: a branch named '{qualifiedBranchName}' already exists"); - return; - } + var qualifiedBranchName = "origin/" + branchName; + if (BranchExists(qualifiedBranchName)) + { + _logger.Normal($"Git Cannot checkout new branch: a branch named '{qualifiedBranchName}' already exists"); + return; + } - _logger.Detailed($"Git checkout new branch '{branchName}'"); - using (var repo = MakeRepo()) - { - var branch = repo.CreateBranch(branchName); - GitCommands.Checkout(repo, branch); - } + _logger.Detailed($"Git checkout new branch '{branchName}'"); + using (var repo = MakeRepo()) + { + var branch = repo.CreateBranch(branchName); + GitCommands.Checkout(repo, branch); + } + }); } private bool BranchExists(string branchName) @@ -123,15 +136,18 @@ private bool BranchExists(string branchName) } } - public void Commit(string message) + public Task Commit(string message) { - _logger.Detailed($"Git commit with message '{message}'"); - using (var repo = MakeRepo()) + return Task.Run(() => { - var signature = GetSignature(repo); - GitCommands.Stage(repo, "*"); - repo.Commit(message, signature, signature); - } + _logger.Detailed($"Git commit with message '{message}'"); + using (var repo = MakeRepo()) + { + var signature = GetSignature(repo); + GitCommands.Stage(repo, "*"); + repo.Commit(message, signature, signature); + } + }); } private Signature GetSignature(Repository repo) @@ -152,35 +168,41 @@ private Signature GetSignature(Repository repo) return repoSignature; } - public void Push(string remoteName, string branchName) + public Task Push(string remoteName, string branchName) { - _logger.Detailed($"Git push to {remoteName}/{branchName}"); - - using (var repo = MakeRepo()) + return Task.Run(() => { + _logger.Detailed($"Git push to {remoteName}/{branchName}"); - var localBranch = repo.Branches - .Single(b => b.CanonicalName.EndsWith(branchName, StringComparison.OrdinalIgnoreCase)); - var remote = repo.Network.Remotes - .Single(b => b.Name.EndsWith(remoteName, StringComparison.OrdinalIgnoreCase)); + using (var repo = MakeRepo()) + { - repo.Branches.Update(localBranch, - b => b.Remote = remote.Name, - b => b.UpstreamBranch = localBranch.CanonicalName); + var localBranch = repo.Branches + .Single(b => b.CanonicalName.EndsWith(branchName, StringComparison.OrdinalIgnoreCase)); + var remote = repo.Network.Remotes + .Single(b => b.Name.EndsWith(remoteName, StringComparison.OrdinalIgnoreCase)); - repo.Network.Push(localBranch, new PushOptions - { - CredentialsProvider = UsernamePasswordCredentials - }); - } + repo.Branches.Update(localBranch, + b => b.Remote = remote.Name, + b => b.UpstreamBranch = localBranch.CanonicalName); + + repo.Network.Push(localBranch, new PushOptions + { + CredentialsProvider = UsernamePasswordCredentials + }); + } + }); } - public string GetCurrentHead() + public Task GetCurrentHead() { - using (var repo = MakeRepo()) + return Task.Run(() => { - return repo.Branches.Single(b => b.IsCurrentRepositoryHead).FriendlyName; - } + using (var repo = MakeRepo()) + { + return repo.Branches.Single(b => b.IsCurrentRepositoryHead).FriendlyName; + } + }); } private Repository MakeRepo() diff --git a/NuKeeper.Git/NuKeeper.Git.csproj b/NuKeeper.Git/NuKeeper.Git.csproj index e7b4f72d3..103d2bca7 100644 --- a/NuKeeper.Git/NuKeeper.Git.csproj +++ b/NuKeeper.Git/NuKeeper.Git.csproj @@ -10,10 +10,15 @@ + ..\CodeAnalysisRules.ruleset + + 1701;1702,CA1031 + + diff --git a/NuKeeper.GitHub.Tests/GitHubSettingsReaderTests.cs b/NuKeeper.GitHub.Tests/GitHubSettingsReaderTests.cs index 6d04af550..a44fc13a7 100644 --- a/NuKeeper.GitHub.Tests/GitHubSettingsReaderTests.cs +++ b/NuKeeper.GitHub.Tests/GitHubSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; @@ -62,18 +63,18 @@ public void AuthSettings_GetsCorrectSettingsFromEnvironment() [TestCase(null)] [TestCase("htps://missingt.com")] - public void InvalidUrlReturnsNull(string value) + public async Task InvalidUrlReturnsNull(string value) { var testUri = value == null ? null : new Uri(value); - var canRead = _gitHubSettingsReader.CanRead(testUri); + var canRead = await _gitHubSettingsReader.CanRead(testUri); Assert.IsFalse(canRead); } [Test] - public void RepositorySettings_GetsCorrectSettings() + public async Task RepositorySettings_GetsCorrectSettings() { - var settings = _gitHubSettingsReader.RepositorySettings(new Uri("https://github.com/owner/reponame.git")); + var settings = await _gitHubSettingsReader.RepositorySettings(new Uri("https://github.com/owner/reponame.git")); Assert.IsNotNull(settings); Assert.AreEqual(new Uri("https://github.com/owner/reponame.git"), settings.RepositoryUri); @@ -82,10 +83,10 @@ public void RepositorySettings_GetsCorrectSettings() } [Test] - public void RepositorySettings_GetsCorrectSettingsWithTargetBranch() + public async Task RepositorySettings_GetsCorrectSettingsWithTargetBranch() { var settings = - _gitHubSettingsReader.RepositorySettings(new Uri("https://github.com/owner/reponame.git"), "Feature1"); + await _gitHubSettingsReader.RepositorySettings(new Uri("https://github.com/owner/reponame.git"), "Feature1"); Assert.IsNotNull(settings); Assert.AreEqual(new Uri("https://github.com/owner/reponame.git"), settings.RepositoryUri); @@ -100,7 +101,7 @@ public void RepositorySettings_GetsCorrectSettingsWithTargetBranch() public void RepositorySettings_InvalidUrlReturnsNull(string value) { var testUri = value == null ? null : new Uri(value); - Assert.Throws(() => _gitHubSettingsReader.RepositorySettings(testUri)); + Assert.ThrowsAsync(() => _gitHubSettingsReader.RepositorySettings(testUri)); } } } diff --git a/NuKeeper.GitHub/GitHubSettingsReader.cs b/NuKeeper.GitHub/GitHubSettingsReader.cs index 2fdab2f75..d80e62a76 100644 --- a/NuKeeper.GitHub/GitHubSettingsReader.cs +++ b/NuKeeper.GitHub/GitHubSettingsReader.cs @@ -5,6 +5,7 @@ using System.Linq; using NuKeeper.Abstractions.Formats; using NuKeeper.Abstractions.Git; +using System.Threading.Tasks; namespace NuKeeper.GitHub { @@ -24,7 +25,7 @@ public GitHubSettingsReader(IGitDiscoveryDriver gitDriver, IEnvironmentVariables public Platform Platform => Platform.GitHub; - public bool CanRead(Uri repositoryUri) + public async Task CanRead(Uri repositoryUri) { if (repositoryUri == null) { @@ -34,7 +35,7 @@ public bool CanRead(Uri repositoryUri) // Is the specified folder already a git repository? if (repositoryUri.IsFile) { - repositoryUri = repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); + repositoryUri = await repositoryUri.GetRemoteUriFromLocalRepo(_gitDriver, PlatformHost); } return repositoryUri?.Host.Contains(PlatformHost, StringComparison.OrdinalIgnoreCase) == true; @@ -47,14 +48,14 @@ public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings se settings.ForkMode = settings.ForkMode ?? ForkMode.PreferFork; } - public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch = null) + public async Task RepositorySettings(Uri repositoryUri, string targetBranch = null) { if (repositoryUri == null) { throw new NuKeeperException($"The provided uri was is not in the correct format. Provided null and format should be {UrlPattern}"); } - var settings = repositoryUri.IsFile ? CreateSettingsFromLocal(repositoryUri, targetBranch) : CreateSettingsFromRemote(repositoryUri, targetBranch); + var settings = repositoryUri.IsFile ? await CreateSettingsFromLocal(repositoryUri, targetBranch) : CreateSettingsFromRemote(repositoryUri, targetBranch); if (settings == null) { throw new NuKeeperException($"The provided uri was is not in the correct format. Provided {repositoryUri} and format should be {UrlPattern}"); @@ -63,21 +64,21 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra return settings; } - private RepositorySettings CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) + private async Task CreateSettingsFromLocal(Uri repositoryUri, string targetBranch) { var remoteInfo = new RemoteInfo(); var localFolder = repositoryUri; - if (_gitDriver.IsGitRepo(repositoryUri)) + if (await _gitDriver.IsGitRepo(repositoryUri)) { // Check the origin remotes - var origin = _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); + var origin = await _gitDriver.GetRemoteForPlatform(repositoryUri, PlatformHost); if (origin != null) { - remoteInfo.LocalRepositoryUri = _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository + remoteInfo.LocalRepositoryUri = await _gitDriver.DiscoverRepo(repositoryUri); // Set to the folder, because we found a remote git repository repositoryUri = origin.Url; - remoteInfo.BranchName = targetBranch ?? _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); + remoteInfo.BranchName = targetBranch ?? await _gitDriver.GetCurrentHead(remoteInfo.LocalRepositoryUri); remoteInfo.RemoteName = origin.Name; remoteInfo.WorkingFolder = localFolder; } diff --git a/NuKeeper.Gitea.Tests/GiteaSettingsReaderTests.cs b/NuKeeper.Gitea.Tests/GiteaSettingsReaderTests.cs index 22fd02387..34bbe0c8a 100644 --- a/NuKeeper.Gitea.Tests/GiteaSettingsReaderTests.cs +++ b/NuKeeper.Gitea.Tests/GiteaSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -34,7 +35,7 @@ public void ReturnsCorrectPlatform() [Test] public void CanRead_NoException_OnBadUri() { - Assert.DoesNotThrow(() => _giteaSettingsReader.CanRead(new Uri("https://try.gitea.io/"))); + Assert.DoesNotThrowAsync(() => _giteaSettingsReader.CanRead(new Uri("https://try.gitea.io/"))); } [Test] @@ -53,10 +54,9 @@ public void UpdatesAuthenticationTokenFromTheEnvironment() } [Test] - public void AssumesItCanReadGiteaUrls() + public async Task AssumesItCanReadGiteaUrls() { - var canRead = _giteaSettingsReader.CanRead(new Uri("https://try.gitea.io/SharpSteff/NuKeeper-TestFork")); - + var canRead = await _giteaSettingsReader.CanRead(new Uri("https://try.gitea.io/SharpSteff/NuKeeper-TestFork")); Assert.AreEqual(true, canRead); } @@ -70,10 +70,10 @@ public void AssumesItCanNotReadGitHubUrls() [TestCase(null)] [TestCase("master")] - public void GetsCorrectSettingsFromTheUrl(string targetBranch) + public async Task GetsCorrectSettingsFromTheUrl(string targetBranch) { var repositoryUri = new Uri("https://try.gitea.io/SharpSteff/NuKeeper-TestFork"); - var repositorySettings = _giteaSettingsReader.RepositorySettings(repositoryUri, targetBranch); + var repositorySettings = await _giteaSettingsReader.RepositorySettings(repositoryUri, targetBranch); Assert.IsNotNull(repositorySettings); Assert.AreEqual(new Uri("https://try.gitea.io/api/v1/"), repositorySettings.ApiUri); diff --git a/NuKeeper.Gitea/GiteaSettingsReader.cs b/NuKeeper.Gitea/GiteaSettingsReader.cs index 607b209d7..9045ba95b 100644 --- a/NuKeeper.Gitea/GiteaSettingsReader.cs +++ b/NuKeeper.Gitea/GiteaSettingsReader.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -22,7 +23,7 @@ public GiteaSettingsReader(IEnvironmentVariablesProvider environmentVariablesPro public Platform Platform => Platform.Gitea; - public bool CanRead(Uri repositoryUri) + public async Task CanRead(Uri repositoryUri) { if (repositoryUri == null || repositoryUri.Segments == null || repositoryUri.Segments.Length < 3) return false; @@ -38,7 +39,7 @@ public bool CanRead(Uri repositoryUri) client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); - HttpResponseMessage response = client.GetAsync($"swagger.v1.json").Result; + HttpResponseMessage response = await client.GetAsync($"swagger.v1.json"); if (response.IsSuccessStatusCode) { return true; @@ -59,7 +60,7 @@ public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings se settings.Token = Concat.FirstValue(envToken, settings.Token); } - public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch = null) + public Task RepositorySettings(Uri repositoryUri, string targetBranch = null) { if (repositoryUri == null) { @@ -86,7 +87,7 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra var baseAddress = GetBaseAddress(repositoryUri); var apiUri = new Uri(baseAddress, ApiBaseAdress); - return new RepositorySettings + return Task.FromResult(new RepositorySettings { ApiUri = apiUri, RepositoryUri = repositoryUri, @@ -95,10 +96,10 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra RemoteInfo = targetBranch == null ? null : new RemoteInfo { BranchName = targetBranch } - }; + }); } - private Uri GetBaseAddress (Uri repoUri) + private Uri GetBaseAddress(Uri repoUri) { var newSegments = repoUri.Segments.Take(repoUri.Segments.Length - 2).ToArray(); var ub = new UriBuilder(repoUri); diff --git a/NuKeeper.Gitlab.Tests/GitlabSettingsReaderTests.cs b/NuKeeper.Gitlab.Tests/GitlabSettingsReaderTests.cs index 664f7a976..19718a986 100644 --- a/NuKeeper.Gitlab.Tests/GitlabSettingsReaderTests.cs +++ b/NuKeeper.Gitlab.Tests/GitlabSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -44,19 +45,19 @@ public void UpdatesAuthenticationTokenFromTheEnvironment() } [Test] - public void AssumesItCanReadGitLabUrls() + public async Task AssumesItCanReadGitLabUrls() { - var canRead = _gitlabSettingsReader.CanRead(new Uri("https://gitlab.com/user/projectname.git")); + var canRead = await _gitlabSettingsReader.CanRead(new Uri("https://gitlab.com/user/projectname.git")); Assert.AreEqual(true, canRead); } [TestCase(null)] [TestCase("master")] - public void GetsCorrectSettingsFromTheUrl(string targetBranch) + public async Task GetsCorrectSettingsFromTheUrl(string targetBranch) { var repositoryUri = new Uri("https://gitlab.com/user/projectname.git"); - var repositorySettings = _gitlabSettingsReader.RepositorySettings(repositoryUri, targetBranch); + var repositorySettings = await _gitlabSettingsReader.RepositorySettings(repositoryUri, targetBranch); Assert.IsNotNull(repositorySettings); Assert.AreEqual(new Uri("https://gitlab.com/api/v4/"), repositorySettings.ApiUri); diff --git a/NuKeeper.Gitlab.Tests/NuKeeper.Gitlab.Tests.csproj b/NuKeeper.Gitlab.Tests/NuKeeper.Gitlab.Tests.csproj index 77e06affe..9c1407d88 100644 --- a/NuKeeper.Gitlab.Tests/NuKeeper.Gitlab.Tests.csproj +++ b/NuKeeper.Gitlab.Tests/NuKeeper.Gitlab.Tests.csproj @@ -6,6 +6,10 @@ false + + ..\CodeAnalysisRules.ruleset + + runtime; build; native; contentfiles; analyzers diff --git a/NuKeeper.Gitlab/GitlabSettingsReader.cs b/NuKeeper.Gitlab/GitlabSettingsReader.cs index 16cd8b8b9..af14f7eb7 100644 --- a/NuKeeper.Gitlab/GitlabSettingsReader.cs +++ b/NuKeeper.Gitlab/GitlabSettingsReader.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -20,12 +21,12 @@ public GitlabSettingsReader(IEnvironmentVariablesProvider environmentVariablesPr public Platform Platform => Platform.GitLab; - public bool CanRead(Uri repositoryUri) + public Task CanRead(Uri repositoryUri) { if (repositoryUri == null) - return false; + return Task.FromResult(false); - return repositoryUri.Host.Contains("gitlab", StringComparison.OrdinalIgnoreCase); + return Task.FromResult(repositoryUri.Host.Contains("gitlab", StringComparison.OrdinalIgnoreCase)); } public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings settings) @@ -35,7 +36,7 @@ public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings se settings.Token = Concat.FirstValue(envToken, settings.Token); } - public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch = null) + public Task RepositorySettings(Uri repositoryUri, string targetBranch = null) { if (repositoryUri == null) { @@ -60,7 +61,7 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra var uriBuilder = new UriBuilder(repositoryUri) { Path = "/api/v4/" }; - return new RepositorySettings + return Task.FromResult(new RepositorySettings { ApiUri = uriBuilder.Uri, RepositoryUri = repositoryUri, @@ -69,7 +70,7 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra RemoteInfo = targetBranch == null ? null : new RemoteInfo { BranchName = targetBranch } - }; + }); } } } diff --git a/NuKeeper.Tests/Commands/RepositoryCommandTests.cs b/NuKeeper.Tests/Commands/RepositoryCommandTests.cs index 4e570a806..49a3f513f 100644 --- a/NuKeeper.Tests/Commands/RepositoryCommandTests.cs +++ b/NuKeeper.Tests/Commands/RepositoryCommandTests.cs @@ -100,7 +100,7 @@ public async Task ShouldInitialiseCollaborationFactory() await command.OnExecute(); - collaborationFactory + await collaborationFactory .Received(1) .Initialise( Arg.Is(new Uri("https://api.github.com")), @@ -135,7 +135,7 @@ public async Task ShouldInitialiseForkModeFromFile() await command.OnExecute(); - collaborationFactory + await collaborationFactory .Received(1) .Initialise( Arg.Is(new Uri("https://api.github.com")), @@ -170,7 +170,7 @@ public async Task ShouldInitialisePlatformFromFile() await command.OnExecute(); - collaborationFactory + await collaborationFactory .Received(1) .Initialise( Arg.Is(new Uri("https://api.github.com")), diff --git a/NuKeeper.Tests/Engine/CollaborationFactoryTests.cs b/NuKeeper.Tests/Engine/CollaborationFactoryTests.cs index d0b95f5e4..ec0da998c 100644 --- a/NuKeeper.Tests/Engine/CollaborationFactoryTests.cs +++ b/NuKeeper.Tests/Engine/CollaborationFactoryTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -45,11 +46,11 @@ public void UnitialisedFactoryHasNulls() } [Test] - public void UnknownApiReturnsUnableToFindPlatform() + public async Task UnknownApiReturnsUnableToFindPlatform() { var collaborationFactory = GetCollaborationFactory(); - var result = collaborationFactory.Initialise( + var result = await collaborationFactory.Initialise( new Uri("https://unknown.com/"), null, ForkMode.SingleRepositoryOnly, null); @@ -59,11 +60,11 @@ public void UnknownApiReturnsUnableToFindPlatform() } [Test] - public void UnknownApiCanHaveManualPlatform() + public async Task UnknownApiCanHaveManualPlatform() { var collaborationFactory = GetCollaborationFactory(); - var result = collaborationFactory.Initialise( + var result = await collaborationFactory.Initialise( new Uri("https://unknown.com/"), "token", ForkMode.SingleRepositoryOnly, Platform.GitHub); @@ -73,11 +74,11 @@ public void UnknownApiCanHaveManualPlatform() } [Test] - public void ManualPlatformWillOverrideUri() + public async Task ManualPlatformWillOverrideUri() { var collaborationFactory = GetCollaborationFactory(); - var result = collaborationFactory.Initialise( + var result = await collaborationFactory.Initialise( new Uri("https://api.github.myco.com"), "token", ForkMode.SingleRepositoryOnly, Platform.AzureDevOps); @@ -87,11 +88,11 @@ public void ManualPlatformWillOverrideUri() } [Test] - public void AzureDevOpsUrlReturnsAzureDevOps() + public async Task AzureDevOpsUrlReturnsAzureDevOps() { var collaborationFactory = GetCollaborationFactory(); - var result = collaborationFactory.Initialise(new Uri("https://dev.azure.com"), "token", + var result = await collaborationFactory.Initialise(new Uri("https://dev.azure.com"), "token", ForkMode.SingleRepositoryOnly, null); Assert.That(result.IsSuccess); @@ -100,11 +101,11 @@ public void AzureDevOpsUrlReturnsAzureDevOps() } [Test] - public void GithubUrlReturnsGitHub() + public async Task GithubUrlReturnsGitHub() { var collaborationFactory = GetCollaborationFactory(); - var result = collaborationFactory.Initialise(new Uri("https://api.github.com"), "token", + var result = await collaborationFactory.Initialise(new Uri("https://api.github.com"), "token", ForkMode.PreferFork, null); Assert.That(result.IsSuccess); diff --git a/NuKeeper.Tests/Engine/RepositoryUpdaterTests.cs b/NuKeeper.Tests/Engine/RepositoryUpdaterTests.cs index 0fbd38dc0..9c0063f97 100644 --- a/NuKeeper.Tests/Engine/RepositoryUpdaterTests.cs +++ b/NuKeeper.Tests/Engine/RepositoryUpdaterTests.cs @@ -105,7 +105,7 @@ await collaborationFactory.CollaborationPlatform.Received(expectedPrs) Arg.Any(), Arg.Any>()); - gitDriver.Received(numberOfUpdates) + await gitDriver.Received(numberOfUpdates) .Commit(Arg.Any()); } diff --git a/NuKeeper.Tests/MockedGitDiscoveryDriver.cs b/NuKeeper.Tests/MockedGitDiscoveryDriver.cs index c5b199ceb..5750cbb89 100644 --- a/NuKeeper.Tests/MockedGitDiscoveryDriver.cs +++ b/NuKeeper.Tests/MockedGitDiscoveryDriver.cs @@ -1,39 +1,40 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions.Git; namespace NuKeeper.Tests { public class MockedGitDiscoveryDriver : IGitDiscoveryDriver { - public bool IsGitRepo(Uri repositoryUri) + public Task IsGitRepo(Uri repositoryUri) { - return true; + return Task.FromResult(true); } - public IEnumerable GetRemotes(Uri repositoryUri) + public Task> GetRemotes(Uri repositoryUri) { - return new List(){ new GitRemote + return Task.FromResult>(new List(){ new GitRemote { Name="origin", Url = repositoryUri - }}; + }}); } - public Uri DiscoverRepo(Uri repositoryUri) + public Task DiscoverRepo(Uri repositoryUri) { - return repositoryUri; + return Task.FromResult(repositoryUri); } - public string GetCurrentHead(Uri repositoryUri) + public Task GetCurrentHead(Uri repositoryUri) { - return "master"; + return Task.FromResult("master"); } - public GitRemote GetRemoteForPlatform(Uri repositoryUri, string platformHost) + public async Task GetRemoteForPlatform(Uri repositoryUri, string platformHost) { - return GetRemotes(repositoryUri).First(); + return (await GetRemotes(repositoryUri)).First(); } } } diff --git a/NuKeeper.sln b/NuKeeper.sln index 7f8582484..1b3937dbd 100644 --- a/NuKeeper.sln +++ b/NuKeeper.sln @@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuKeeper.Gitea", "NuKeeper. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuKeeper.Gitea.Tests", "NuKeeper.Gitea.Tests\NuKeeper.Gitea.Tests.csproj", "{C39BD2F9-CF1E-4E0C-BA00-ADD65759161E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuKeeper.Git.Tests", "NuKeeper.Git.Tests\NuKeeper.Git.Tests.csproj", "{8718DFD0-71E0-4841-A91F-62CC8E726126}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +131,10 @@ Global {C39BD2F9-CF1E-4E0C-BA00-ADD65759161E}.Debug|Any CPU.Build.0 = Debug|Any CPU {C39BD2F9-CF1E-4E0C-BA00-ADD65759161E}.Release|Any CPU.ActiveCfg = Release|Any CPU {C39BD2F9-CF1E-4E0C-BA00-ADD65759161E}.Release|Any CPU.Build.0 = Release|Any CPU + {8718DFD0-71E0-4841-A91F-62CC8E726126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8718DFD0-71E0-4841-A91F-62CC8E726126}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8718DFD0-71E0-4841-A91F-62CC8E726126}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8718DFD0-71E0-4841-A91F-62CC8E726126}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NuKeeper/Collaboration/CollaborationFactory.cs b/NuKeeper/Collaboration/CollaborationFactory.cs index e8696b5ed..acd19a7a0 100644 --- a/NuKeeper/Collaboration/CollaborationFactory.cs +++ b/NuKeeper/Collaboration/CollaborationFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -40,10 +41,10 @@ public class CollaborationFactory : ICollaborationFactory Settings = new CollaborationPlatformSettings(); } - public ValidationResult Initialise(Uri apiEndpoint, string token, + public async Task Initialise(Uri apiEndpoint, string token, ForkMode? forkModeFromSettings, Platform? platformFromSettings) { - var platformSettingsReader = FindPlatformSettingsReader(platformFromSettings, apiEndpoint); + var platformSettingsReader = await FindPlatformSettingsReader(platformFromSettings, apiEndpoint); if (platformSettingsReader != null) { _platform = platformSettingsReader.Platform; @@ -69,7 +70,7 @@ public class CollaborationFactory : ICollaborationFactory return ValidationResult.Success; } - private ISettingsReader FindPlatformSettingsReader( + private async Task FindPlatformSettingsReader( Platform? platformFromSettings, Uri apiEndpoint) { if (platformFromSettings.HasValue) @@ -86,8 +87,8 @@ public class CollaborationFactory : ICollaborationFactory } else { - var reader = _settingReaders - .FirstOrDefault(s => s.CanRead(apiEndpoint)); + var reader = await _settingReaders + .FirstOrDefaultAsync(s => s.CanRead(apiEndpoint)); if (reader != null) { diff --git a/NuKeeper/Commands/CollaborationPlatformCommand.cs b/NuKeeper/Commands/CollaborationPlatformCommand.cs index 892190602..8d3896b48 100644 --- a/NuKeeper/Commands/CollaborationPlatformCommand.cs +++ b/NuKeeper/Commands/CollaborationPlatformCommand.cs @@ -58,9 +58,9 @@ internal abstract class CollaborationPlatformCommand : CommandBase CollaborationFactory = collaborationFactory; } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; @@ -79,7 +79,7 @@ protected override ValidationResult PopulateSettings(SettingsContainer settings) try { - var collaborationResult = CollaborationFactory.Initialise( + var collaborationResult = await CollaborationFactory.Initialise( baseUri, PersonalAccessToken, forkMode, platform); diff --git a/NuKeeper/Commands/CommandBase.cs b/NuKeeper/Commands/CommandBase.cs index 679b136d0..efa42c101 100644 --- a/NuKeeper/Commands/CommandBase.cs +++ b/NuKeeper/Commands/CommandBase.cs @@ -77,6 +77,10 @@ internal abstract class CommandBase Description = "Prefix that will be added to created branch name.")] public string BranchNamePrefix { get; set; } + [Option(CommandOptionType.SingleValue, ShortName = "git", LongName = "gitclipath", + Description = "Path to git to use instead of lib2gitsharp implementation")] + public string GitCliPath { get; set; } + protected CommandBase(IConfigureLogger logger, IFileSettingsCache fileSettingsCache) { _configureLogger = logger; @@ -89,7 +93,7 @@ public async Task OnExecute() var settings = MakeSettings(); - var validationResult = PopulateSettings(settings); + var validationResult = await PopulateSettings(settings); if (!validationResult.IsSuccess) { var logger = _configureLogger as INuKeeperLogger; @@ -124,6 +128,7 @@ private SettingsContainer MakeSettings() var usePrerelease = Concat.FirstValue(UsePrerelease, fileSettings.UsePrerelease, Abstractions.Configuration.UsePrerelease.FromPrerelease); var branchPrefixName = Concat.FirstValue(BranchNamePrefix, fileSettings.BranchNamePrefix); + var gitpath = Concat.FirstValue(GitCliPath, fileSettings.GitCliPath); var settings = new SettingsContainer { @@ -133,7 +138,8 @@ private SettingsContainer MakeSettings() { AllowedChange = allowedChange, UsePrerelease = usePrerelease, - NuGetSources = NuGetSources + NuGetSources = NuGetSources, + GitPath = gitpath }, BranchSettings = new BranchSettings { @@ -144,12 +150,12 @@ private SettingsContainer MakeSettings() return settings; } - protected virtual ValidationResult PopulateSettings(SettingsContainer settings) + protected virtual async Task PopulateSettings(SettingsContainer settings) { var minPackageAge = ReadMinPackageAge(); if (!minPackageAge.HasValue) { - return ValidationResult.Failure($"Min package age '{MinimumPackageAge}' could not be parsed"); + return await Task.FromResult(ValidationResult.Failure($"Min package age '{MinimumPackageAge}' could not be parsed")); } settings.PackageFilters.MinimumAge = minPackageAge.Value; @@ -190,7 +196,7 @@ protected virtual ValidationResult PopulateSettings(SettingsContainer settings) return branchNamePrefixValid; } - return ValidationResult.Success; + return await Task.FromResult(ValidationResult.Success); } private TimeSpan? ReadMinPackageAge() diff --git a/NuKeeper/Commands/GlobalCommand.cs b/NuKeeper/Commands/GlobalCommand.cs index 417dff850..ddd5f31ed 100644 --- a/NuKeeper/Commands/GlobalCommand.cs +++ b/NuKeeper/Commands/GlobalCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -15,9 +16,9 @@ public GlobalCommand(ICollaborationEngine engine, IConfigureLogger logger, IFile { } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/InspectCommand.cs b/NuKeeper/Commands/InspectCommand.cs index 4e7e5e005..2bec72389 100644 --- a/NuKeeper/Commands/InspectCommand.cs +++ b/NuKeeper/Commands/InspectCommand.cs @@ -17,9 +17,9 @@ internal class InspectCommand : LocalNuKeeperCommand _engine = engine; } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/LocalNuKeeperCommand.cs b/NuKeeper/Commands/LocalNuKeeperCommand.cs index 33794853f..ee3ba2448 100644 --- a/NuKeeper/Commands/LocalNuKeeperCommand.cs +++ b/NuKeeper/Commands/LocalNuKeeperCommand.cs @@ -2,6 +2,7 @@ using NuKeeper.Inspection.Logging; using System.IO; using NuKeeper.Abstractions.Configuration; +using System.Threading.Tasks; namespace NuKeeper.Commands { @@ -18,9 +19,9 @@ protected LocalNuKeeperCommand(IConfigureLogger logger, IFileSettingsCache fileS { } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/MultipleRepositoryCommand.cs b/NuKeeper/Commands/MultipleRepositoryCommand.cs index 153f024f6..ed34f0954 100644 --- a/NuKeeper/Commands/MultipleRepositoryCommand.cs +++ b/NuKeeper/Commands/MultipleRepositoryCommand.cs @@ -1,5 +1,6 @@ using System; using System.Text.RegularExpressions; +using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; @@ -26,9 +27,9 @@ protected MultipleRepositoryCommand(ICollaborationEngine engine, IConfigureLogge { } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/OrganisationCommand.cs b/NuKeeper/Commands/OrganisationCommand.cs index 0d3227c56..411c23fbf 100644 --- a/NuKeeper/Commands/OrganisationCommand.cs +++ b/NuKeeper/Commands/OrganisationCommand.cs @@ -4,6 +4,7 @@ using NuKeeper.Abstractions.Configuration; using NuKeeper.Collaboration; using NuKeeper.Inspection.Logging; +using System.Threading.Tasks; namespace NuKeeper.Commands { @@ -18,12 +19,12 @@ public OrganisationCommand(ICollaborationEngine engine, IConfigureLogger logger, { } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { var fileSettings = FileSettingsCache.GetSettings(); ApiEndpoint = Concat.FirstValue(ApiEndpoint, fileSettings.Api, "https://api.github.com"); - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/RepositoryCommand.cs b/NuKeeper/Commands/RepositoryCommand.cs index a8b8bd5ca..a48ba5e2d 100644 --- a/NuKeeper/Commands/RepositoryCommand.cs +++ b/NuKeeper/Commands/RepositoryCommand.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using NuKeeper.Abstractions.Formats; using NuKeeper.Collaboration; +using System.Threading.Tasks; namespace NuKeeper.Commands { @@ -27,7 +28,7 @@ public RepositoryCommand(ICollaborationEngine engine, IConfigureLogger logger, I _settingsReaders = settingsReaders; } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { if (string.IsNullOrWhiteSpace(RepositoryUri)) { @@ -50,11 +51,11 @@ protected override ValidationResult PopulateSettings(SettingsContainer settings) { if (didRead) continue; - if (reader.CanRead(repoUri)) + if (await reader.CanRead(repoUri)) { didRead = true; settings.SourceControlServerSettings.Repository = - reader.RepositorySettings(repoUri, TargetBranch); + await reader.RepositorySettings(repoUri, TargetBranch); } } @@ -63,7 +64,7 @@ protected override ValidationResult PopulateSettings(SettingsContainer settings) return ValidationResult.Failure($"Unable to work out which platform to use {RepositoryUri} could not be matched"); } - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Commands/UpdateCommand.cs b/NuKeeper/Commands/UpdateCommand.cs index f06308dea..438ec4ae0 100644 --- a/NuKeeper/Commands/UpdateCommand.cs +++ b/NuKeeper/Commands/UpdateCommand.cs @@ -22,9 +22,9 @@ public UpdateCommand(ILocalEngine engine, IConfigureLogger logger, IFileSettings _engine = engine; } - protected override ValidationResult PopulateSettings(SettingsContainer settings) + protected override async Task PopulateSettings(SettingsContainer settings) { - var baseResult = base.PopulateSettings(settings); + var baseResult = await base.PopulateSettings(settings); if (!baseResult.IsSuccess) { return baseResult; diff --git a/NuKeeper/Engine/GitRepositoryEngine.cs b/NuKeeper/Engine/GitRepositoryEngine.cs index 3344afa90..651b8cf4a 100644 --- a/NuKeeper/Engine/GitRepositoryEngine.cs +++ b/NuKeeper/Engine/GitRepositoryEngine.cs @@ -82,7 +82,10 @@ public class GitRepositoryEngine : IGitRepositoryEngine repositoryData.DefaultBranch = repository.RemoteInfo.BranchName; } - var git = new LibGit2SharpDriver(_logger, folder, credentials, user); + repositoryData.IsLocalRepo = repository.IsLocalRepo; + IGitDriver git = string.IsNullOrWhiteSpace(settings?.UserSettings?.GitPath) ? + new LibGit2SharpDriver(_logger, folder, credentials, user) as IGitDriver : + new GitCmdDriver(settings.UserSettings.GitPath, _logger, folder, credentials) as IGitDriver; var updatesDone = await _repositoryUpdater.Run(git, repositoryData, settings); diff --git a/NuKeeper/Engine/Packages/PackageUpdater.cs b/NuKeeper/Engine/Packages/PackageUpdater.cs index 8ef94fbab..75e0ccfa1 100644 --- a/NuKeeper/Engine/Packages/PackageUpdater.cs +++ b/NuKeeper/Engine/Packages/PackageUpdater.cs @@ -67,22 +67,22 @@ public class PackageUpdater : IPackageUpdater { _logger.Normal(UpdatesLogger.OldVersionsToBeUpdated(updates)); - git.Checkout(repository.DefaultBranch); + await git.Checkout(repository.DefaultBranch); // branch var branchWithChanges = BranchNamer.MakeName(updates, settings.BranchSettings.BranchNamePrefix); _logger.Detailed($"Using branch name: '{branchWithChanges}'"); - git.CheckoutNewBranch(branchWithChanges); + await git.CheckoutNewBranch(branchWithChanges); foreach (var updateSet in updates) { await _updateRunner.Update(updateSet, sources); var commitMessage = _collaborationFactory.CommitWorder.MakeCommitMessage(updateSet); - git.Commit(commitMessage); + await git.Commit(commitMessage); } - git.Push(repository.Remote, branchWithChanges); + await git.Push(repository.Remote, branchWithChanges); var title = _collaborationFactory.CommitWorder.MakePullRequestTitle(updates); var body = _collaborationFactory.CommitWorder.MakeCommitDetails(updates); @@ -102,7 +102,7 @@ public class PackageUpdater : IPackageUpdater await _collaborationFactory.CollaborationPlatform.OpenPullRequest(repository.Pull, pullRequestRequest, settings.SourceControlServerSettings.Labels); - git.Checkout(repository.DefaultBranch); + await git.Checkout(repository.DefaultBranch); return updates.Count; } } diff --git a/NuKeeper/Engine/RepositoryUpdater.cs b/NuKeeper/Engine/RepositoryUpdater.cs index a905066a2..2f608513a 100644 --- a/NuKeeper/Engine/RepositoryUpdater.cs +++ b/NuKeeper/Engine/RepositoryUpdater.cs @@ -47,7 +47,9 @@ public class RepositoryUpdater : IRepositoryUpdater SettingsContainer settings) { if (!repository.IsLocalRepo) - GitInit(git, repository); + { + await GitInit(git, repository); + } var userSettings = settings.UserSettings; @@ -104,11 +106,11 @@ public class RepositoryUpdater : IRepositoryUpdater return updatesDone; } - private static void GitInit(IGitDriver git, RepositoryData repository) + private static async Task GitInit(IGitDriver git, RepositoryData repository) { - git.Clone(repository.Pull.Uri, repository.DefaultBranch); - repository.DefaultBranch = repository.DefaultBranch ?? git.GetCurrentHead(); - git.AddRemote(repository.Remote, repository.Push.Uri); + await git.Clone(repository.Pull.Uri, repository.DefaultBranch); + repository.DefaultBranch = repository.DefaultBranch ?? await git.GetCurrentHead(); + await git.AddRemote(repository.Remote, repository.Push.Uri); } } } diff --git a/Nukeeper.AzureDevOps.Tests/AzureDevOpsSettingsReaderTests.cs b/Nukeeper.AzureDevOps.Tests/AzureDevOpsSettingsReaderTests.cs index 6b725157b..68c2efd8b 100644 --- a/Nukeeper.AzureDevOps.Tests/AzureDevOpsSettingsReaderTests.cs +++ b/Nukeeper.AzureDevOps.Tests/AzureDevOpsSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; @@ -23,9 +24,9 @@ public void Setup() } [Test] - public void ReturnsTrueIfCanRead() + public async Task ReturnsTrueIfCanRead() { - var canRead = _azureSettingsReader.CanRead(new Uri("https://dev.azure.com/org")); + var canRead = await _azureSettingsReader.CanRead(new Uri("https://dev.azure.com/org")); Assert.IsTrue(canRead); } @@ -70,18 +71,18 @@ public void AuthSettings_GetsCorrectSettingsFromEnvironment() [TestCase(null)] [TestCase("htps://org.visualstudio.com")] - public void InvalidUrlReturnsNull(string value) + public async Task InvalidUrlReturnsNull(string value) { var uriToTest = value == null ? null : new Uri(value); - var canRead = _azureSettingsReader.CanRead(uriToTest); + var canRead = await _azureSettingsReader.CanRead(uriToTest); Assert.IsFalse(canRead); } [Test] - public void RepositorySettings_GetsCorrectSettings() + public async Task RepositorySettings_GetsCorrectSettings() { - var settings = _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project/_git/reponame")); + var settings = await _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project/_git/reponame")); Assert.IsNotNull(settings); Assert.AreEqual(settings.ApiUri, "https://dev.azure.com/org/"); @@ -91,23 +92,23 @@ public void RepositorySettings_GetsCorrectSettings() } [Test] - public void RepositorySettings_ReturnsNull() + public async Task RepositorySettings_ReturnsNull() { - var settings = _azureSettingsReader.RepositorySettings(null); + var settings = await _azureSettingsReader.RepositorySettings(null); Assert.IsNull(settings); } [Test] public void RepositorySettings_PathTooLong() { - Assert.Throws(() => _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project/_git/reponame/thisShouldNotBeHere/"))); + Assert.ThrowsAsync(() => _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project/_git/reponame/thisShouldNotBeHere/"))); } [Test] - public void RepositorySettings_HandlesSpacesInRepo() + public async Task RepositorySettings_HandlesSpacesInRepo() { - var settings = _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project%20name/_git/repo%20name")); + var settings = await _azureSettingsReader.RepositorySettings(new Uri("https://dev.azure.com/org/project%20name/_git/repo%20name")); Assert.IsNotNull(settings); Assert.AreEqual("https://dev.azure.com/org/", settings.ApiUri.ToString()); diff --git a/Nukeeper.AzureDevOps.Tests/TfsSettingsReaderTests.cs b/Nukeeper.AzureDevOps.Tests/TfsSettingsReaderTests.cs index 3d9af0b9a..63c9fac44 100644 --- a/Nukeeper.AzureDevOps.Tests/TfsSettingsReaderTests.cs +++ b/Nukeeper.AzureDevOps.Tests/TfsSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; @@ -24,9 +25,9 @@ public void Setup() [TestCase("https://tfs/")] [TestCase("https://internalserver/tfs/")] - public void ReturnsTrueIfCanRead(string value) + public async Task ReturnsTrueIfCanRead(string value) { - var canRead = _azureSettingsReader.CanRead(new Uri(value)); + var canRead = await _azureSettingsReader.CanRead(new Uri(value)); Assert.IsTrue(canRead); } @@ -71,18 +72,18 @@ public void AuthSettings_GetsCorrectSettingsFromEnvironment() [TestCase(null)] [TestCase("htps://dev.azure.com")] - public void InvalidUrlReturnsNull(string value) + public async Task InvalidUrlReturnsNull(string value) { var uriToTest = value == null ? null : new Uri(value); - var canRead = _azureSettingsReader.CanRead(uriToTest); + var canRead = await _azureSettingsReader.CanRead(uriToTest); Assert.IsFalse(canRead); } [Test] - public void RepositorySettings_GetsCorrectSettings() + public async Task RepositorySettings_GetsCorrectSettings() { - var settings = _azureSettingsReader.RepositorySettings(new Uri("https://internalserver/tfs/project/_git/reponame")); + var settings = await _azureSettingsReader.RepositorySettings(new Uri("https://internalserver/tfs/project/_git/reponame")); Assert.IsNotNull(settings); Assert.AreEqual("https://internalserver/tfs", settings.ApiUri.ToString()); @@ -92,24 +93,24 @@ public void RepositorySettings_GetsCorrectSettings() } [Test] - public void RepositorySettings_ReturnsNull() + public async Task RepositorySettings_ReturnsNull() { - var settings = _azureSettingsReader.RepositorySettings(null); + var settings = await _azureSettingsReader.RepositorySettings(null); Assert.IsNull(settings); } [Test] public void RepositorySettings_InvalidFormat() { - Assert.Throws(() => + Assert.ThrowsAsync(() => _azureSettingsReader.RepositorySettings( new Uri("https://org.visualstudio.com/project/isnot_git/reponame/"))); } [Test] - public void RepositorySettings_HandlesSpaces() + public async Task RepositorySettings_HandlesSpaces() { - var settings = _azureSettingsReader.RepositorySettings(new Uri("https://internalserver/tfs/project%20name/_git/repo%20name")); + var settings = await _azureSettingsReader.RepositorySettings(new Uri("https://internalserver/tfs/project%20name/_git/repo%20name")); Assert.IsNotNull(settings); Assert.AreEqual("https://internalserver/tfs", settings.ApiUri.ToString()); diff --git a/Nukeeper.AzureDevOps.Tests/VstsSettingsReaderTests.cs b/Nukeeper.AzureDevOps.Tests/VstsSettingsReaderTests.cs index afd780dce..d59826284 100644 --- a/Nukeeper.AzureDevOps.Tests/VstsSettingsReaderTests.cs +++ b/Nukeeper.AzureDevOps.Tests/VstsSettingsReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using NSubstitute; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; @@ -23,9 +24,9 @@ public void Setup() } [Test] - public void ReturnsTrueIfCanRead() + public async Task ReturnsTrueIfCanRead() { - var canRead = _azureSettingsReader.CanRead(new Uri("https://org.visualstudio.com")); + var canRead = await _azureSettingsReader.CanRead(new Uri("https://org.visualstudio.com")); Assert.IsTrue(canRead); } @@ -70,18 +71,18 @@ public void AuthSettings_GetsCorrectSettingsFromEnvironment() [TestCase(null)] [TestCase("htps://dev.azure.com")] - public void InvalidUrlReturnsNull(string value) + public async Task InvalidUrlReturnsNull(string value) { var uriToTest = value == null ? null : new Uri(value); - var canRead = _azureSettingsReader.CanRead(uriToTest); + var canRead = await _azureSettingsReader.CanRead(uriToTest); Assert.IsFalse(canRead); } [Test] - public void RepositorySettings_GetsCorrectSettings() + public async Task RepositorySettings_GetsCorrectSettings() { - var settings = _azureSettingsReader.RepositorySettings(new Uri("https://org.visualstudio.com/project/_git/reponame")); + var settings = await _azureSettingsReader.RepositorySettings(new Uri("https://org.visualstudio.com/project/_git/reponame")); Assert.IsNotNull(settings); Assert.AreEqual("https://org.visualstudio.com/", settings.ApiUri.ToString()); @@ -91,16 +92,16 @@ public void RepositorySettings_GetsCorrectSettings() } [Test] - public void RepositorySettings_ReturnsNull() + public async Task RepositorySettings_ReturnsNull() { - var settings = _azureSettingsReader.RepositorySettings(null); + var settings = await _azureSettingsReader.RepositorySettings(null); Assert.IsNull(settings); } [Test] public void RepositorySettings_InvalidFormat() { - Assert.Throws(() => + Assert.ThrowsAsync(() => _azureSettingsReader.RepositorySettings( new Uri("https://org.visualstudio.com/project/_git/reponame/thisShouldNotBeHere/"))); } diff --git a/Nukeeper.BitBucketLocal/BitBucketLocalSettingsReader.cs b/Nukeeper.BitBucketLocal/BitBucketLocalSettingsReader.cs index 86f7366eb..74e3845c1 100644 --- a/Nukeeper.BitBucketLocal/BitBucketLocalSettingsReader.cs +++ b/Nukeeper.BitBucketLocal/BitBucketLocalSettingsReader.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Threading.Tasks; using NuKeeper.Abstractions; using NuKeeper.Abstractions.CollaborationPlatform; using NuKeeper.Abstractions.Configuration; @@ -21,13 +22,13 @@ public BitBucketLocalSettingsReader(IEnvironmentVariablesProvider environmentVar private string Username { get; set; } - public bool CanRead(Uri repositoryUri) + public Task CanRead(Uri repositoryUri) { - return repositoryUri?.Host.Contains("bitbucket", StringComparison.OrdinalIgnoreCase) == true && - repositoryUri.Host.Contains("bitbucket.org", StringComparison.OrdinalIgnoreCase) == false; + return Task.FromResult(repositoryUri?.Host.Contains("bitbucket", StringComparison.OrdinalIgnoreCase) == true && + repositoryUri.Host.Contains("bitbucket.org", StringComparison.OrdinalIgnoreCase) == false); } - public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBranch) + public Task RepositorySettings(Uri repositoryUri, string targetBranch) { if (repositoryUri == null) { @@ -49,13 +50,13 @@ public RepositorySettings RepositorySettings(Uri repositoryUri, string targetBra var repoName = pathParts[pathParts.Count - 1].ToLower(CultureInfo.CurrentCulture).Replace(".git", string.Empty); var project = pathParts[pathParts.Count - 2]; - return new RepositorySettings + return Task.FromResult(new RepositorySettings { ApiUri = new Uri($"{repositoryUri.Scheme}://{repositoryUri.Authority}"), RepositoryUri = repositoryUri, RepositoryName = repoName, RepositoryOwner = project - }; + }); } public void UpdateCollaborationPlatformSettings(CollaborationPlatformSettings settings) diff --git a/site/content/basics/configuration.md b/site/content/basics/configuration.md index 6a2e71486..8e319e8a8 100644 --- a/site/content/basics/configuration.md +++ b/site/content/basics/configuration.md @@ -28,6 +28,7 @@ title: "Configuration" | maxpackageupdates| m | `repo`, `org`, `global`, `update`| 3, or when the command is `update`, 1 | | consolidate | n | `repo`, `org`, `global` | false | | platform | | `repo`, `org`, `global` | _null_ | +| gitclipath | git | `repo`, `org`, `global` | _null_ (use default Lib2Git-Implementation) | | | | | | | maxrepo | | `org`, `global` | 10 | | includerepos | | `org`, `global` | _null_ | @@ -70,6 +71,7 @@ Examples: `0` = zero, `12h` = 12 hours, `3d` = 3 days, `2w` = two weeks. * *maxrepo* The maximum number of repositories to change. Used in Organisation and Global mode. * *consolidate* Consolidate updates into a single pull request, instead of the default of 1 pull request per package update applied. * *platform* One of `GitHub`, `AzureDevOps`, `Bitbucket`, `BitbucketLocal`, `Gitlab`, `Gitea`. Determines which kind of source control api will be used. This is typicaly infered from the api url structure, but since this does not always work, it can be specified here if neccessary. +* *gitclipath* Path to `git` command line. Alternative use native git via cli instead of the default lib2gitsharp implementation. In special cases lib2gitsharp is not enough. E.g. in enviroments with company-proxies or self hosted git services signed certificates. For Windows it is typically `C:\Program Files\Git\bin\git.exe`. For Linux it is typically `/usr/bin/git`. * *includerepos* A regex to filter repositories by name. Only consider repositories where the name matches this regex pattern. Used in Organisation and Global mode. * *excluderepos* A regex to filter repositories by name. Do not consider repositories where the name matches this regex pattern. Used in Organisation and Global mode.