Code cleanup for libgit2 interop

This commit is contained in:
Jordan Brown
2020-04-24 18:32:08 -04:00
parent b2bfa1f63e
commit 23aca07cc9
9 changed files with 212 additions and 87 deletions
@@ -108,9 +108,14 @@ namespace Tgstation.Server.Host.Components
readonly IPlatformIdentifier platformIdentifier;
/// <summary>
/// The <see cref="IRepositoryFactory"/> for the <see cref="InstanceFactory"/>.
/// The <see cref="ILibGit2RepositoryFactory"/> for the <see cref="InstanceFactory"/>.
/// </summary>
readonly IRepositoryFactory repositoryFactory;
readonly ILibGit2RepositoryFactory repositoryFactory;
/// <summary>
/// The <see cref="ILibGit2Commands"/> for the <see cref="InstanceFactory"/>.
/// </summary>
readonly ILibGit2Commands repositoryCommands;
/// <summary>
/// The <see cref="IServerPortProvider"/> for the <see cref="InstanceFactory"/>.
@@ -138,6 +143,7 @@ namespace Tgstation.Server.Host.Components
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/></param>
/// <param name="platformIdentifier">The value of <see cref="platformIdentifier"/></param>
/// <param name="repositoryFactory">The value of <see cref="repositoryFactory"/>.</param>
/// <param name="repositoryCommands">The value of <see cref="repositoryCommands"/>.</param>
/// <param name="serverPortProvider">The value of <see cref="serverPortProvider"/>.</param>
public InstanceFactory(
IIOManager ioManager,
@@ -157,7 +163,8 @@ namespace Tgstation.Server.Host.Components
INetworkPromptReaper networkPromptReaper,
IGitHubClientFactory gitHubClientFactory,
IPlatformIdentifier platformIdentifier,
IRepositoryFactory repositoryFactory,
ILibGit2RepositoryFactory repositoryFactory,
ILibGit2Commands repositoryCommands,
IServerPortProvider serverPortProvider)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
@@ -178,6 +185,7 @@ namespace Tgstation.Server.Host.Components
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory));
this.repositoryCommands = repositoryCommands ?? throw new ArgumentNullException(nameof(repositoryCommands));
this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
}
@@ -198,6 +206,7 @@ namespace Tgstation.Server.Host.Components
var eventConsumer = new EventConsumer(configuration);
var repoManager = new RepositoryManager(
repositoryFactory,
repositoryCommands,
repoIoManager,
eventConsumer,
loggerFactory.CreateLogger<Repository.Repository>(),
@@ -0,0 +1,29 @@
using LibGit2Sharp;
using System.Collections.Generic;
namespace Tgstation.Server.Host.Components.Repository
{
/// <summary>
/// For low level interactions with a <see cref="LibGit2Sharp.IRepository"/>.
/// </summary>
interface ILibGit2Commands
{
/// <summary>
/// Runs a blocking fetch operation on a given <paramref name="repository"/>.
/// </summary>
/// <param name="repository">The <see cref="IRepository"/> to fetch with.</param>
/// <param name="refSpecs">The refspecs to fetch.</param>
/// <param name="remote">The <see cref="Remote"/> to fetch.</param>
/// <param name="fetchOptions">The <see cref="FetchOptions"/>.</param>
/// <param name="logMessage">The message to write in the git log.</param>
void Fetch(LibGit2Sharp.IRepository repository, IEnumerable<string> refSpecs, Remote remote, FetchOptions fetchOptions, string logMessage);
/// <summary>
/// Runs a blocking checkout operation on a given <paramref name="repository"/>.
/// </summary>
/// <param name="repository">The <see cref="IRepository"/> to checkout with.</param>
/// <param name="checkoutOptions">The <see cref="CheckoutOptions"/>.</param>
/// <param name="commitish">The git object to checkout.</param>
void Checkout(LibGit2Sharp.IRepository repository, CheckoutOptions checkoutOptions, string commitish);
}
}
@@ -8,7 +8,7 @@ namespace Tgstation.Server.Host.Components.Repository
/// <summary>
/// Factory for creating <see cref="LibGit2Sharp.IRepository"/>s.
/// </summary>
interface IRepositoryFactory : ICredentialsProvider
interface ILibGit2RepositoryFactory : ICredentialsProvider
{
/// <summary>
/// Create an in-memeory <see cref="LibGit2Sharp.IRepository"/>.
@@ -0,0 +1,34 @@
using LibGit2Sharp;
using System;
using System.Collections.Generic;
namespace Tgstation.Server.Host.Components.Repository
{
/// <inheritdoc />
sealed class LibGit2Commands : ILibGit2Commands
{
/// <inheritdoc />
public void Checkout(LibGit2Sharp.IRepository libGit2Repo, CheckoutOptions checkoutOptions, string commitish)
=> Commands.Checkout(libGit2Repo, commitish, checkoutOptions);
/// <inheritdoc />
public void Fetch(
LibGit2Sharp.IRepository libGit2Repo,
IEnumerable<string> refSpecs,
Remote remote,
FetchOptions fetchOptions,
string logMessage)
{
if (libGit2Repo == null)
throw new ArgumentNullException(nameof(libGit2Repo));
if (!(libGit2Repo is LibGit2Sharp.Repository concreteRepo))
throw new ArgumentException("libGit2Repo must be an instance of LibGit2Sharp.Repository!", nameof(libGit2Repo));
if (remote == null)
throw new ArgumentNullException(nameof(remote));
Commands.Fetch(concreteRepo, remote.Name, refSpecs, fetchOptions, logMessage);
}
}
}
@@ -9,18 +9,18 @@ using Tgstation.Server.Host.Jobs;
namespace Tgstation.Server.Host.Components.Repository
{
/// <inheritdoc />
sealed class RepositoryFactory : IRepositoryFactory
sealed class LibGit2RepositoryFactory : ILibGit2RepositoryFactory
{
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="RepositoryFactory"/>.
/// The <see cref="ILogger"/> for the <see cref="LibGit2RepositoryFactory"/>.
/// </summary>
readonly ILogger<RepositoryFactory> logger;
readonly ILogger<LibGit2RepositoryFactory> logger;
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryFactory"/> <see langword="class"/>.
/// Initializes a new instance of the <see cref="LibGit2RepositoryFactory"/> <see langword="class"/>.
/// </summary>
/// <param name="logger">The value of <see cref="logger"/>.</param>
public RepositoryFactory(ILogger<RepositoryFactory> logger)
public LibGit2RepositoryFactory(ILogger<LibGit2RepositoryFactory> logger)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -28,8 +28,18 @@ namespace Tgstation.Server.Host.Components.Repository
/// <inheritdoc />
public LibGit2Sharp.IRepository CreateInMemory()
{
logger.LogTrace("Creating in-memory repository...");
return new LibGit2Sharp.Repository();
logger.LogTrace("Creating in-memory libgit2 repository...");
var repo = new LibGit2Sharp.Repository();
try
{
logger.LogTrace("Successfully created in-memory libgit2 repository.");
return repo;
}
catch
{
repo.Dispose();
throw;
}
}
/// <inheritdoc />
@@ -37,9 +47,13 @@ namespace Tgstation.Server.Host.Components.Repository
{
if (path == null)
throw new ArgumentNullException(nameof(path));
logger.LogTrace("Creating repostory at {0}...", path);
return Task.Factory.StartNew(
() => (LibGit2Sharp.IRepository)new LibGit2Sharp.Repository(path),
return Task.Factory.StartNew<LibGit2Sharp.IRepository>(
() =>
{
logger.LogTrace("Creating libgit2 repostory at {0}...", path);
return new LibGit2Sharp.Repository(path);
},
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Current);
@@ -43,21 +43,26 @@ namespace Tgstation.Server.Host.Components.Repository
public string GitHubRepoName { get; }
/// <inheritdoc />
public bool Tracking => Reference != null && repository.Head.IsTracking;
public bool Tracking => Reference != null && libGitRepo.Head.IsTracking;
/// <inheritdoc />
public string Head => repository.Head.Tip.Sha;
public string Head => libGitRepo.Head.Tip.Sha;
/// <inheritdoc />
public string Reference => repository.Head.FriendlyName;
public string Reference => libGitRepo.Head.FriendlyName;
/// <inheritdoc />
public string Origin => repository.Network.Remotes.First().Url;
public string Origin => libGitRepo.Network.Remotes.First().Url;
/// <summary>
/// The <see cref="LibGit2Sharp.IRepository"/> for the <see cref="Repository"/>
/// </summary>
readonly LibGit2Sharp.IRepository repository;
readonly LibGit2Sharp.IRepository libGitRepo;
/// <summary>
/// The <see cref="ILibGit2Commands"/> for the <see cref="Repository"/>.
/// </summary>
readonly ILibGit2Commands commands;
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="Repository"/>
@@ -99,21 +104,32 @@ namespace Tgstation.Server.Host.Components.Repository
/// <summary>
/// Construct a <see cref="Repository"/>
/// </summary>
/// <param name="repository">The value of <see cref="repository"/></param>
/// <param name="libGitRepo">The value of <see cref="libGitRepo"/></param>
/// <param name="commands">The value of <see cref="commands"/>.</param>
/// <param name="ioMananger">The value of <see cref="ioMananger"/></param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
/// <param name="credentialsProvider">The value of <see cref="credentialsProvider"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
/// <param name="onDispose">The value if <see cref="onDispose"/></param>
public Repository(LibGit2Sharp.IRepository repository, IIOManager ioMananger, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger<Repository> logger, Action onDispose)
public Repository(
LibGit2Sharp.IRepository libGitRepo,
ILibGit2Commands commands,
IIOManager ioMananger,
IEventConsumer eventConsumer,
ICredentialsProvider credentialsProvider,
ILogger<Repository> logger,
Action onDispose)
{
this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
this.libGitRepo = libGitRepo ?? throw new ArgumentNullException(nameof(libGitRepo));
this.commands = commands ?? throw new ArgumentNullException(nameof(commands));
this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
IsGitHubRepository = Origin.Contains(GitHubUrl, StringComparison.InvariantCultureIgnoreCase);
if (IsGitHubRepository)
{
GetRepositoryOwnerName(Origin, out var owner, out var name);
@@ -134,7 +150,7 @@ namespace Tgstation.Server.Host.Components.Repository
}
logger.LogTrace("Disposing...");
repository.Dispose();
libGitRepo.Dispose();
onDispose();
}
@@ -191,15 +207,18 @@ namespace Tgstation.Server.Host.Components.Repository
progressReporter(0);
cancellationToken.ThrowIfCancellationRequested();
Commands.Checkout(repository, committish, new CheckoutOptions
{
CheckoutModifiers = CheckoutModifiers.Force,
OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
});
commands.Checkout(
libGitRepo,
new CheckoutOptions
{
CheckoutModifiers = CheckoutModifiers.Force,
OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
},
committish);
cancellationToken.ThrowIfCancellationRequested();
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
}
/// <inheritdoc />
@@ -248,7 +267,7 @@ namespace Tgstation.Server.Host.Components.Repository
var refSpecList = new List<string> { refSpec };
var logMessage = String.Format(CultureInfo.InvariantCulture, "Merge remote pull request #{0}", testMergeParameters.Number);
var originalCommit = repository.Head;
var originalCommit = libGitRepo.Head;
MergeResult result = null;
@@ -261,37 +280,42 @@ namespace Tgstation.Server.Host.Components.Repository
{
logger.LogTrace("Fetching refspec {0}...", refSpec);
var remote = repository.Network.Remotes.First();
var remote = libGitRepo.Network.Remotes.First();
progressReporter(0);
Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, refSpecList, new FetchOptions
{
Prune = true,
OnProgress = (a) => !cancellationToken.IsCancellationRequested,
OnTransferProgress = (a) =>
commands.Fetch(
libGitRepo,
refSpecList,
remote,
new FetchOptions
{
var percentage = 50 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
progressReporter((int)percentage);
return !cancellationToken.IsCancellationRequested;
Prune = true,
OnProgress = (a) => !cancellationToken.IsCancellationRequested,
OnTransferProgress = (a) =>
{
var percentage = 50 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
progressReporter((int)percentage);
return !cancellationToken.IsCancellationRequested;
},
OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password)
},
OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password)
}, logMessage);
logMessage);
}
catch (UserCancelledException) { }
cancellationToken.ThrowIfCancellationRequested();
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
cancellationToken.ThrowIfCancellationRequested();
testMergeParameters.PullRequestRevision = repository.Lookup(testMergeParameters.PullRequestRevision ?? localBranchName).Sha;
testMergeParameters.PullRequestRevision = libGitRepo.Lookup(testMergeParameters.PullRequestRevision ?? localBranchName).Sha;
cancellationToken.ThrowIfCancellationRequested();
logger.LogTrace("Merging {0} into {1}...", testMergeParameters.PullRequestRevision.Substring(0, 7), Reference);
result = repository.Merge(testMergeParameters.PullRequestRevision, sig, new MergeOptions
result = libGitRepo.Merge(testMergeParameters.PullRequestRevision, sig, new MergeOptions
{
CommitOnSuccess = commitMessage == null,
FailOnConflict = true,
@@ -302,7 +326,7 @@ namespace Tgstation.Server.Host.Components.Repository
}
finally
{
repository.Branches.Remove(localBranchName);
libGitRepo.Branches.Remove(localBranchName);
}
cancellationToken.ThrowIfCancellationRequested();
@@ -315,7 +339,7 @@ namespace Tgstation.Server.Host.Components.Repository
cancellationToken.ThrowIfCancellationRequested();
}
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
if (result.Status == MergeStatus.Conflicts)
@@ -327,7 +351,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (commitMessage != null && result.Status != MergeStatus.UpToDate)
{
logger.LogTrace("Committing merge: \"{0}\"...", commitMessage);
await Task.Factory.StartNew(() => repository.Commit(commitMessage, sig, sig, new CommitOptions
await Task.Factory.StartNew(() => libGitRepo.Commit(commitMessage, sig, sig, new CommitOptions
{
PrettifyMessage = true
}), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
@@ -359,7 +383,7 @@ namespace Tgstation.Server.Host.Components.Repository
await eventConsumer.HandleEvent(EventType.RepoCheckout, new List<string> { committish }, cancellationToken).ConfigureAwait(false);
await Task.Factory.StartNew(() =>
{
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
RawCheckout(committish, progressReporter, cancellationToken);
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
}
@@ -373,10 +397,10 @@ namespace Tgstation.Server.Host.Components.Repository
await eventConsumer.HandleEvent(EventType.RepoFetch, Array.Empty<string>(), cancellationToken).ConfigureAwait(false);
await Task.Factory.StartNew(() =>
{
var remote = repository.Network.Remotes.First();
var remote = libGitRepo.Network.Remotes.First();
try
{
Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, remote.FetchRefSpecs.Select(x => x.Specification), new FetchOptions
Commands.Fetch((LibGit2Sharp.Repository)libGitRepo, remote.Name, remote.FetchRefSpecs.Select(x => x.Specification), new FetchOptions
{
Prune = true,
OnProgress = (a) => !cancellationToken.IsCancellationRequested,
@@ -408,17 +432,17 @@ namespace Tgstation.Server.Host.Components.Repository
Task PushHeadToTemporaryBranch(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
{
logger.LogInformation("Pushing changes to temporary remote branch...");
var branch = repository.CreateBranch(RemoteTemporaryBranchName);
var branch = libGitRepo.CreateBranch(RemoteTemporaryBranchName);
try
{
cancellationToken.ThrowIfCancellationRequested();
var remote = repository.Network.Remotes.First();
var remote = libGitRepo.Network.Remotes.First();
try
{
var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName);
repository.Network.Push(remote, forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken));
libGitRepo.Network.Push(remote, forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken));
var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName);
repository.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken));
libGitRepo.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken));
}
catch (UserCancelledException)
{
@@ -431,7 +455,7 @@ namespace Tgstation.Server.Host.Components.Repository
}
finally
{
repository.Branches.Remove(branch);
libGitRepo.Branches.Remove(branch);
}
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
@@ -443,7 +467,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (!Tracking)
throw new JobException("Cannot reset to origin while not on a tracked reference!");
logger.LogTrace("Reset to origin...");
var trackedBranch = repository.Head.TrackedBranch;
var trackedBranch = libGitRepo.Head.TrackedBranch;
await eventConsumer.HandleEvent(EventType.RepoResetOrigin, new List<string> { trackedBranch.FriendlyName, trackedBranch.Tip.Sha }, cancellationToken).ConfigureAwait(false);
await ResetToSha(trackedBranch.Tip.Sha, progressReporter, cancellationToken).ConfigureAwait(false);
}
@@ -458,16 +482,16 @@ namespace Tgstation.Server.Host.Components.Repository
logger.LogDebug("Reset to sha: {0}", sha.Substring(0, 7));
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
cancellationToken.ThrowIfCancellationRequested();
var gitObject = repository.Lookup(sha, ObjectType.Commit);
var gitObject = libGitRepo.Lookup(sha, ObjectType.Commit);
cancellationToken.ThrowIfCancellationRequested();
if (gitObject == null)
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot reset to non-existent SHA: {0}", sha));
repository.Reset(ResetMode.Hard, gitObject.Peel<Commit>(), new CheckoutOptions
libGitRepo.Reset(ResetMode.Hard, gitObject.Peel<Commit>(), new CheckoutOptions
{
OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
});
@@ -491,7 +515,7 @@ namespace Tgstation.Server.Host.Components.Repository
MergeResult result = null;
Branch trackedBranch = null;
var oldHead = repository.Head;
var oldHead = libGitRepo.Head;
var oldTip = oldHead.Tip;
await Task.Factory.StartNew(() =>
@@ -499,13 +523,13 @@ namespace Tgstation.Server.Host.Components.Repository
if (!Tracking)
throw new JobException("Cannot reset to origin while not on a tracked reference!");
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
cancellationToken.ThrowIfCancellationRequested();
trackedBranch = repository.Head.TrackedBranch;
trackedBranch = libGitRepo.Head.TrackedBranch;
logger.LogDebug("Merge origin/{2}: <{0} ({1})>", committerName, committerEmail, trackedBranch.FriendlyName);
result = repository.Merge(trackedBranch, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions
result = libGitRepo.Merge(trackedBranch, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions
{
CommitOnSuccess = true,
FailOnConflict = true,
@@ -519,14 +543,14 @@ namespace Tgstation.Server.Host.Components.Repository
if (result.Status == MergeStatus.Conflicts)
{
logger.LogDebug("Merge conflict, aborting and reverting to {0}", oldHead.FriendlyName);
repository.Reset(ResetMode.Hard, oldTip, new CheckoutOptions
libGitRepo.Reset(ResetMode.Hard, oldTip, new CheckoutOptions
{
OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
});
cancellationToken.ThrowIfCancellationRequested();
}
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
if (result.Status == MergeStatus.Conflicts)
@@ -566,9 +590,9 @@ namespace Tgstation.Server.Host.Components.Repository
logger.LogTrace("Configuring <{0} ({1})> as author/committer", committerName, committerEmail);
await Task.Factory.StartNew(() =>
{
repository.Config.Set("user.name", committerName);
libGitRepo.Config.Set("user.name", committerName);
cancellationToken.ThrowIfCancellationRequested();
repository.Config.Set("user.email", committerEmail);
libGitRepo.Config.Set("user.email", committerEmail);
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -592,9 +616,9 @@ namespace Tgstation.Server.Host.Components.Repository
logger.LogTrace("Resetting and cleaning untracked files...");
await Task.Factory.StartNew(() =>
{
repository.RemoveUntrackedFiles();
libGitRepo.RemoveUntrackedFiles();
cancellationToken.ThrowIfCancellationRequested();
repository.Reset(ResetMode.Hard, repository.Head.Tip, new CheckoutOptions
libGitRepo.Reset(ResetMode.Hard, libGitRepo.Head.Tip, new CheckoutOptions
{
OnCheckoutProgress = CheckoutProgressHandler(progress => progressReporter(progress / 10))
});
@@ -620,10 +644,10 @@ namespace Tgstation.Server.Host.Components.Repository
return await Task.Factory.StartNew(() =>
{
var remote = repository.Network.Remotes.First();
var remote = libGitRepo.Network.Remotes.First();
try
{
repository.Network.Push(repository.Head, GeneratePushOptions(FinalReporter, username, password, cancellationToken));
libGitRepo.Network.Push(libGitRepo.Head, GeneratePushOptions(FinalReporter, username, password, cancellationToken));
return true;
}
catch (NonFastForwardException)
@@ -648,18 +672,18 @@ namespace Tgstation.Server.Host.Components.Repository
public Task<bool> IsSha(string committish, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
{
// check if it's a tag
var gitObject = repository.Lookup(committish, ObjectType.Tag);
var gitObject = libGitRepo.Lookup(committish, ObjectType.Tag);
if (gitObject != null)
return false;
cancellationToken.ThrowIfCancellationRequested();
// check if it's a branch
if (repository.Branches[committish] != null)
if (libGitRepo.Branches[committish] != null)
return false;
cancellationToken.ThrowIfCancellationRequested();
// err on the side of references, if we can't look it up, assume its a reference
if (repository.Lookup<Commit>(committish) != null)
if (libGitRepo.Lookup<Commit>(committish) != null)
return true;
return false;
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
@@ -19,9 +19,14 @@ namespace Tgstation.Server.Host.Components.Repository
public bool CloneInProgress { get; private set; }
/// <summary>
/// The <see cref="IRepositoryFactory"/> for the <see cref="RepositoryManager"/>
/// The <see cref="ILibGit2RepositoryFactory"/> for the <see cref="RepositoryManager"/>
/// </summary>
readonly IRepositoryFactory repositoryFactory;
readonly ILibGit2RepositoryFactory repositoryFactory;
/// <summary>
/// The <see cref="ILibGit2Commands"/> for the <see cref="RepositoryManager"/>.
/// </summary>
readonly ILibGit2Commands commands;
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="RepositoryManager"/>
@@ -57,13 +62,15 @@ namespace Tgstation.Server.Host.Components.Repository
/// Construct a <see cref="RepositoryManager"/>
/// </summary>
/// <param name="repositoryFactory">The value of <see cref="repositoryFactory"/>.</param>
/// <param name="commands">The value of <see cref="commands"/>.</param>
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
/// <param name="repositoryLogger">The value of <see cref="repositoryLogger"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
/// <param name="repositorySettings">The value of <see cref="repositorySettings"/></param>
public RepositoryManager(
IRepositoryFactory repositoryFactory,
ILibGit2RepositoryFactory repositoryFactory,
ILibGit2Commands commands,
IIOManager ioManager,
IEventConsumer eventConsumer,
ILogger<Repository> repositoryLogger,
@@ -71,6 +78,7 @@ namespace Tgstation.Server.Host.Components.Repository
RepositorySettings repositorySettings)
{
this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory));
this.commands = commands ?? throw new ArgumentNullException(nameof(commands));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger));
@@ -175,12 +183,18 @@ namespace Tgstation.Server.Host.Components.Repository
using (var context = await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
try
{
var repo = await repositoryFactory.CreateFromPath(ioManager.ResolvePath(), cancellationToken).ConfigureAwait(false);
var libGitRepo = await repositoryFactory.CreateFromPath(ioManager.ResolvePath(), cancellationToken).ConfigureAwait(false);
if (repo == null)
if (libGitRepo == null)
return null;
return new Repository(repo, ioManager, eventConsumer, repositoryFactory, repositoryLogger, () =>
return new Repository(
libGitRepo,
commands,
ioManager,
eventConsumer,
repositoryFactory,
repositoryLogger, () =>
{
logger.LogTrace("Releasing semaphore due to Repository disposal...");
semaphore.Release();
@@ -289,7 +289,8 @@ namespace Tgstation.Server.Host.Core
});
// configure component services
services.AddSingleton<IRepositoryFactory, RepositoryFactory>();
services.AddSingleton<ILibGit2RepositoryFactory, LibGit2RepositoryFactory>();
services.AddSingleton<ILibGit2Commands, LibGit2Commands>();
services.AddSingleton<IProviderFactory, ProviderFactory>();
services.AddSingleton<IChatManagerFactory, ChatManagerFactory>();
services.AddSingleton<IInstanceFactory, InstanceFactory>();
@@ -9,26 +9,26 @@ using System.Threading.Tasks;
namespace Tgstation.Server.Host.Components.Repository.Tests
{
/// <summary>
/// Tests for <see cref="RepositoryFactory"/>.
/// Tests for <see cref="LibGit2RepositoryFactory"/>.
/// </summary>
[TestClass]
public sealed class TestRepositoryFactory
{
static IRepositoryFactory CreateFactory() => new RepositoryFactory(Mock.Of<ILogger<RepositoryFactory>>());
static ILibGit2RepositoryFactory CreateFactory() => new LibGit2RepositoryFactory(Mock.Of<ILogger<LibGit2RepositoryFactory>>());
static Task<LibGit2Sharp.IRepository> TestRepoLoading(
string path,
IRepositoryFactory repositoryFactory = null) =>
ILibGit2RepositoryFactory repositoryFactory = null) =>
(repositoryFactory ?? CreateFactory())
.CreateFromPath(path, default);
[TestMethod]
public void TestConstructionThrows() => Assert.ThrowsException<ArgumentNullException>(() => new RepositoryFactory(null));
public void TestConstructionThrows() => Assert.ThrowsException<ArgumentNullException>(() => new LibGit2RepositoryFactory(null));
[TestMethod]
public void TestInMemoryRepoCreation()
{
new RepositoryFactory(Mock.Of<ILogger<RepositoryFactory>>()).CreateInMemory().Dispose();
new LibGit2RepositoryFactory(Mock.Of<ILogger<LibGit2RepositoryFactory>>()).CreateInMemory().Dispose();
}
[TestMethod]