diff --git a/Appy.GitDb.Core/Appy.GitDb.Core.csproj b/Appy.GitDb.Core/Appy.GitDb.Core.csproj index 8fe82b2..29c7182 100644 --- a/Appy.GitDb.Core/Appy.GitDb.Core.csproj +++ b/Appy.GitDb.Core/Appy.GitDb.Core.csproj @@ -1,14 +1,14 @@ - + - net461;netstandard2.0 + net10.0 1591 AnyCpu - 0.14.0 + 0.14.0 - + - + - - \ No newline at end of file + + diff --git a/Appy.GitDb.Local/Appy.GitDb.Local.csproj b/Appy.GitDb.Local/Appy.GitDb.Local.csproj index 60ca672..4d93885 100644 --- a/Appy.GitDb.Local/Appy.GitDb.Local.csproj +++ b/Appy.GitDb.Local/Appy.GitDb.Local.csproj @@ -1,18 +1,17 @@ - + - net461 + net10.0 1591 AnyCpu $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage 0.17.0 - + - - - - + + + @@ -24,8 +23,8 @@ - + - \ No newline at end of file + diff --git a/Appy.GitDb.Local/LocalGitDb.cs b/Appy.GitDb.Local/LocalGitDb.cs index 13ca8f3..ca3ee70 100644 --- a/Appy.GitDb.Local/LocalGitDb.cs +++ b/Appy.GitDb.Local/LocalGitDb.cs @@ -63,7 +63,7 @@ public LocalGitDb(string path, string remoteName = null, string remoteUrl = null if (!string.IsNullOrEmpty(_remoteUrl)) { _logger.Trace($"No repository exists on disk and there's a remote URL, cloning the repo from {_remoteUrl}"); - Repository.Clone(_remoteUrl, path, new CloneOptions {IsBare = true, CredentialsProvider = credentials}); + Repository.Clone(_remoteUrl, path, new CloneOptions { IsBare = true, FetchOptions = { CredentialsProvider = credentials } }); } else { diff --git a/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj b/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj index 139edc9..eb5406e 100644 --- a/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj +++ b/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj @@ -1,22 +1,17 @@ - - + + - net461;netstandard2.0 + net10.0 1591 - AnyCpu + AnyCpu $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage 0.18.0 - + - + - - - - - true @@ -26,8 +21,8 @@ - + - \ No newline at end of file + diff --git a/Appy.GitDb.Remote/RemoteGitDb.cs b/Appy.GitDb.Remote/RemoteGitDb.cs index c105d52..292a310 100644 --- a/Appy.GitDb.Remote/RemoteGitDb.cs +++ b/Appy.GitDb.Remote/RemoteGitDb.cs @@ -6,7 +6,7 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; -using System.Web; +using System.Net; using Appy.GitDb.Core.Interfaces; using Appy.GitDb.Core.Model; using Newtonsoft.Json; @@ -34,7 +34,7 @@ public RemoteGitDb(string userName, string password, string url, int batchSize = string urlEncode(string value) => - HttpUtility.UrlEncode(value); + WebUtility.UrlEncode(value); public Task Get(string branch, string key) => _client.GetAsync($"/{branch}/document/{urlEncode(key)}"); diff --git a/Appy.GitDb.Server/App.config b/Appy.GitDb.Server/App.config deleted file mode 100644 index e3ed6b4..0000000 --- a/Appy.GitDb.Server/App.config +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.Server/App.cs b/Appy.GitDb.Server/App.cs index b1b86b7..6bc8eaf 100644 --- a/Appy.GitDb.Server/App.cs +++ b/Appy.GitDb.Server/App.cs @@ -1,76 +1,61 @@ -using System; using System.Collections.Generic; -using System.Web.Http; -using System.Web.Http.ExceptionHandling; using Appy.GitDb.Core.Interfaces; using Appy.GitDb.Server.Auth; using Appy.GitDb.Server.Logging; -using Autofac; -using Autofac.Integration.WebApi; -using Microsoft.Owin.Extensions; -using Microsoft.Owin.Hosting; -using NLog; -using Owin; -using ExceptionLogger = Appy.GitDb.Server.Logging.ExceptionLogger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using NLog.LayoutRenderers; +using AppyExceptionLayoutRenderer = Appy.GitDb.Server.Logging.ExceptionLayoutRenderer; namespace Appy.GitDb.Server { public class App { - App(){} - IContainer _container; - string _url; - IEnumerable _users; - Logger _serverLog; + App() { } - public static App Create(string url, IGitDb repo, IEnumerable users) + static App() { - var builder = new ContainerBuilder(); - builder.RegisterInstance(repo).As().ExternallyOwned(); - builder.RegisterApiControllers(typeof(App).Assembly); + LayoutRenderer.Register("appy-exception"); + LayoutRenderer.Register("correlationid"); + } - var app = new App - { - _container = builder.Build(), - _url = url, - _users = users, - _serverLog = LogManager.GetLogger("server-log") - }; - LoggingMiddleware.Logger = LogManager.GetLogger("server-log"); + public static WebApplication Create(string url, IGitDb repo, IEnumerable users) + { + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseUrls(url); + ConfigureServices(builder.Services, repo, users); + var app = builder.Build(); + ConfigurePipeline(app); return app; } - public IDisposable Start() + public static IWebHostBuilder CreateTestBuilder(IGitDb repo, IEnumerable users) => + new WebHostBuilder() + .ConfigureServices(services => ConfigureServices(services, repo, users)) + .Configure(ConfigurePipeline); + + static void ConfigureServices(IServiceCollection services, IGitDb repo, IEnumerable users) { - try - { - _serverLog.Info("Starting up git server"); - var result = WebApp.Start(new StartOptions(_url), Configuration); - _serverLog.Info($"Server started on {_url}"); - return result; - } - catch (Exception ex) - { - _serverLog.Fatal(ex, string.Empty); - throw; - } + services.AddSingleton(repo); + services.AddSingleton(users); + services.AddTransient(); + services.AddAuthentication("Basic") + .AddScheme("Basic", null); + services.AddAuthorization(); + services.AddResponseCompression(); + services.AddControllers() + .AddNewtonsoftJson(); } - - public void Configuration(IAppBuilder app) + static void ConfigurePipeline(IApplicationBuilder app) { - var config = new HttpConfiguration(); - config.Services.Add(typeof(IExceptionLogger), new ExceptionLogger()); - app.UseAutofacMiddleware(_container); - var auth = new Authentication(_users); - app.UseBasicAuthentication("appy.gitdb", auth.ValidateUsernameAndPassword); - config.MapHttpAttributeRoutes(); - config.DependencyResolver = new AutofacWebApiDependencyResolver(_container); - - app.Use(); - app.UseCompressionModule(OwinCompression.DefaultCompressionSettings); - app.UseStageMarker(PipelineStage.MapHandler); - app.UseWebApi(config); + app.UseMiddleware(); + app.UseResponseCompression(); + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => endpoints.MapControllers()); } } -} \ No newline at end of file +} diff --git a/Appy.GitDb.Server/Appy.GitDb.Server.csproj b/Appy.GitDb.Server/Appy.GitDb.Server.csproj index 6595878..2d7f496 100644 --- a/Appy.GitDb.Server/Appy.GitDb.Server.csproj +++ b/Appy.GitDb.Server/Appy.GitDb.Server.csproj @@ -1,153 +1,20 @@ - - - + + - Debug - AnyCPU - {A9FAB943-9F28-49F8-82FB-0380A22FD683} + net10.0 Exe - Properties - Appy.GitDb.Server - Appy.GitDb.Server - v4.6.1 - 512 - true + enable - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Autofac.4.1.1\lib\net45\Autofac.dll - True - - - ..\packages\Autofac.Owin.4.0.0\lib\net45\Autofac.Integration.Owin.dll - True - - - ..\packages\Autofac.WebApi2.4.0.0\lib\net45\Autofac.Integration.WebApi.dll - True - - - ..\packages\Autofac.WebApi2.Owin.4.0.0\lib\net45\Autofac.Integration.WebApi.Owin.dll - True - - - ..\packages\FSharp.Core.4.1.17\lib\net45\FSharp.Core.dll - True - - - ..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll - - - ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll - True - - - ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll - True - - - ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll - True - - - ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - - - ..\packages\NLog.4.4.9\lib\net45\NLog.dll - - - ..\packages\Owin.1.0\lib\net40\Owin.dll - True - - - ..\packages\Owin.Compression.1.0.15\lib\net45\Owin.Compression.dll - - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll - True - - - - ..\packages\Thinktecture.IdentityModel.Owin.BasicAuthentication.1.0.1\lib\net45\Thinktecture.IdentityModel.Owin.BasicAuthentication.dll - True - - - ..\packages\YamlDotNet.5.4.0\lib\net45\YamlDotNet.dll - - - - - - - - - - - - - - - + - - - Always - Designer - - - Designer - - + + + + - - {72ae8930-98ef-446d-8167-7dd251bcf4e6} - Appy.GitDb.Core - - - {37d87fb6-e7d6-4217-b021-38cea7233e6f} - Appy.GitDb.Local - + + - - - \ No newline at end of file + + diff --git a/Appy.GitDb.Server/Auth/BasicAuthHandler.cs b/Appy.GitDb.Server/Auth/BasicAuthHandler.cs new file mode 100644 index 0000000..cdeff83 --- /dev/null +++ b/Appy.GitDb.Server/Auth/BasicAuthHandler.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Appy.GitDb.Server.Auth +{ + public class BasicAuthOptions : AuthenticationSchemeOptions { } + + public class BasicAuthHandler : AuthenticationHandler + { + readonly IEnumerable _users; + + public BasicAuthHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + IEnumerable users) + : base(options, logger, encoder) + { + _users = users; + } + + protected override Task HandleAuthenticateAsync() + { + if (!Request.Headers.TryGetValue("Authorization", out var authHeader)) + return Task.FromResult(AuthenticateResult.NoResult()); + + try + { + var header = authHeader.ToString(); + if (!header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) + return Task.FromResult(AuthenticateResult.NoResult()); + + var credentials = Encoding.UTF8.GetString(Convert.FromBase64String(header["Basic ".Length..].Trim())); + var separatorIndex = credentials.IndexOf(':'); + if (separatorIndex < 0) + return Task.FromResult(AuthenticateResult.Fail("Invalid credentials format")); + + var userName = credentials[..separatorIndex]; + var password = credentials[(separatorIndex + 1)..]; + + var authentication = new Authentication(_users); + var claims = authentication.ValidateUsernameAndPassword(userName, password).GetAwaiter().GetResult(); + + if (claims == null) + return Task.FromResult(AuthenticateResult.Fail("Invalid username or password")); + + var identity = new ClaimsIdentity(claims, Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + return Task.FromResult(AuthenticateResult.Success(ticket)); + } + catch (Exception ex) + { + return Task.FromResult(AuthenticateResult.Fail(ex)); + } + } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + { + Response.Headers["WWW-Authenticate"] = "Basic realm=\"appy.gitdb\""; + Response.StatusCode = 401; + return Task.CompletedTask; + } + } +} diff --git a/Appy.GitDb.Server/GitApi.cs b/Appy.GitDb.Server/GitApi.cs index 08abac0..adc8a51 100644 --- a/Appy.GitDb.Server/GitApi.cs +++ b/Appy.GitDb.Server/GitApi.cs @@ -1,15 +1,15 @@ -using System; +using System; using System.Collections.Generic; -using System.Net; -using System.Net.Http; using System.Threading.Tasks; -using System.Web.Http; using Appy.GitDb.Core.Interfaces; using Appy.GitDb.Core.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Appy.GitDb.Server { - public class GitApiController : ApiController + [ApiController] + public class GitApiController : ControllerBase { readonly IGitDb _gitDb; @@ -21,55 +21,55 @@ public GitApiController(IGitDb gitDb) [Route("{branch}/document/{*key}")] [HttpGet] [Authorize(Roles = "admin, read")] - public Task Get(string branch, string key) => + public Task Get(string branch, string key) => result(() => _gitDb.Get(branch, key)); [Route("{branch}/documents/{*key}")] [HttpGet] [Authorize(Roles = "admin, read")] - public Task GetFiles(string branch, string key) => + public Task GetFiles(string branch, string key) => result(() => _gitDb.GetFiles(branch, key)); [Route("{branch}/{start}/{pageSize}/documents/{*key}")] [HttpGet] [Authorize(Roles = "admin, read")] - public Task GetFilesPaged(string branch, string key, int start, int pageSize) => + public Task GetFilesPaged(string branch, string key, int start, int pageSize) => result(() => _gitDb.GetFilesPaged(branch, key, start, pageSize)); [Route("{branch}/document")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task Save(string branch, [FromBody] SaveRequest request) => + public Task Save(string branch, [FromBody] SaveRequest request) => result(() => _gitDb.Save(branch, request.Message, request.Document, request.Author)); [Route("{branch}/document/delete")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task Delete(string branch, [FromBody] DeleteRequest request) => + public Task Delete(string branch, [FromBody] DeleteRequest request) => result(() => _gitDb.Delete(branch, request.Key, request.Message, request.Author)); [Route("{branch}/transactions/close")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task CloseTransactions(string branch) => + public Task CloseTransactions(string branch) => result(() => _gitDb.CloseTransactions(branch)); [Route("tag")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task Tag([FromBody] Reference reference) => + public Task Tag([FromBody] Reference reference) => result(() => _gitDb.Tag(reference)); [Route("branch")] [HttpGet] [Authorize(Roles = "admin,read")] - public Task GetBranches() => + public Task GetBranches() => result(() => _gitDb.GetAllBranches()); [Route("branch")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task CreateBranch([FromBody] Reference reference) => + public Task CreateBranch([FromBody] Reference reference) => result(() => _gitDb.CreateBranch(reference)); static readonly Dictionary _transactions = new Dictionary(); @@ -77,7 +77,7 @@ public Task CreateBranch([FromBody] Reference reference) => [Route("{branch}/transaction")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task CreateTransaction(string branch) => + public Task CreateTransaction(string branch) => result(async () => { var trans = await _gitDb.CreateTransaction(branch); @@ -89,45 +89,43 @@ public Task CreateTransaction(string branch) => [Route("{branch}")] [HttpDelete] [Authorize(Roles = "admin,write")] - public Task DeleteBranch(string branch) => + public Task DeleteBranch(string branch) => result(() => _gitDb.DeleteBranch(branch)); [Route("tag/{tag}")] [HttpDelete] [Authorize(Roles = "admin,write")] - public Task DeleteTag(string tag) => + public Task DeleteTag(string tag) => result(() => _gitDb.DeleteTag(tag)); [Route("{transactionId}/add")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task AddToTransaction(string transactionId, Document document) => + public Task AddToTransaction(string transactionId, Document document) => result(() => _transactions[transactionId].Add(document)); [Route("{transactionId}/addmany")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task AddToTransaction(string transactionId, List documents) => + public Task AddManyToTransaction(string transactionId, List documents) => result(() => _transactions[transactionId].AddMany(documents)); - [Route("{transactionId}/delete/{key}")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task DeleteInTransaction(string transactionId, string key) => + public Task DeleteInTransaction(string transactionId, string key) => result(() => _transactions[transactionId].Delete(key)); [Route("{transactionId}/deleteMany")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task DeleteInTransaction(string transactionId, List keys) => + public Task DeleteManyInTransaction(string transactionId, List keys) => result(() => _transactions[transactionId].DeleteMany(keys)); - [Route("{transactionId}/commit")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task CommitTransaction(string transactionId, [FromBody] CommitTransaction commit) => + public Task CommitTransaction(string transactionId, [FromBody] CommitTransaction commit) => result(async () => { var transaction = _transactions[transactionId]; @@ -139,7 +137,7 @@ public Task CommitTransaction(string transactionId, [FromBody [Route("{transactionId}/abort")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task AbortTransaction(string transactionId) => + public Task AbortTransaction(string transactionId) => result(async () => { var transaction = _transactions[transactionId]; @@ -150,29 +148,28 @@ public Task AbortTransaction(string transactionId) => [Route("merge")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task Merge(MergeRequest mergeRequest) => - result(() =>_gitDb.MergeBranch(mergeRequest.Source, mergeRequest.Target, mergeRequest.Author, mergeRequest.Message)); + public Task Merge(MergeRequest mergeRequest) => + result(() => _gitDb.MergeBranch(mergeRequest.Source, mergeRequest.Target, mergeRequest.Author, mergeRequest.Message)); [Route("{branch}/rebase")] [HttpPost] [Authorize(Roles = "admin,write")] - public Task Rebase(RebaseRequest rebaseRequest) => + public Task Rebase(RebaseRequest rebaseRequest) => result(() => _gitDb.RebaseBranch(rebaseRequest.Source, rebaseRequest.Target, rebaseRequest.Author, rebaseRequest.Message)); [Route("diff/{reference}/{reference2}")] [HttpGet] [Authorize(Roles = "admin,read")] - public Task Diff(string reference, string reference2) => + public Task Diff(string reference, string reference2) => result(() => _gitDb.Diff(reference, reference2)); [Route("log/{reference}/{reference2}")] [HttpGet] [Authorize(Roles = "admin,read")] - public Task Log(string reference, string reference2) => + public Task Log(string reference, string reference2) => result(() => _gitDb.Log(reference, reference2)); - - async Task result(Func> action) + async Task result(Func> action) { try { @@ -180,15 +177,15 @@ async Task result(Func> action) } catch (ArgumentException ex) { - throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = new StringContent(ex.Message)}); + return BadRequest(ex.Message); } catch (NotSupportedException ex) { - throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ex.Message) }); + return BadRequest(ex.Message); } } - async Task result(Func action) + async Task result(Func action) { try { @@ -197,13 +194,12 @@ async Task result(Func action) } catch (ArgumentException ex) { - throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = new StringContent(ex.Message)}); + return BadRequest(ex.Message); } catch (NotSupportedException ex) { - throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ex.Message) }); + return BadRequest(ex.Message); } } - } -} \ No newline at end of file +} diff --git a/Appy.GitDb.Server/GitProtocol.cs b/Appy.GitDb.Server/GitProtocol.cs index 900a463..9864771 100644 --- a/Appy.GitDb.Server/GitProtocol.cs +++ b/Appy.GitDb.Server/GitProtocol.cs @@ -1,65 +1,76 @@ -using System; -using System.Configuration; +using System; using System.Diagnostics; using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; +using System.Text; using System.Threading.Tasks; -using System.Web.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; namespace Appy.GitDb.Server { - [RoutePrefix("server")] - public class GitProtocolController : ApiController + [ApiController] + [Route("server")] + public class GitProtocolController : ControllerBase { const string FlushMessage = "0000"; - readonly string _gitPath = ConfigurationManager.AppSettings["git.path"]; - readonly string _gitHomePath = ConfigurationManager.AppSettings["git.homePath"]; - readonly string _repoPath = ConfigurationManager.AppSettings["git.repository.path"]; + readonly string _gitPath; + readonly string _gitHomePath; + readonly string _repoPath; - [HttpGet] - [Route("repository.git")] + public GitProtocolController(IConfiguration config) + { + _gitPath = config["git.path"]; + _gitHomePath = config["git.homePath"]; + _repoPath = config["git.repository.path"]; + } + + [HttpGet("repository.git")] [Authorize(Roles = "admin, read")] - public IHttpActionResult GitUrl() => - Ok(); + public IActionResult GitUrl() => Ok(); - [HttpGet] - [Route("repository.git/info/refs")] + [HttpGet("repository.git/info/refs")] [Authorize(Roles = "admin, read")] - public IHttpActionResult InfoRefs(string service) => - new GitResult( - Request, - $"application/x-{service}-advertisement", - (input, outStream) => executeServiceByName(input, outStream, service.Substring(4), true, false), - formatMessage($"# service={service}\n") + FlushMessage); - - [HttpPost, Route("repository.git/git-upload-pack")] + public IActionResult InfoRefs([FromQuery] string service) + { + var contentType = $"application/x-{service}-advertisement"; + var preamble = formatMessage($"# service={service}\n") + FlushMessage; + return new GitStreamResult( + contentType, + async (input, output) => + { + var writer = new StreamWriter(output, leaveOpen: true); + writer.Write(preamble); + await writer.FlushAsync(); + await executeServiceByName(input, output, service.Substring(4), true, false); + }); + } + + [HttpPost("repository.git/git-upload-pack")] [Authorize(Roles = "admin, read")] - public IHttpActionResult UploadPack() => - new GitResult(Request, - "application/x-git-upload-pack-result", - (input, outStream) => executeServiceByName(input, outStream, "upload-pack", false, true)); + public IActionResult UploadPack() => + new GitStreamResult( + "application/x-git-upload-pack-result", + (input, output) => executeServiceByName(input, output, "upload-pack", false, true)); - [HttpPost, Route("repository.git/git-receive-pack")] + [HttpPost("repository.git/git-receive-pack")] [Authorize(Roles = "admin, write")] - public IHttpActionResult ReceivePack() => - new GitResult(Request, - "application/x-git-receive-pack-result", - (input, outStream) => executeServiceByName(input, outStream, "receive-pack", false, false)); + public IActionResult ReceivePack() => + new GitStreamResult( + "application/x-git-receive-pack-result", + (input, output) => executeServiceByName(input, output, "receive-pack", false, false)); - static string formatMessage(string input) => + static string formatMessage(string input) => (input.Length + 4).ToString("X").PadLeft(4, '0') + input; async Task executeServiceByName(Stream input, Stream output, string serviceName, bool addAdvertiseRefs, bool closeInput) { - var args = serviceName + " --stateless-rpc"; + var args = new StringBuilder(serviceName).Append(" --stateless-rpc"); if (addAdvertiseRefs) - args += " --advertise-refs"; - args += " \"" + _repoPath + "\""; + args.Append(" --advertise-refs"); + args.Append($" \"{_repoPath}\""); - var info = new ProcessStartInfo(_gitPath + @"\git.exe", args) + var info = new ProcessStartInfo(Path.Combine(_gitPath, "git.exe"), args.ToString()) { CreateNoWindow = true, RedirectStandardError = true, @@ -73,57 +84,39 @@ async Task executeServiceByName(Stream input, Stream output, string serviceName, info.EnvironmentVariables.Remove("HOME"); info.EnvironmentVariables.Add("HOME", _gitHomePath); - using (var process = Process.Start(info)) - { - await input.CopyToAsync(process.StandardInput.BaseStream); - if (closeInput) - process.StandardInput.Close(); - else - process.StandardInput.Write('\0'); - - await process.StandardOutput.BaseStream.CopyToAsync(output); - process.WaitForExit(); - } - } + using var process = Process.Start(info); + await input.CopyToAsync(process.StandardInput.BaseStream); + if (closeInput) + process.StandardInput.Close(); + else + process.StandardInput.Write('\0'); + + await process.StandardOutput.BaseStream.CopyToAsync(output); + process.WaitForExit(); + } } - public class GitResult : IHttpActionResult + public class GitStreamResult : IActionResult { - readonly HttpRequestMessage _request; readonly string _contentType; - readonly string _advertiseRefsContent; - readonly Func _executeGitCommand; + readonly Func _execute; - public GitResult(HttpRequestMessage request, string contentType, Func executeGitCommand, string advertiseRefsContent = null) + public GitStreamResult(string contentType, Func execute) { - _request = request; _contentType = contentType; - _advertiseRefsContent = advertiseRefsContent; - _executeGitCommand = executeGitCommand; + _execute = execute; } - public async Task ExecuteAsync(CancellationToken cancellationToken) + public async Task ExecuteResultAsync(ActionContext context) { - var resp = _request.CreateResponse(HttpStatusCode.OK); - var input = await _request.Content.ReadAsStreamAsync(); - resp.Content = new PushStreamContent(async (output, content, context) => - { - if (_advertiseRefsContent != null) - { - var writer = new StreamWriter(output); - writer.Write(_advertiseRefsContent); - await writer.FlushAsync(); - } - await _executeGitCommand(input, output); - output.Close(); - }); - - resp.Content.Headers.Expires = DateTimeOffset.MinValue; - resp.Headers.Add("pragma", "no-cache"); - resp.Headers.CacheControl = CacheControlHeaderValue.Parse("no-cache, max-age=0, must-revalidate"); - resp.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType); - - return resp; + var response = context.HttpContext.Response; + response.ContentType = _contentType; + response.Headers["Pragma"] = "no-cache"; + response.Headers["Cache-Control"] = "no-cache, max-age=0, must-revalidate"; + response.Headers["Expires"] = DateTimeOffset.MinValue.ToString("R"); + + var input = context.HttpContext.Request.Body; + await _execute(input, response.Body); } } -} \ No newline at end of file +} diff --git a/Appy.GitDb.Server/Logging/ExceptionLogger.cs b/Appy.GitDb.Server/Logging/ExceptionLogger.cs deleted file mode 100644 index 192c4c3..0000000 --- a/Appy.GitDb.Server/Logging/ExceptionLogger.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Web.Http.ExceptionHandling; - -namespace Appy.GitDb.Server.Logging -{ - public class ExceptionLogger : IExceptionLogger - { - public Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken) - { - context.Request.GetOwinContext().Set("exception", context.Exception); - return Task.CompletedTask; - } - } -} diff --git a/Appy.GitDb.Server/Logging/LogContext.cs b/Appy.GitDb.Server/Logging/LogContext.cs index 4e3cb55..ae30054 100644 --- a/Appy.GitDb.Server/Logging/LogContext.cs +++ b/Appy.GitDb.Server/Logging/LogContext.cs @@ -1,5 +1,5 @@ -using System; -using System.Runtime.Remoting.Messaging; +using System; +using System.Threading; using NLog; namespace Appy.GitDb.Server.Logging @@ -7,6 +7,7 @@ namespace Appy.GitDb.Server.Logging public class LogContext : IDisposable { const string CorrelationIdKey = "lctx:correlationid"; + static readonly AsyncLocal _correlationId = new(); public string CorrelationId { get; } @@ -21,19 +22,17 @@ public void Dispose() => public static void SetCorrelationId(string correlationId) { - CallContext.LogicalSetData(CorrelationIdKey, correlationId); + _correlationId.Value = correlationId; MappedDiagnosticsLogicalContext.Set(CorrelationIdKey, correlationId); } public static void Clear() { - CallContext.LogicalSetData(CorrelationIdKey, null); + _correlationId.Value = null; MappedDiagnosticsLogicalContext.Clear(); } - public static string GetCorrelationId() => get(CorrelationIdKey); - - static string get(string key) => - CallContext.LogicalGetData(key) as string ?? MappedDiagnosticsLogicalContext.Get(key); + public static string GetCorrelationId() => + _correlationId.Value ?? MappedDiagnosticsLogicalContext.Get(CorrelationIdKey); } } diff --git a/Appy.GitDb.Server/Logging/LoggingMiddleWare.cs b/Appy.GitDb.Server/Logging/LoggingMiddleWare.cs index cf341e1..1d00255 100644 --- a/Appy.GitDb.Server/Logging/LoggingMiddleWare.cs +++ b/Appy.GitDb.Server/Logging/LoggingMiddleWare.cs @@ -1,84 +1,65 @@ -using System; +using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using Microsoft.Owin; +using Microsoft.AspNetCore.Http; using NLog; namespace Appy.GitDb.Server.Logging { - public class LoggingMiddleware : OwinMiddleware + public class LoggingMiddleware : IMiddleware { - public static Logger Logger; - public LoggingMiddleware(OwinMiddleware next) : base(next){ } - public override async Task Invoke(IOwinContext context) - { - var correlationid = context.Request.Headers["X-FORWARD-CORRELATIONID"] ?? Guid.NewGuid().ToString("N"); - - LogContext.SetCorrelationId(correlationid); - - context.Response.OnSendingHeaders(state => - { - var ctx = state as IOwinContext; - if (ctx == null) return; - if (!ctx.Response.Headers.ContainsKey("correlationid")) - ctx.Response.Headers.Add("correlationid", new[] { correlationid }); - }, context); - - var watch = new Stopwatch(); - watch.Start(); + public static Logger Logger = LogManager.GetLogger("server-log"); - await Next.Invoke(context); + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + var correlationId = context.Request.Headers["X-FORWARD-CORRELATIONID"].FirstOrDefault() + ?? Guid.NewGuid().ToString("N"); - watch.Stop(); + LogContext.SetCorrelationId(correlationId); + context.Response.Headers["correlationid"] = correlationId; - var identity = context.Request.User?.Identity; - var userName = identity?.Name; - var user = !string.IsNullOrEmpty(userName) - ? userName - : "(anonymous)"; + var watch = Stopwatch.StartNew(); + Exception caughtException = null; - var exception = context.Environment.ContainsKey("exception") - ? (Exception)context.Environment["exception"] - : null; + try + { + await next(context); + } + catch (Exception ex) + { + caughtException = ex; + throw; + } + finally + { + watch.Stop(); + var userName = context.User?.Identity?.Name ?? "(anonymous)"; - LogLevel level; - if (exception != null || context.Response.StatusCode >= 500) - level = LogLevel.Error; - else if (context.Response.StatusCode >= 400) - level = LogLevel.Warn; - else - level = LogLevel.Trace; - + var level = caughtException != null || context.Response.StatusCode >= 500 + ? LogLevel.Error + : context.Response.StatusCode >= 400 + ? LogLevel.Warn + : LogLevel.Trace; - Logger.Log(new LogEventInfo - { - Level = level, - Exception = exception, - LoggerName = "http-log", - Properties = + Logger.Log(new LogEventInfo { - { "user", user}, - { "method", context.Request.Method}, - { "url", context.Request.Uri.AbsolutePath}, - { "querystring", context.Request.Uri.Query}, - { "request-headers", toLogString(context.Request.Headers)}, - { "response-headers", toLogString(context.Response.Headers) }, - { "statuscode", context.Response.StatusCode }, - { "reason", context.Response.ReasonPhrase }, - { "duration", watch.Elapsed.TotalMilliseconds }, - { "useragent", getValue(context.Request.Headers, "User-Agent") }, - { "host", context.Request.Uri.Host } - } - }); + Level = level, + Exception = caughtException, + LoggerName = "http-log", + Properties = + { + { "user", userName }, + { "method", context.Request.Method }, + { "url", context.Request.Path.Value }, + { "querystring", context.Request.QueryString.Value }, + { "statuscode", context.Response.StatusCode }, + { "duration", watch.Elapsed.TotalMilliseconds }, + { "useragent", context.Request.Headers["User-Agent"].FirstOrDefault() ?? string.Empty }, + { "host", context.Request.Host.Value } + } + }); + } } - - static string toLogString(IHeaderDictionary headers) => - string.Join("\n", headers.Select(kv => kv.Key + ": " + string.Join(" ", kv.Value))); - - static string getValue(IHeaderDictionary headers, string key) => - headers.ContainsKey(key) - ? string.Join(" ", headers[key]) - : string.Empty; } -} \ No newline at end of file +} diff --git a/Appy.GitDb.Server/NLog.config b/Appy.GitDb.Server/NLog.config index f32cb58..05d3694 100644 --- a/Appy.GitDb.Server/NLog.config +++ b/Appy.GitDb.Server/NLog.config @@ -1,5 +1,5 @@ - + + encoding="utf-8"> diff --git a/Appy.GitDb.Server/Program.cs b/Appy.GitDb.Server/Program.cs index 5611086..8c7354b 100644 --- a/Appy.GitDb.Server/Program.cs +++ b/Appy.GitDb.Server/Program.cs @@ -1,11 +1,8 @@ -using System; using System.Collections.Generic; -using System.Configuration; using Appy.GitDb.Local; using Appy.GitDb.Server.Auth; using Appy.GitDb.Server.Logging; -using NLog.LayoutRenderers; -using ExceptionLayoutRenderer = Appy.GitDb.Server.Logging.ExceptionLayoutRenderer; +using Microsoft.Extensions.Configuration; namespace Appy.GitDb.Server { @@ -13,30 +10,31 @@ class Program { public static void Main(string[] args) { - LayoutRenderer.Register("appy-exception"); - LayoutRenderer.Register("correlationid"); - var url = ConfigurationManager.AppSettings["server.url"]; - var gitRepoPath = ConfigurationManager.AppSettings["git.repository.path"]; - var remoteName = ConfigurationManager.AppSettings["remote.name"]; - var remoteUrl = ConfigurationManager.AppSettings["remote.url"]; - var userName = ConfigurationManager.AppSettings["remote.user.name"]; - var userEmail = ConfigurationManager.AppSettings["remote.user.email"]; - var password = ConfigurationManager.AppSettings["remote.user.password"]; - var remoteBranchSync = ConfigurationManager.AppSettings["remote.branch.sync"]; - if (!int.TryParse(ConfigurationManager.AppSettings["transactions.timeout"], out int transactionTimeout)) + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true) + .AddEnvironmentVariables() + .AddCommandLine(args) + .Build(); + + var url = config["server.url"] ?? "http://+:9500"; + var gitRepoPath = config["git.repository.path"]; + var remoteName = config["remote.name"]; + var remoteUrl = config["remote.url"]; + var userName = config["remote.user.name"]; + var userEmail = config["remote.user.email"]; + var password = config["remote.user.password"]; + var remoteBranchSync = config["remote.branch.sync"]; + if (!int.TryParse(config["transactions.timeout"], out int transactionTimeout)) transactionTimeout = 10; var app = App.Create(url, new LocalGitDb(gitRepoPath, remoteName, remoteUrl, userName, userEmail, password, remoteBranchSync, transactionTimeout), new List { - new User{ UserName = "GitAdmin", Password = ConfigurationManager.AppSettings["GitAdmin"], Roles = new [] { "admin","read","write" }}, - new User{ UserName = "GitReader",Password = ConfigurationManager.AppSettings["GitAdmin"],Roles = new [] { "read" }}, - new User{ UserName = "GitWriter", Password = ConfigurationManager.AppSettings["GitWriter"] ,Roles = new [] { "write" }} + new User { UserName = "GitAdmin", Password = config["GitAdmin"], Roles = new[] { "admin", "read", "write" } }, + new User { UserName = "GitReader", Password = config["GitAdmin"], Roles = new[] { "read" } }, + new User { UserName = "GitWriter", Password = config["GitWriter"], Roles = new[] { "write" } } }); - using (app.Start()) - { - Console.WriteLine("Press any key to exit"); - Console.ReadKey(); - } + + app.Run(); } } } diff --git a/Appy.GitDb.Server/Properties/AssemblyInfo.cs b/Appy.GitDb.Server/Properties/AssemblyInfo.cs deleted file mode 100644 index b9c061e..0000000 --- a/Appy.GitDb.Server/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Appy.GitDb.Server")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Server")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("a9fab943-9f28-49f8-82fb-0380a22fd683")] - -[assembly: AssemblyVersion("1.0.*")] \ No newline at end of file diff --git a/Appy.GitDb.Server/appsettings.json b/Appy.GitDb.Server/appsettings.json new file mode 100644 index 0000000..d6768d8 --- /dev/null +++ b/Appy.GitDb.Server/appsettings.json @@ -0,0 +1,16 @@ +{ + "server.url": "http://+:9500", + "git.repository.path": "", + "remote.name": "", + "remote.url": "", + "remote.user.name": "", + "remote.user.email": "", + "remote.user.password": "", + "remote.branch.sync": "", + "transactions.timeout": "10", + "GitAdmin": "", + "GitReader": "", + "GitWriter": "", + "git.path": "", + "git.homePath": "" +} diff --git a/Appy.GitDb.Server/packages.config b/Appy.GitDb.Server/packages.config deleted file mode 100644 index 2e6cf6a..0000000 --- a/Appy.GitDb.Server/packages.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj b/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj index b5c795f..b0ab7d7 100644 --- a/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj +++ b/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj @@ -1,186 +1,34 @@ - - - - - + + - Debug - AnyCPU - {1C2D86C4-8CAD-457E-B4F1-86A5523B3112} - Library - Properties - Appy.GitDb.Tests - Appy.GitDb.Tests - v4.6.1 - 512 - - + net10.0 + false + enable + true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll - True - - - ..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.dll - True - - - ..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.Core.dll - True - - - ..\packages\FSharp.Core.4.1.17\lib\net45\FSharp.Core.dll - True - - - ..\packages\LibGit2Sharp.0.26.2\lib\net46\LibGit2Sharp.dll - - - ..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll - - - ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll - True - - - ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll - True - - - ..\packages\Microsoft.Owin.Testing.3.0.1\lib\net45\Microsoft.Owin.Testing.dll - True - - - ..\packages\Moq.4.5.22\lib\net45\Moq.dll - True - - - ..\packages\Moq.AutoMock.0.4.0.0\lib\net40\Moq.AutoMock.dll - True - - - ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - - - ..\packages\NLog.4.4.9\lib\net45\NLog.dll - - - ..\packages\Owin.1.0\lib\net40\Owin.dll - True - - - - - ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll - - - - - - ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll - True - - - ..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll - True - - - ..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll - True - - - ..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - + - - {72ae8930-98ef-446d-8167-7dd251bcf4e6} - Appy.GitDb.Core - - - {37d87fb6-e7d6-4217-b021-38cea7233e6f} - Appy.GitDb.Local - - - {3F7A7050-68CB-4D7F-9149-A59095EB78A1} - Appy.GitDb.Remote - - - {A9FAB943-9F28-49F8-82FB-0380A22FD683} - Appy.GitDb.Server - - - {e3eba271-650e-4482-bdea-41f569051b4d} - Appy.GitDb.Watcher - + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + - + + + + + - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file + + diff --git a/Appy.GitDb.Tests/Merging.cs b/Appy.GitDb.Tests/Merging.cs index aa9ec70..68683c7 100644 --- a/Appy.GitDb.Tests/Merging.cs +++ b/Appy.GitDb.Tests/Merging.cs @@ -77,11 +77,11 @@ async Task removeItems(string branch, int start, int count) [Fact] public void FirstMergesShouldSuccedWithValidInfo() => - _firstMergeResult.ShouldBeEquivalentTo(MergeInfo.Succeeded("test2", "master", _commitBeforeSecondMerge)); + _firstMergeResult.Should().BeEquivalentTo(MergeInfo.Succeeded("test2", "master", _commitBeforeSecondMerge)); [Fact] public void SecondMergeShouldSuccedWithValidInfo() => - _secondMergeResult.ShouldBeEquivalentTo(MergeInfo.Succeeded("test", "master", _commitAfterSecondMerge)); + _secondMergeResult.Should().BeEquivalentTo(MergeInfo.Succeeded("test", "master", _commitAfterSecondMerge)); [Fact] public async Task AddsTheCorrectFilesToMaster() => @@ -126,7 +126,7 @@ protected override async Task Because() [Fact] public void MergeShouldSuccedWithValidInfo() => - _mergeResult.ShouldBeEquivalentTo(MergeInfo.Succeeded("test", "master", string.Empty)); + _mergeResult.Should().BeEquivalentTo(MergeInfo.Succeeded("test", "master", string.Empty)); [Fact] public void DoesNotCreateACommitOnMaster() => @@ -179,7 +179,7 @@ public void ShouldNotSucceedAndReturnConflicts() c.TargetSha = null; }); - _mergeResult.ShouldBeEquivalentTo(new MergeInfo + _mergeResult.Should().BeEquivalentTo(new MergeInfo { Message = "Could not merge test into master because of conflicts. Please merge manually", SourceBranch = "test", @@ -236,7 +236,7 @@ public void ShouldNotSucceedAndReturnConflicts() c.TargetSha = null; }); - _mergeResult.ShouldBeEquivalentTo(new MergeInfo + _mergeResult.Should().BeEquivalentTo(new MergeInfo { Message = "Could not merge test into master because of conflicts. Please merge manually", SourceBranch = "test", diff --git a/Appy.GitDb.Tests/Properties/AssemblyInfo.cs b/Appy.GitDb.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index b47b640..0000000 --- a/Appy.GitDb.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using Xunit; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Appy.GitDb.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Tests")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1c2d86c4-8cad-457e-b4f1-86a5523b3112")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/Appy.GitDb.Tests/Rebasing.cs b/Appy.GitDb.Tests/Rebasing.cs index bf60b93..4452a3d 100644 --- a/Appy.GitDb.Tests/Rebasing.cs +++ b/Appy.GitDb.Tests/Rebasing.cs @@ -62,7 +62,7 @@ async Task removeItems(string branch, int start, int count) [Fact] public void RebaseShouldSuccedWithValidInfo() => - _rebaseResult.ShouldBeEquivalentTo(RebaseInfo.Succeeded("test", "master", Repo.Branches["test"].Tip.Sha)); + _rebaseResult.Should().BeEquivalentTo(RebaseInfo.Succeeded("test", "master", Repo.Branches["test"].Tip.Sha)); [Fact] public async Task AddsTheCorrectFilesToTheBranch() => @@ -100,7 +100,7 @@ protected override async Task Because() [Fact] public void RebaseShouldSuccedWithValidInfo() => - _rebaseResult.ShouldBeEquivalentTo(RebaseInfo.Succeeded("test", "master", string.Empty)); + _rebaseResult.Should().BeEquivalentTo(RebaseInfo.Succeeded("test", "master", string.Empty)); [Fact] public void DoesNotCreateACommitOnSourceBranch() => @@ -152,7 +152,7 @@ public void ShouldNotSucceedAndReturnConflicts() c.TargetSha = null; }); - _rebaseResult.ShouldBeEquivalentTo(new RebaseInfo + _rebaseResult.Should().BeEquivalentTo(new RebaseInfo { Message = "Could not rebase test onto master because of conflicts. Please merge manually", SourceBranch = "test", @@ -210,7 +210,7 @@ public void ShouldNotSucceedAndReturnConflicts() c.TargetSha = null; }); - _rebaseResult.ShouldBeEquivalentTo(new RebaseInfo + _rebaseResult.Should().BeEquivalentTo(new RebaseInfo { Message = "Could not rebase test onto master because of conflicts. Please merge manually", SourceBranch = "test", diff --git a/Appy.GitDb.Tests/Transactions.cs b/Appy.GitDb.Tests/Transactions.cs index 9f46ec6..17cac37 100644 --- a/Appy.GitDb.Tests/Transactions.cs +++ b/Appy.GitDb.Tests/Transactions.cs @@ -36,7 +36,7 @@ public void CreatesACommitWithTheCorrectAuthor() => public void CreatesASingleCommitWithAllKeys() => Repo.Branches[Branch].Tip.Tree .Select(e => new Document { Key = e.Path, Value = ((Blob)e.Target).GetContentText() }) - .ShouldBeEquivalentTo(_docs); + .Should().BeEquivalentTo(_docs); } public class DeletingItemsFromATransaction : WithRepo diff --git a/Appy.GitDb.Tests/Utils/WithRepo.cs b/Appy.GitDb.Tests/Utils/WithRepo.cs index b77497b..b66dc86 100644 --- a/Appy.GitDb.Tests/Utils/WithRepo.cs +++ b/Appy.GitDb.Tests/Utils/WithRepo.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Appy.GitDb.Core.Interfaces; @@ -13,7 +14,7 @@ using Appy.GitDb.Server.Auth; using Appy.GitDb.Watcher; using LibGit2Sharp; -using Microsoft.Owin.Testing; +using Microsoft.AspNetCore.TestHost; using Xunit; namespace Appy.GitDb.Tests.Utils @@ -28,37 +29,42 @@ public abstract class WithRepo : IAsyncLifetime TestServer _server; HttpClient _client; - protected static readonly User Admin = new User {UserName = "admin", Password = "admin", Roles = new[] {"admin", "read", "write"}}; + protected static readonly User Admin = new User { UserName = "admin", Password = "admin", Roles = new[] { "admin", "read", "write" } }; protected static readonly User ReadOnly = new User { UserName = "readonly", Password = "readonly", Roles = new[] { "read" } }; protected static readonly User WriteOnly = new User { UserName = "writeonly", Password = "writeonly", Roles = new[] { "write" } }; protected static readonly User ReadWrite = new User { UserName = "readwrite", Password = "readwrite", Roles = new[] { "read", "write" } }; - - protected static readonly User None = new User { UserName = "", Password = "", Roles = new string[0] }; + protected static readonly User None = new User { UserName = "", Password = "", Roles = Array.Empty() }; - readonly IEnumerable _users = new List {Admin, ReadOnly, WriteOnly, ReadWrite}; + readonly IEnumerable _users = new List { Admin, ReadOnly, WriteOnly, ReadWrite }; protected virtual Task Because() => Task.CompletedTask; static void deleteReadOnlyDirectory(string directory) { Directory.EnumerateDirectories(directory) + .ToList() .ForEach(deleteReadOnlyDirectory); - Directory.EnumerateFiles(directory).Select(file => new FileInfo(file) { Attributes = FileAttributes.Normal }) + Directory.EnumerateFiles(directory) + .Select(file => new FileInfo(file) { Attributes = FileAttributes.Normal }) + .ToList() .ForEach(fi => fi.Delete()); - Directory.Delete(directory); } protected void WithUser(User user) => - _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user.UserName}:{user.Password}"))); - + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Basic", + Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user.UserName}:{user.Password}"))); + public async Task InitializeAsync() { - const string url = "http://localhost"; // this is a dummy url, requests are in-memory, not over the network - var app = App.Create(url, new LocalGitDb(LocalPath, transactionTimeout: TransactionTimeout), _users); - _server = TestServer.Create(app.Configuration); - _client = _server.HttpClient; - WithUser(Admin); + const string url = "http://localhost"; + _server = new TestServer(App.CreateTestBuilder( + new LocalGitDb(LocalPath, transactionTimeout: TransactionTimeout), + _users)); + _client = _server.CreateClient(); + _client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); + WithUser(Admin); Subject = new RemoteGitDb(_client); Repo = new Repository(LocalPath); await Because(); @@ -73,12 +79,10 @@ public Task DisposeAsync() return Task.CompletedTask; } - // Use this method to inspect the resulting repository protected void MoveToNormalRepo(string baseDir) { if (Directory.Exists(baseDir)) deleteReadOnlyDirectory(baseDir); - Repository.Clone(LocalPath, baseDir, new CloneOptions { BranchName = "master", @@ -86,4 +90,4 @@ protected void MoveToNormalRepo(string baseDir) }); } } -} \ No newline at end of file +} diff --git a/Appy.GitDb.Tests/Utils/WithWatcher.cs b/Appy.GitDb.Tests/Utils/WithWatcher.cs index df6b2c3..de639a8 100644 --- a/Appy.GitDb.Tests/Utils/WithWatcher.cs +++ b/Appy.GitDb.Tests/Utils/WithWatcher.cs @@ -50,7 +50,7 @@ await Task.WhenAll(Enumerable.Range(0, 20) await Subject.Start(new List()); await Because(); - Thread.Sleep(500); + Thread.Sleep(2500); } public Task DisposeAsync() diff --git a/Appy.GitDb.Tests/app.config b/Appy.GitDb.Tests/app.config deleted file mode 100644 index 5b56fa8..0000000 --- a/Appy.GitDb.Tests/app.config +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.Tests/packages.config b/Appy.GitDb.Tests/packages.config deleted file mode 100644 index b21a13e..0000000 --- a/Appy.GitDb.Tests/packages.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj b/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj index b5a7377..5697b04 100644 --- a/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj +++ b/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj @@ -1,70 +1,13 @@ - - - - + + - Debug - AnyCPU - {E3EBA271-650E-4482-BDEA-41F569051B4D} - Library - Properties - Appy.GitDb.Watcher - Appy.GitDb.Watcher - v4.6.1 - 512 - - + net10.0 + 0.9.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\LibGit2Sharp.0.26.2\lib\net46\LibGit2Sharp.dll - - - ..\packages\NLog.4.4.9\lib\net45\NLog.dll - - - - - - - - - - - + - - + + - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file + + diff --git a/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs b/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs deleted file mode 100644 index e1e0f2c..0000000 --- a/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Appy.GitDb.Watcher")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Watcher")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("e3eba271-650e-4482-bdea-41f569051b4d")] - -[assembly: AssemblyVersion("0.11.0")] diff --git a/Appy.GitDb.Watcher/packages.config b/Appy.GitDb.Watcher/packages.config deleted file mode 100644 index 080ebd8..0000000 --- a/Appy.GitDb.Watcher/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.sln b/Appy.GitDb.sln index b31783b..1c4d529 100644 --- a/Appy.GitDb.sln +++ b/Appy.GitDb.sln @@ -57,9 +57,7 @@ Global {E3EBA271-650E-4482-BDEA-41F569051B4D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3EBA271-650E-4482-BDEA-41F569051B4D}.Release|Any CPU.Build.0 = Release|Any CPU {314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}.Debug|Any CPU.Build.0 = Debug|Any CPU {314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/global.json b/global.json index d906919..f4407e0 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { - "version": "2.2.102" + "version": "10.0.200", + "rollForward": "latestMajor" } - } \ No newline at end of file + }