tgstation-server  4.3.2
The /tg/station 13 server suite
RepositoryManager.cs
Go to the documentation of this file.
1 using LibGit2Sharp;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components.Repository
13 {
16  {
18  public bool InUse => semaphore.CurrentCount == 0;
19 
21  public bool CloneInProgress { get; private set; }
22 
27 
32 
37 
42 
46  readonly ILogger<Repository> repositoryLogger;
47 
51  readonly ILogger<RepositoryManager> logger;
52 
56  readonly SemaphoreSlim semaphore;
57 
68  ILibGit2RepositoryFactory repositoryFactory,
69  ILibGit2Commands commands,
70  IIOManager ioManager,
71  IEventConsumer eventConsumer,
72  ILogger<Repository> repositoryLogger,
73  ILogger<RepositoryManager> logger)
74  {
75  this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory));
76  this.commands = commands ?? throw new ArgumentNullException(nameof(commands));
77  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
78  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
79  this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger));
80  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
81  semaphore = new SemaphoreSlim(1);
82  }
83 
85  public void Dispose()
86  {
87  logger.LogTrace("Disposing...");
88  semaphore.Dispose();
89  }
90 
92  public async Task<IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
93  {
94  if (url == null)
95  throw new ArgumentNullException(nameof(url));
96  if (progressReporter == null)
97  throw new ArgumentNullException(nameof(progressReporter));
98 
99  logger.LogInformation("Begin clone {0} (Branch: {1})", url, initialBranch);
100  lock (semaphore)
101  {
102  if (CloneInProgress)
103  throw new JobException(ErrorCode.RepoCloning);
104  CloneInProgress = true;
105  }
106 
107  try
108  {
109  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
110  {
111  logger.LogTrace("Semaphore acquired");
112  var repositoryPath = ioManager.ResolvePath();
113  if (!await ioManager.DirectoryExists(repositoryPath, cancellationToken).ConfigureAwait(false))
114  try
115  {
116  var cloneOptions = new CloneOptions
117  {
118  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
119  OnTransferProgress = (a) =>
120  {
121  var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
122  progressReporter((int)percentage);
123  return !cancellationToken.IsCancellationRequested;
124  },
125  RecurseSubmodules = true,
126  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
127  RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
128  BranchName = initialBranch,
129  CredentialsProvider = repositoryFactory.GenerateCredentialsHandler(username, password)
130  };
131 
132  await repositoryFactory.Clone(
133  url,
134  cloneOptions,
135  repositoryPath,
136  cancellationToken)
137  .ConfigureAwait(false);
138  }
139  catch
140  {
141  try
142  {
143  logger.LogTrace("Deleting partially cloned repository...");
144  await ioManager.DeleteDirectory(repositoryPath, default).ConfigureAwait(false);
145  }
146  catch (Exception e)
147  {
148  logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e);
149  }
150 
151  throw;
152  }
153  else
154  {
155  logger.LogDebug("Repository exists, clone aborted!");
156  return null;
157  }
158  }
159 
160  logger.LogInformation("Clone complete!");
161  }
162  finally
163  {
164  CloneInProgress = false;
165  }
166 
167  return await LoadRepository(cancellationToken).ConfigureAwait(false);
168  }
169 
171  public async Task<IRepository> LoadRepository(CancellationToken cancellationToken)
172  {
173  logger.LogTrace("Begin LoadRepository...");
174  lock (semaphore)
175  if (CloneInProgress)
176  throw new JobException(ErrorCode.RepoCloning);
177  await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
178  try
179  {
180  try
181  {
182  var libGitRepo = await repositoryFactory.CreateFromPath(ioManager.ResolvePath(), cancellationToken).ConfigureAwait(false);
183  return new Repository(
184  libGitRepo,
185  commands,
186  ioManager,
187  eventConsumer,
188  repositoryFactory,
189  repositoryLogger, () =>
190  {
191  logger.LogTrace("Releasing semaphore due to Repository disposal...");
192  semaphore.Release();
193  });
194  }
195  catch
196  {
197  semaphore.Release();
198  throw;
199  }
200  }
201  catch (RepositoryNotFoundException e)
202  {
203  logger.LogDebug("Repository not found!");
204  logger.LogTrace("Exception: {0}", e);
205  return null;
206  }
207  }
208 
210  public async Task DeleteRepository(CancellationToken cancellationToken)
211  {
212  logger.LogInformation("Deleting repository...");
213  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
214  {
215  logger.LogTrace("Semaphore acquired, deleting Repository directory...");
216  await ioManager.DeleteDirectory(ioManager.ResolvePath(), cancellationToken).ConfigureAwait(false);
217  }
218  }
219  }
220 }
ErrorCode
Types of ErrorMessages that the API may return.
Definition: ErrorCode.cs:10
readonly ILogger< RepositoryManager > logger
The ILogger for the RepositoryManager
For low level interactions with a LibGit2Sharp.IRepository.
RepositoryManager(ILibGit2RepositoryFactory repositoryFactory, ILibGit2Commands commands, IIOManager ioManager, IEventConsumer eventConsumer, ILogger< Repository > repositoryLogger, ILogger< RepositoryManager > logger)
Construct a RepositoryManager
readonly ILogger< Repository > repositoryLogger
The ILogger created Repositorys
readonly SemaphoreSlim semaphore
Used for controlling single access to the IRepository
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:9
Represents a git repository
Definition: Repository.cs:8
readonly IIOManager ioManager
The IIOManager for the RepositoryManager
Consumes EventTypes and takes the appropriate actions
readonly ILibGit2RepositoryFactory repositoryFactory
The ILibGit2RepositoryFactory for the RepositoryManager
Factory for creating and loading IRepositorys
readonly ILibGit2Commands commands
The ILibGit2Commands for the RepositoryManager.
readonly IEventConsumer eventConsumer
The IEventConsumer for the RepositoryManager
async Task< IRepository > CloneRepository(Uri url, string initialBranch, string username, string password, Action< int > progressReporter, CancellationToken cancellationToken)
Clone the repository at url
Interface for using filesystems
Definition: IIOManager.cs:11
async Task< IRepository > LoadRepository(CancellationToken cancellationToken)
Attempt to load the IRepository from the default location
async Task DeleteRepository(CancellationToken cancellationToken)
Delete the current repository
static async Task< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
Asyncronously locks a semaphore