diff --git a/Appy.GitDb.Benchmark/Appy.GitDb.Benchmark.csproj b/Appy.GitDb.Benchmark/Appy.GitDb.Benchmark.csproj index e374525..b4206aa 100644 --- a/Appy.GitDb.Benchmark/Appy.GitDb.Benchmark.csproj +++ b/Appy.GitDb.Benchmark/Appy.GitDb.Benchmark.csproj @@ -1,96 +1,37 @@ - - - - + + - Debug - AnyCPU - {314EE50D-F6E4-4CB6-AB00-22A7258CF8F5} Exe - Properties - Appy.GitDb.Benchmark - Appy.GitDb.Benchmark - v4.6.1 - 512 + net461 true - - + 1591 - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\CsvHelper.2.16.3.0\lib\net45\CsvHelper.dll - True - - - ..\packages\LibGit2Sharp.0.23.1\lib\net40\LibGit2Sharp.dll - - - ..\packages\NLog.4.4.9\lib\net45\NLog.dll - - - - - - - - - - + + + - - - - + + + + + - - + + + + + + + - - {72ae8930-98ef-446d-8167-7dd251bcf4e6} - Appy.GitDb.Core - - - {37d87fb6-e7d6-4217-b021-38cea7233e6f} - Appy.GitDb.Local - + + - - - - 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.Benchmark/Program.cs b/Appy.GitDb.Benchmark/Program.cs index 741c7a3..0c64968 100644 --- a/Appy.GitDb.Benchmark/Program.cs +++ b/Appy.GitDb.Benchmark/Program.cs @@ -130,7 +130,7 @@ class CsvEntry public double Value12 { get; set; } } - sealed class EntryMap : CsvClassMap + sealed class EntryMap : ClassMap { public EntryMap() { diff --git a/Appy.GitDb.Benchmark/Properties/AssemblyInfo.cs b/Appy.GitDb.Benchmark/Properties/AssemblyInfo.cs index f31a54a..29819ba 100644 --- a/Appy.GitDb.Benchmark/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Benchmark/Properties/AssemblyInfo.cs @@ -1,35 +1,5 @@ using System.Reflection; using System.Runtime.InteropServices; -// 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.Benchmark")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Benchmark")] -[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("314ee50d-f6e4-4cb6-ab00-22a7258cf8f5")] - -// 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")] diff --git a/Appy.GitDb.Benchmark/packages.config b/Appy.GitDb.Benchmark/packages.config deleted file mode 100644 index 5a80cbd..0000000 --- a/Appy.GitDb.Benchmark/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.Core/Appy.GitDb.Core.csproj b/Appy.GitDb.Core/Appy.GitDb.Core.csproj index 441b4a0..c3744aa 100644 --- a/Appy.GitDb.Core/Appy.GitDb.Core.csproj +++ b/Appy.GitDb.Core/Appy.GitDb.Core.csproj @@ -1,65 +1,15 @@ - - - + + - Debug - AnyCPU - {72AE8930-98EF-446D-8167-7DD251BCF4E6} - Library - Properties - Appy.GitDb.Core - Appy.GitDb.Core - v4.6.1 - 512 + net461;netstandard2.0 + 1591 + AnyCpu - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - - - - - - - - - - - - - - - - + + + - + - - + \ No newline at end of file diff --git a/Appy.GitDb.Core/Properties/AssemblyInfo.cs b/Appy.GitDb.Core/Properties/AssemblyInfo.cs index ea1f255..4e21322 100644 --- a/Appy.GitDb.Core/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Core/Properties/AssemblyInfo.cs @@ -1,18 +1,5 @@ using System.Reflection; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("Appy.GitDb.Core")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Core")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - [assembly: ComVisible(false)] - [assembly: Guid("72ae8930-98ef-446d-8167-7dd251bcf4e6")] - -[assembly: AssemblyVersion("0.12.0")] -[assembly: AssemblyFileVersion("0.12.0")] \ No newline at end of file diff --git a/Appy.GitDb.Core/packages.config b/Appy.GitDb.Core/packages.config deleted file mode 100644 index 7ee8c10..0000000 --- a/Appy.GitDb.Core/packages.config +++ /dev/null @@ -1,4 +0,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 4f5ba4d..2757bde 100644 --- a/Appy.GitDb.Local/Appy.GitDb.Local.csproj +++ b/Appy.GitDb.Local/Appy.GitDb.Local.csproj @@ -1,79 +1,23 @@ - - - - + + - Debug - AnyCPU - {37D87FB6-E7D6-4217-B021-38CEA7233E6F} - Library - Properties - Appy.GitDb.Local - Appy.GitDb.Local - v4.6.1 - 512 - - + net461;netstandard2.0 + 1591 + AnyCpu + Library to use a local git repository as a NoSQL database - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\LibGit2Sharp.0.23.1\lib\net40\LibGit2Sharp.dll - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\packages\NLog.4.4.9\lib\net45\NLog.dll - - - - - - - - - - + + + - - {72ae8930-98ef-446d-8167-7dd251bcf4e6} - Appy.GitDb.Core - + + + + + - - + - - - - 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.Local/Appy.GitDb.Local.nuspec b/Appy.GitDb.Local/Appy.GitDb.Local.nuspec deleted file mode 100644 index 2a25fd0..0000000 --- a/Appy.GitDb.Local/Appy.GitDb.Local.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - $id$ - $version$ - GitDb Local - Library to use a local git repository as a NoSQL database - $author$ - https://github.com/YellowLineParking/Appy.GitDb - https://github.com/YellowLineParking/Appy.GitDb/blob/master/LICENSE - https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/logo.png - $copyright$ - git database nosql - - \ No newline at end of file diff --git a/Appy.GitDb.Local/Properties/AssemblyInfo.cs b/Appy.GitDb.Local/Properties/AssemblyInfo.cs index f672d5e..8f03be9 100644 --- a/Appy.GitDb.Local/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Local/Properties/AssemblyInfo.cs @@ -1,17 +1,5 @@ using System.Reflection; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("Appy.GitDb.Local")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AppyParking")] -[assembly: AssemblyProduct("Appy.GitDb.Local")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - [assembly: ComVisible(false)] - [assembly: Guid("37d87fb6-e7d6-4217-b021-38cea7233e6f")] - -[assembly: AssemblyVersion("0.16.1")] diff --git a/Appy.GitDb.Local/packages.config b/Appy.GitDb.Local/packages.config deleted file mode 100644 index 8f88fa2..0000000 --- a/Appy.GitDb.Local/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Appy.GitDb.NetCore.Server.csproj b/Appy.GitDb.NetCore.Server/Appy.GitDb.NetCore.Server.csproj new file mode 100644 index 0000000..6ab7f64 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Appy.GitDb.NetCore.Server.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + + diff --git a/Appy.GitDb.NetCore.Server/Auth/AuthDependencyExtensions.cs b/Appy.GitDb.NetCore.Server/Auth/AuthDependencyExtensions.cs new file mode 100644 index 0000000..f7da36f --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Auth/AuthDependencyExtensions.cs @@ -0,0 +1,29 @@ +using Appy.GitDb.NetCore.Server.GitDb; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Appy.GitDb.NetCore.Server.Auth +{ + public static class AuthDependencyExtensions + { + public static IServiceCollection AddBasicAuthentication(this IServiceCollection services, string schemeName = "BasicAuthentication") + { + var configuration = services.BuildServiceProvider() + .GetService(); + + services + .Configure(settings => configuration.Bind("GitApiAuth", settings)) + .AddSingleton() + .AddAuthentication(config => + { + config.DefaultAuthenticateScheme = schemeName; + config.DefaultChallengeScheme = schemeName; + config.DefaultScheme = schemeName; + }) + .AddScheme(schemeName, null); + + return services; + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Auth/Authentication.cs b/Appy.GitDb.NetCore.Server/Auth/Authentication.cs new file mode 100644 index 0000000..3301c2b --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Auth/Authentication.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Appy.GitDb.NetCore.Server.Auth +{ + public class Authentication + { + readonly IEnumerable _users; + + public Authentication(IEnumerable users) => + _users = users; + + public Task> ValidateUsernameAndPassword(string userName, string password) => + Task.FromResult(_users.FirstOrDefault(u => + string.Equals(u.UserName, userName, StringComparison.CurrentCultureIgnoreCase) && + u.Password == password)?.Claims); + } + + public class User + { + public string UserName { get; set; } + public string Password { get; set; } + public IEnumerable Roles { get; set; } + + public IEnumerable Claims => + Roles.Select(r => new Claim(ClaimTypes.Role, r)) + .Union(new[] { new Claim(ClaimTypes.Name, UserName) }); + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Auth/BasicAuthenticationHandler.cs b/Appy.GitDb.NetCore.Server/Auth/BasicAuthenticationHandler.cs new file mode 100644 index 0000000..a239894 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Auth/BasicAuthenticationHandler.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; + +namespace Appy.GitDb.NetCore.Server.Auth +{ + public class BasicAuthenticationHandler : AuthenticationHandler + { + readonly IUserService _userService; + + public BasicAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + IUserService userService) + : base(options, logger, encoder, clock) + { + _userService = userService; + } + + protected override async Task HandleAuthenticateAsync() + { + if (!Request.Headers.ContainsKey("Authorization")) + return AuthenticateResult.Fail("Missing Authorization Header"); + + User user = null; + try + { + var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); + var credentialBytes = Convert.FromBase64String(authHeader.Parameter); + var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':'); + var userName = credentials[0]; + var password = credentials[1]; + user = await _userService.Authenticate(userName, password); + } + catch + { + return AuthenticateResult.Fail("Invalid Authorization Header"); + } + + if (user == null) + return AuthenticateResult.Fail("Invalid Username or Password"); + + var claims = user.Claims; + var identity = new ClaimsIdentity(claims, Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + + return AuthenticateResult.Success(ticket); + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Auth/UserService.cs b/Appy.GitDb.NetCore.Server/Auth/UserService.cs new file mode 100644 index 0000000..b4768d0 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Auth/UserService.cs @@ -0,0 +1,46 @@ +using Appy.GitDb.NetCore.Server.GitDb; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Appy.GitDb.NetCore.Server.Auth +{ + public interface IUserService + { + Task Authenticate(string username, string password); + } + + public class UserService : IUserService + { + readonly List _users; + + public UserService(IOptions options) => + _users = buildUsers(options.Value); + + static List buildUsers(GitApiAuthSettings settings) => + settings.Users.Select(setting => new User() + { + UserName = setting.UserName, + Password = setting.Password, + Roles = setting.Roles.Select(r => r) + }).ToList(); + + static User createUser(string userName, string password, string[] roles) => + new User { UserName = userName, Password = password, Roles = roles }; + + public Task Authenticate(string userName, string password) + { + var user = _users.FirstOrDefault(u => + string.Equals(u.UserName, userName, StringComparison.CurrentCultureIgnoreCase) && + u.Password == password); + + if (user == null) + return Task.FromResult(default(User)); + + var copyUser = new User { UserName = user.UserName, Roles = user.Roles.Select(x => x) }; + return Task.FromResult(copyUser); + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Compression/CompressionDependencyExtensions.cs b/Appy.GitDb.NetCore.Server/Compression/CompressionDependencyExtensions.cs new file mode 100644 index 0000000..e25d991 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Compression/CompressionDependencyExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.Extensions.DependencyInjection; + +namespace Appy.GitDb.NetCore.Server.Compression +{ + public static class CompressionDependencyExtensions + { + public static IServiceCollection AddGzipCompression(this IServiceCollection services) => + services + .Configure(options => options.Level = System.IO.Compression.CompressionLevel.Fastest) + .AddResponseCompression(options => { options.Providers.Add(); }); // options.EnableForHttps = true; + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitApi.cs b/Appy.GitDb.NetCore.Server/GitApi.cs new file mode 100644 index 0000000..4ee84ef --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitApi.cs @@ -0,0 +1,204 @@ +using Appy.GitDb.Core.Interfaces; +using Appy.GitDb.Core.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Document = Appy.GitDb.Core.Model.Document; + +namespace Appy.GitDb.NetCore.Server +{ + [ApiController] + public class GitApiController : ControllerBase + { + readonly IGitDb _gitDb; + + public GitApiController(IGitDb gitDb) => + _gitDb = gitDb; + + [Route("{branch}/document/{*key}")] + [HttpGet] + [Authorize(Roles = "admin, read")] + 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) => + 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) => + result(() => _gitDb.GetFilesPaged(branch, key, start, pageSize)); + + [Route("{branch}/document")] + [HttpPost] + [Authorize(Roles = "admin,write")] + 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) => + result(() => _gitDb.Delete(branch, request.Key, request.Message, request.Author)); + + [Route("{branch}/transactions/close")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task CloseTransactions(string branch) => + result(() => _gitDb.CloseTransactions(branch)); + + [Route("tag")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task Tag([FromBody] Reference reference) => + result(() => _gitDb.Tag(reference)); + + [Route("branch")] + [HttpGet] + [Authorize(Roles = "admin,read")] + public Task GetBranches() => + result(() => _gitDb.GetAllBranches()); + + [Route("branch")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task CreateBranch([FromBody] Reference reference) => + result(() => _gitDb.CreateBranch(reference)); + + static readonly Dictionary _transactions = new Dictionary(); + + [Route("{branch}/transaction")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task CreateTransaction(string branch) => + result(async () => + { + var trans = await _gitDb.CreateTransaction(branch); + var transactionId = Guid.NewGuid().ToString(); + _transactions.Add(transactionId, trans); + return transactionId; + }); + + [Route("{branch}")] + [HttpDelete] + [Authorize(Roles = "admin,write")] + public Task DeleteBranch(string branch) => + result(() => _gitDb.DeleteBranch(branch)); + + [Route("tag/{tag}")] + [HttpDelete] + [Authorize(Roles = "admin,write")] + public Task DeleteTag(string tag) => + result(() => _gitDb.DeleteTag(tag)); + + [Route("{transactionId}/add")] + [HttpPost] + [Authorize(Roles = "admin,write")] + 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) => + result(() => _transactions[transactionId].AddMany(documents)); + + [Route("{transactionId}/delete/{key}")] + [HttpPost] + [Authorize(Roles = "admin,write")] + 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) => + result(() => _transactions[transactionId].DeleteMany(keys)); + + [Route("{transactionId}/commit")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task CommitTransaction(string transactionId, [FromBody] CommitTransaction commit) => + result(async () => + { + var transaction = _transactions[transactionId]; + var sha = await transaction.Commit(commit.Message, commit.Author); + _transactions.Remove(transactionId); + return sha; + }); + + [Route("{transactionId}/abort")] + [HttpPost] + [Authorize(Roles = "admin,write")] + public Task AbortTransaction(string transactionId) => + result(async () => + { + var transaction = _transactions[transactionId]; + await transaction.Abort(); + _transactions.Remove(transactionId); + }); + + [Route("merge")] + [HttpPost] + [Authorize(Roles = "admin,write")] + 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) => + 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) => + result(() => _gitDb.Diff(reference, reference2)); + + [Route("log/{reference}/{reference2}")] + [HttpGet] + [Authorize(Roles = "admin,read")] + public Task Log(string reference, string reference2) => + result(() => _gitDb.Log(reference, reference2)); + + async Task result(Func> action) + { + try + { + return Ok(await action()); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (NotSupportedException ex) + { + return BadRequest(ex.Message); + } + } + + async Task result(Func action) + { + try + { + await action(); + return Ok(); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (NotSupportedException ex) + { + return BadRequest(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitDb/GitApiAuthSettings.cs b/Appy.GitDb.NetCore.Server/GitDb/GitApiAuthSettings.cs new file mode 100644 index 0000000..e6fa357 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitDb/GitApiAuthSettings.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Appy.GitDb.NetCore.Server.GitDb +{ + public class GitApiAuthSettings + { + public List Users { get; set; } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitDb/GitApiUserSetting.cs b/Appy.GitDb.NetCore.Server/GitDb/GitApiUserSetting.cs new file mode 100644 index 0000000..b286ddd --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitDb/GitApiUserSetting.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Appy.GitDb.NetCore.Server.GitDb +{ + public class GitApiUserSetting + { + public string UserName { get; set; } + public string Password { get; set; } + public List Roles { get; set; } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitDb/GitDbDependencyExtensions.cs b/Appy.GitDb.NetCore.Server/GitDb/GitDbDependencyExtensions.cs new file mode 100644 index 0000000..8ef0e99 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitDb/GitDbDependencyExtensions.cs @@ -0,0 +1,31 @@ +using Appy.GitDb.Core.Interfaces; +using Appy.GitDb.Local; +using Autofac; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Appy.GitDb.NetCore.Server.GitDb +{ + public static class GitDbDependencyExtensions + { + public static IServiceCollection AddGitDb(this ContainerBuilder builder, IServiceCollection services) + { + var configuration = services.BuildServiceProvider() + .GetService(); + + var gitDbSettings = new GitDbSettings(); + configuration.Bind("GitDb", gitDbSettings); + + services.Configure(settings => configuration.Bind("GitDb", settings)); + + var repo = new LocalGitDb( + gitDbSettings.GitHomePath, + gitDbSettings.Remote.Url, gitDbSettings.Remote.UserName, gitDbSettings.Remote.Email, gitDbSettings.Remote.Password, + gitDbSettings.TransactionsTimeout); + + builder.RegisterInstance(repo).As().ExternallyOwned(); + + return services; + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitDb/GitDbRemoteSettings.cs b/Appy.GitDb.NetCore.Server/GitDb/GitDbRemoteSettings.cs new file mode 100644 index 0000000..9bef53e --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitDb/GitDbRemoteSettings.cs @@ -0,0 +1,10 @@ +namespace Appy.GitDb.NetCore.Server.GitDb +{ + public class GitDbRemoteSettings + { + public string Url { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitDb/GitDbSettings.cs b/Appy.GitDb.NetCore.Server/GitDb/GitDbSettings.cs new file mode 100644 index 0000000..3515c85 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitDb/GitDbSettings.cs @@ -0,0 +1,11 @@ +namespace Appy.GitDb.NetCore.Server.GitDb +{ + public class GitDbSettings + { + public string ServerUrl { get; set; } + public int TransactionsTimeout { get; set; } + public string GitPath { get; set; } + public string GitHomePath { get; set; } + public GitDbRemoteSettings Remote { get; set; } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/GitProtocol.cs b/Appy.GitDb.NetCore.Server/GitProtocol.cs new file mode 100644 index 0000000..a810c16 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/GitProtocol.cs @@ -0,0 +1,140 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Protocols; +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +namespace Appy.GitDb.Server +{ + //[Route("server")] + //[ApiController] + //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"]; + + // [HttpGet] + // [Route("repository.git")] + // [Authorize(Roles = "admin, read")] + // public IActionResult GitUrl() => + // Ok(); + + // [HttpGet] + // [Route("repository.git/info/refs")] + // [Authorize(Roles = "admin, read")] + // public IActionResult InfoRefs(string service) => + // new GitResult($"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")] + // [Authorize(Roles = "admin, read")] + // public IActionResult UploadPack() => + // new GitResult("application/x-git-upload-pack-result", + // (input, outStream) => executeServiceByName(input, outStream, "upload-pack", false, true)); + + // [HttpPost, Route("repository.git/git-receive-pack")] + // [Authorize(Roles = "admin, write")] + // public IActionResult ReceivePack() => + // new GitResult("application/x-git-receive-pack-result", + // (input, outStream) => executeServiceByName(input, outStream, "receive-pack", false, false)); + + // 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"; + // if (addAdvertiseRefs) + // args += " --advertise-refs"; + // args += " \"" + _repoPath + "\""; + + // var info = new ProcessStartInfo(_gitPath + @"\git.exe", args) + // { + // CreateNoWindow = true, + // RedirectStandardError = true, + // RedirectStandardInput = true, + // RedirectStandardOutput = true, + // UseShellExecute = false, + // WorkingDirectory = Path.GetDirectoryName(_repoPath), + // }; + + // if (info.EnvironmentVariables.ContainsKey("HOME")) + // 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(); + // } + // } + //} + + // TODO (https://techblog.dorogin.com/server-sent-event-aspnet-core-a42dc9b9ffa9) + //public class GitResult : IActionResult + //{ + // readonly string _contentType; + // readonly string _advertiseRefsContent; + // readonly Func _executeGitCommand; + + // public GitResult(string contentType, Func executeGitCommand, string advertiseRefsContent = null) + // { + // _contentType = contentType; + // _advertiseRefsContent = advertiseRefsContent; + // _executeGitCommand = executeGitCommand; + // } + + // public Task ExecuteResultAsync(ActionContext context) + // { + // var input = context.HttpContext.Request.Body; + // context.HttpContext.Response.GetTypedHeaders().ContentType = new MediaTypeHeaderValue(_contentType); + + // 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 Task.CompletedTask; + // } + + // //public async Task ExecuteResultAsync(ActionContext context) + // //{ + // // var objectResult = new ObjectResult(_result.Exception ?? _result.Data) + // // { + // // StatusCode = _result.Exception != null + // // ? StatusCodes.Status500InternalServerError + // // : StatusCodes.Status200OK + // // }; + + // // await objectResult.ExecuteResultAsync(context); + // //} + //} +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Logging/AppyLoggingMiddleware.cs b/Appy.GitDb.NetCore.Server/Logging/AppyLoggingMiddleware.cs new file mode 100644 index 0000000..9b7fd46 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/AppyLoggingMiddleware.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using NLog; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + public class AppyLoggingMiddleware + { + public static Logger Logger; + readonly RequestDelegate _next; + + public AppyLoggingMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + var correlationid = context.Request.Headers.TryGetValue("X-FORWARD-CORRELATIONID", out var correlationHeader) + ? correlationHeader.ToString() + : Guid.NewGuid().ToString("N"); + + LogContext.SetCorrelationId(correlationid); + + context.Response.OnStarting(state => + { + var ctx = state as HttpContext; + if (ctx == null) + return Task.FromResult(0); + + if (!ctx.Response.Headers.ContainsKey("correlationid")) + ctx.Response.Headers.Add("correlationid", new[] { correlationid }); + + return Task.FromResult(0); + }, context); + + var watch = new Stopwatch(); + watch.Start(); + + await _next.Invoke(context); + + watch.Stop(); + + var identity = context.User?.Identity; + var userName = identity?.Name; + var user = !string.IsNullOrEmpty(userName) + ? userName + : "(anonymous)"; + + var exception = context.Items.ContainsKey("exception") + ? (Exception)context.Items["exception"] + : null; + + 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 requestUri = mapToUri(context.Request); + + Logger.Log(new LogEventInfo + { + Level = level, + Exception = exception, + LoggerName = "http-log", + Properties = + { + { "user", user}, + { "method", context.Request.Method}, + { "url", requestUri.AbsolutePath}, + { "querystring", requestUri.Query}, + { "request-headers", toLogString(context.Request.Headers)}, + { "response-headers", toLogString(context.Response.Headers) }, + { "statuscode", context.Response.StatusCode }, + { "reason", context.Response.HttpContext.Features.Get().ReasonPhrase }, + { "duration", watch.Elapsed.TotalMilliseconds }, + { "useragent", getValue(context.Request.Headers, "User-Agent") }, + { "host", requestUri.Host } + } + }); + } + + static Uri mapToUri(HttpRequest request) + { + var builder = new UriBuilder(); + builder.Scheme = request.Scheme; + builder.Host = request.Host.Host; + if (request.Host.Port.HasValue) + builder.Port = request.Host.Port.Value; + builder.Path = request.Path; + builder.Query = request.QueryString.ToUriComponent(); + + return builder.Uri; + } + + 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.NetCore.Server/Logging/CorrelationIdRenderer.cs b/Appy.GitDb.NetCore.Server/Logging/CorrelationIdRenderer.cs new file mode 100644 index 0000000..b398364 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/CorrelationIdRenderer.cs @@ -0,0 +1,16 @@ +using System.Text; +using NLog; +using NLog.LayoutRenderers; +using NLog.Web.LayoutRenderers; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + [LayoutRenderer("correlationid")] + public class CorrelationIdLayoutRenderer : AspNetLayoutRendererBase + { + protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) + { + builder.Append(LogContext.GetCorrelationId()); + } + } +} diff --git a/Appy.GitDb.NetCore.Server/Logging/ExceptionLayoutRenderer.cs b/Appy.GitDb.NetCore.Server/Logging/ExceptionLayoutRenderer.cs new file mode 100644 index 0000000..767318f --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/ExceptionLayoutRenderer.cs @@ -0,0 +1,101 @@ +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using NLog; +using NLog.Config; +using NLog.LayoutRenderers; +using NLog.Web.LayoutRenderers; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + [LayoutRenderer("appy-exception")] + class ExceptionLayoutRenderer : AspNetLayoutRendererBase + { + [RequiredParameter] + public string Field { get; set; } + + protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) + { + if (logEvent.Exception == null) + return; + + builder.Append(typeof(Signature).GetProperty(Field)?.GetValue(logEvent.Exception.GetSignature(), null)); + } + } + + class Signature + { + public Signature(Exception exception) + { + ErrorType = exception.GetType().FullName; + ClassName = exception.TargetSite == null ? null : exception.TargetSite.DeclaringType?.FullName; + MethodName = exception.TargetSite == null ? null : exception.TargetSite.Name; + AssemblyName = exception.TargetSite == null ? null : exception.TargetSite.DeclaringType?.Assembly.GetName().Name; + StackTrace = exception.StackTrace; + Message = exception.Message; + + // signatures + StackTraceSignature = exception.GetCleanStackTrace(1).GetSignature(); + MessageSignature = exception.GetCleanMessage().GetSignature(); + MethodSignature = (ClassName + "." + MethodName).GetSignature(); + ExceptionSignature = $"{MethodSignature.Substring(0, 7)}_{MessageSignature.Substring(0, 7)}_{StackTraceSignature}"; + } + public string ErrorType { get; } + public string ClassName { get; } + public string MethodName { get; } + public string AssemblyName { get; } + public string Message { get; } + public string StackTrace { get; } + public string MethodSignature { get; } + public string MessageSignature { get; } + public string StackTraceSignature { get; } + public string ExceptionSignature { get; } + } + + static class ExceptionExtensions + { + internal static string GetSignature(this string inputString) + { + var sb = new StringBuilder(); + foreach (var b in MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(inputString))) + sb.Append(b.ToString("X2")); + + return sb.ToString(); + } + + internal static string GetCleanStackTrace(this Exception ex, int folderDepth) + { + if (string.IsNullOrEmpty(ex.StackTrace)) + return string.Empty; + + var stackTrace = ex.StackTrace; + + var matches = new Regex(@"in ([^ ]*\\.*\.cs)[ :]").Matches(stackTrace); + + foreach (Match match in matches) + { + var path = match.Groups[1].Value; + + // trim the number of folders + var pathParts = path.Split('\\').ToList(); + pathParts.Reverse(); + pathParts = pathParts.Take(folderDepth + 1).ToList(); // add 1 to get the file name + pathParts.Reverse(); + + var newPath = string.Join("\\", pathParts); + + stackTrace = stackTrace.Replace(path, newPath); + } + + + return stackTrace; + } + internal static string GetCleanMessage(this Exception ex) => + Regex.Replace(ex.Message, @"\[[^\]]*\]|\{[^\}]*\}", "[...]", RegexOptions.Multiline | RegexOptions.IgnoreCase); + + internal static Signature GetSignature(this Exception exception) => + new Signature(exception); + } +} diff --git a/Appy.GitDb.NetCore.Server/Logging/HttpGlobalExceptionFilter.cs b/Appy.GitDb.NetCore.Server/Logging/HttpGlobalExceptionFilter.cs new file mode 100644 index 0000000..6b90d4b --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/HttpGlobalExceptionFilter.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + public partial class HttpGlobalExceptionFilter : IExceptionFilter + { + readonly IHostingEnvironment _env; + readonly ILogger _logger; + + public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger logger) + { + _env = env; + _logger = logger; + } + + public void OnException(ExceptionContext context) + { + context.HttpContext.Items["exception"] = context.Exception; + // context.ExceptionHandled = true; + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Logging/LogContext.cs b/Appy.GitDb.NetCore.Server/Logging/LogContext.cs new file mode 100644 index 0000000..5994f30 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/LogContext.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using NLog; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + /// + /// Provides a way to set contextual data that flows with the call and + /// async context of a test or invocation. + /// + public static class NetStandardCallContext + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item in the call context. + /// The object to store in the call context. + public static void SetData(string name, object data) => + state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; + + /// + /// Retrieves an object with the specified name from the . + /// + /// The name of the item in the call context. + /// The object in the call context associated with the specified name, or if not found. + public static object GetData(string name) => + state.TryGetValue(name, out AsyncLocal data) ? data.Value : null; + } + + public static class NetStandardCallContext + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item in the call context. + /// The object to store in the call context. + public static void SetData(string name, T data) => + state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; + + /// + /// Retrieves an object with the specified name from the . + /// + /// The type of the data being retrieved. Must match the type used when the was set via . + /// The name of the item in the call context. + /// The object in the call context associated with the specified name, or a default value for if none is found. + public static T GetData(string name) => + state.TryGetValue(name, out AsyncLocal data) ? data.Value : default(T); + } + + public class LogContext : IDisposable + { + const string CorrelationIdKey = "lctx:correlationid"; + + public string CorrelationId { get; } + + public LogContext(string correlationId = null) + { + CorrelationId = correlationId ?? Guid.NewGuid().ToString("N"); + SetCorrelationId(CorrelationId); + } + + public void Dispose() => + Clear(); + + public static void SetCorrelationId(string correlationId) + { + NetStandardCallContext.SetData(CorrelationIdKey, correlationId); + MappedDiagnosticsLogicalContext.Set(CorrelationIdKey, correlationId); + } + + public static void Clear() + { + NetStandardCallContext.SetData(CorrelationIdKey, null); + MappedDiagnosticsLogicalContext.Clear(); + } + + public static string GetCorrelationId() => get(CorrelationIdKey); + + static string get(string key) => + NetStandardCallContext.GetData(key) as string ?? MappedDiagnosticsLogicalContext.Get(key); + } +} diff --git a/Appy.GitDb.NetCore.Server/Logging/LoggingApplicationExtensions.cs b/Appy.GitDb.NetCore.Server/Logging/LoggingApplicationExtensions.cs new file mode 100644 index 0000000..93b0837 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Logging/LoggingApplicationExtensions.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Builder; + +namespace Appy.GitDb.NetCore.Server.Logging +{ + public static class LoggingApplicationExtensions + { + public static IApplicationBuilder UseAppyLogging(this IApplicationBuilder app) => + app.UseMiddleware(); + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Mvc/MvcDependencyExtensions.cs b/Appy.GitDb.NetCore.Server/Mvc/MvcDependencyExtensions.cs new file mode 100644 index 0000000..1163bda --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Mvc/MvcDependencyExtensions.cs @@ -0,0 +1,18 @@ +using Appy.GitDb.NetCore.Server.Logging; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; + +namespace Appy.GitDb.NetCore.Server.Mvc +{ + public static class MvcDependencyExtensions + { + public static IServiceCollection AddCustomMvc(this IServiceCollection services) + { + services + .AddMvc(o => o.Filters.Add()) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + + return services; + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/NLog.config b/Appy.GitDb.NetCore.Server/NLog.config new file mode 100644 index 0000000..f0312ea --- /dev/null +++ b/Appy.GitDb.NetCore.Server/NLog.config @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/NLog.xsd b/Appy.GitDb.NetCore.Server/NLog.xsd new file mode 100644 index 0000000..c9cbd05 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/NLog.xsd @@ -0,0 +1,3009 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged.. + + + + + Throw an exception when there is an internal error. Default value is: false. + + + + + Throw an exception when there is a configuration error. If not set, determined by throwExceptions. + + + + + Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. + + + + + Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. + + + + + Write timestamps for internal NLog messages. Default value is: true. + + + + + Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable or disable logging rule. Disabled rules are ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Delay the flush until the LogEvent has been confirmed as written + + + + + Condition expression. Log events who meet this condition will cause a flush on the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Indicates whether to use sliding timeout. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Indicates whether to include dictionary contents. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Viewer parameter name. + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to use default row highlighting rules. + + + + + Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Name of the database provider. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Database parameter name. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Database parameter size. + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Optional entrytype. When not set, or when not convertable to then determined by + + + + + Message length limit to write to the Event Log. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Maximum number of archive files that should be kept. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + + + + + Is the an absolute or relative path? + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write + + + + + Is the an absolute or relative path? + + + + + Value indicationg whether file creation calls should be synchronized by a system global mutex. + + + + + Indicates whether the footer should be written only when the file is archived. + + + + + Name of the file to write to. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to archive old log file on startup. + + + + + Indicates whether to create directories if they do not exist. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + File attributes (Windows only). + + + + + Indicates whether to delete old log file on startup. + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + Maximum number of log filenames that should be stored as existing. + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Log file buffer size in bytes. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Interval in which messages will be written up to the number of messages. + + + + + Maximum allowed number of messages written per . + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + Type of the parameter. Obsolete alias for + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Encoding to be used for sending e-mail. + + + + + Indicates whether to add new lines between log entries. + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Indicates the SMTP client timeout. + + + + + Priority used for sending mails. + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used when writing text to the queue. + + + + + Indicates whether to use the XML format when serializing message. This will also disable creating queues. + + + + + Indicates whether to check if a queue exists before writing to it. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Indicates whether to create the queue if it doesn't exists. + + + + + Label to associate with each message. + + + + + Name of the queue to write to. + + + + + Indicates whether to use recoverable messages (with guaranteed delivery). + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Maximum current connections. 0 = no maximum. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Network address. + + + + + Maximum queue size. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Indicates whether to include dictionary contents. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Encoding. + + + + + Value whether escaping be done according to the old NLog style (Very non-standard) + + + + + Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Protocol to be used when calling web service. + + + + + Web service URL. + + + + + Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). + + + + + (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + + + + + + + + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log events + + + + + Option to render the empty object value {} + + + + + Option to suppress the extra spaces in the output json + + + + + + + + + + + + + + + Determines wether or not this attribute will be Json encoded. + + + + + Indicates whether to escape non-ascii characters + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + Option to include all properties from the log events + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Program.cs b/Appy.GitDb.NetCore.Server/Program.cs new file mode 100644 index 0000000..6e47831 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Program.cs @@ -0,0 +1,47 @@ +using System; +using Appy.GitDb.NetCore.Server.Logging; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using NLog; +using NLog.LayoutRenderers; +using NLog.Web; +using ExceptionLayoutRenderer = Appy.GitDb.NetCore.Server.Logging.ExceptionLayoutRenderer; + +namespace Appy.GitDb.NetCore.Server +{ + public class Program + { + public static void Main(string[] args) + { + LayoutRenderer.Register("appy-exception"); + LayoutRenderer.Register("correlationid"); + var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); + try + { + logger.Debug("Starting up git server"); + CreateWebHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + logger.Fatal(ex, "Stopped program because of exception"); + throw; + } + finally + { + NLog.LogManager.Shutdown(); + } + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + }) + .UseNLog(); + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Properties/launchSettings.json b/Appy.GitDb.NetCore.Server/Properties/launchSettings.json new file mode 100644 index 0000000..62eef19 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55639", + "sslPort": 44344 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Appy.GitDb.NetCore.Server": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/Startup.cs b/Appy.GitDb.NetCore.Server/Startup.cs new file mode 100644 index 0000000..62ab93c --- /dev/null +++ b/Appy.GitDb.NetCore.Server/Startup.cs @@ -0,0 +1,57 @@ +using Appy.GitDb.NetCore.Server.Auth; +using Appy.GitDb.NetCore.Server.Compression; +using Appy.GitDb.NetCore.Server.GitDb; +using Appy.GitDb.NetCore.Server.Logging; +using Appy.GitDb.NetCore.Server.Mvc; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using NLog; +using System; + +namespace Appy.GitDb.NetCore.Server +{ + public class Startup + { + public IConfiguration Configuration { get; } + public IContainer Container { get; private set; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + AppyLoggingMiddleware.Logger = LogManager.GetLogger("server-log"); + } + + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services + .AddOptions() + .AddGzipCompression() + .AddBasicAuthentication() + .AddCustomMvc(); + + var builder = new ContainerBuilder(); + builder.Populate(services); + builder.AddGitDb(services); + + Container = builder.Build(); + return new AutofacServiceProvider(Container); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment() || env.EnvironmentName == "local") + { + app.UseDeveloperExceptionPage(); + } + + app.UseResponseCompression() + .UseAppyLogging() + .UseAuthentication() + .UseMvc(); + } + } +} \ No newline at end of file diff --git a/Appy.GitDb.NetCore.Server/appsettings.Development.json b/Appy.GitDb.NetCore.Server/appsettings.Development.json new file mode 100644 index 0000000..2b3377a --- /dev/null +++ b/Appy.GitDb.NetCore.Server/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/Appy.GitDb.NetCore.Server/appsettings.json b/Appy.GitDb.NetCore.Server/appsettings.json new file mode 100644 index 0000000..0fc9a8a --- /dev/null +++ b/Appy.GitDb.NetCore.Server/appsettings.json @@ -0,0 +1,39 @@ +{ + "GitDb": { + "ServerUrl": "http://+:9500", + "Remote": { + "Url": "", + "UserName": "", + "Email": "", + "Password": "" + }, + "TransactionsTimeout": 10, + "GitPath": "", + "GitHomePath": "C:\\test" + }, + "GitApiAuth": { + "Users": [ + { + "UserName": "GitAdmin", + "Password": "", + "Roles": [ "admin", "read", "write" ] + }, + { + "UserName": "GitReader", + "Password": "", + "Roles": [ "read" ] + }, + { + "UserName": "GitWriter", + "Password": "", + "Roles": [ "write" ] + } + ] + }, + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Appy.GitDb.NetCore.Server/appsettings.production.json b/Appy.GitDb.NetCore.Server/appsettings.production.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/Appy.GitDb.NetCore.Server/appsettings.production.json @@ -0,0 +1,2 @@ +{ +} diff --git a/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj b/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj index 04e4555..44ea401 100644 --- a/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj +++ b/Appy.GitDb.Remote/Appy.GitDb.Remote.csproj @@ -1,66 +1,25 @@ - - - + + - Debug - AnyCPU - {3F7A7050-68CB-4D7F-9149-A59095EB78A1} - Library - Properties - Appy.GitDb.Remote - Appy.GitDb.Remote - v4.6.1 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net461;netstandard2.0 + 1591 + AnyCpu + Library to use a remote git repository as a NoSQL database through a REST API. Note: You need an installed version of the GitDb server, which can be found at the project URL. + + + - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - + + + + + - - - - - - - - {72ae8930-98ef-446d-8167-7dd251bcf4e6} - Appy.GitDb.Core - - - - - + - - + \ No newline at end of file diff --git a/Appy.GitDb.Remote/Appy.GitDb.Remote.nuspec b/Appy.GitDb.Remote/Appy.GitDb.Remote.nuspec deleted file mode 100644 index 8589d79..0000000 --- a/Appy.GitDb.Remote/Appy.GitDb.Remote.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - $id$ - $version$ - GitDb Remote - Library to use a remote git repository as a NoSQL database through a REST API. Note: You need an installed version of the GitDb server, which can be found at the project URL. - $author$ - https://github.com/YellowLineParking/Appy.GitDb - https://github.com/YellowLineParking/Appy.GitDb/blob/master/LICENSE - https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/logo.png - $copyright$ - git database nosql - - \ No newline at end of file diff --git a/Appy.GitDb.Remote/Extensions.cs b/Appy.GitDb.Remote/Extensions.cs index b98fa04..009727c 100644 --- a/Appy.GitDb.Remote/Extensions.cs +++ b/Appy.GitDb.Remote/Extensions.cs @@ -18,10 +18,10 @@ public static IEnumerable> Batch(this IEnumerable items, in public static async Task GetAsync(this HttpClient client, string url) where T : class { - var response = await client.GetAsync(url).WhenSuccessful(); - var result = await response.Content.ReadAsStringAsync(); - return JsonConvert.DeserializeObject(result); - } + var response = await client.GetAsync(url).WhenSuccessful(); + var result = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(result); + } public static Task PostAsync(this HttpClient client, string url, T item) => client.PostAsync(url, new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json")); @@ -37,7 +37,7 @@ public static async Task WhenSuccessful(this Task + static string urlEncode(string value) => HttpUtility.UrlEncode(value); - public Task Get(string branch, string key) => - _client.GetAsync($"/{branch}/document/{urlEncode(key)}"); + public Task Get(string branch, string key) => + _client.GetAsync($"/{branch}/document/{urlEncode(key)}"); public async Task Get(string branch, string key) where T : class { diff --git a/Appy.GitDb.Remote/packages.config b/Appy.GitDb.Remote/packages.config deleted file mode 100644 index 7ee8c10..0000000 --- a/Appy.GitDb.Remote/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Appy.GitDb.Server/App.config b/Appy.GitDb.Server/App.config index 2c66b99..c996e04 100644 --- a/Appy.GitDb.Server/App.config +++ b/Appy.GitDb.Server/App.config @@ -2,7 +2,7 @@ - + diff --git a/Appy.GitDb.Server/Appy.GitDb.Server.csproj b/Appy.GitDb.Server/Appy.GitDb.Server.csproj index 132fa7d..5ab4604 100644 --- a/Appy.GitDb.Server/Appy.GitDb.Server.csproj +++ b/Appy.GitDb.Server/Appy.GitDb.Server.csproj @@ -1,130 +1,49 @@ - - - + + - Debug - AnyCPU - {A9FAB943-9F28-49F8-82FB-0380A22FD683} Exe - Properties - Appy.GitDb.Server - Appy.GitDb.Server - v4.6.1 - 512 + net461 true - + + - 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.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\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.4.0.0\lib\net35\YamlDotNet.dll - True - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + - Always Designer @@ -132,24 +51,11 @@ 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/Program.cs b/Appy.GitDb.Server/Program.cs index 57fc1f4..2eb04ac 100644 --- a/Appy.GitDb.Server/Program.cs +++ b/Appy.GitDb.Server/Program.cs @@ -27,7 +27,7 @@ public static void Main(string[] args) var app = App.Create(url, new LocalGitDb(gitRepoPath, remoteUrl, userName, userEmail, password, 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 = "GitReader",Password = ConfigurationManager.AppSettings["GitReader"],Roles = new [] { "read" }}, new User{ UserName = "GitWriter", Password = ConfigurationManager.AppSettings["GitWriter"] ,Roles = new [] { "write" }} }); using (app.Start()) diff --git a/Appy.GitDb.Server/Properties/AssemblyInfo.cs b/Appy.GitDb.Server/Properties/AssemblyInfo.cs index b9c061e..3392c08 100644 --- a/Appy.GitDb.Server/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Server/Properties/AssemblyInfo.cs @@ -1,17 +1,5 @@ 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/packages.config b/Appy.GitDb.Server/packages.config deleted file mode 100644 index 5243ea8..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 fb41315..b058c81 100644 --- a/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj +++ b/Appy.GitDb.Tests/Appy.GitDb.Tests.csproj @@ -1,185 +1,79 @@ - - - - + + - Debug - AnyCPU - {1C2D86C4-8CAD-457E-B4F1-86A5523B3112} - Library - Properties - Appy.GitDb.Tests - Appy.GitDb.Tests - v4.6.1 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net461;netcoreapp2.2 + 1591 + Full + + + - - ..\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.23.1\lib\net40\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.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\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 - + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - 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 - + + + + + + + + + + + + + - - - - - 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/Diff.cs b/Appy.GitDb.Tests/Diff.cs index c429025..7438325 100644 --- a/Appy.GitDb.Tests/Diff.cs +++ b/Appy.GitDb.Tests/Diff.cs @@ -64,9 +64,9 @@ public async Task ShowsDifferenceBetweenTags() => void testDiff(Diff diff) { - diff.Added.Select(a => a.Key).Should().BeEquivalentTo(_additions.Select(a => a.Key)); - diff.Modified.Select(a => a.Key).Should().BeEquivalentTo(_modifications.Select(a => a.Key)); - diff.Deleted.Select(a => a.Key).Should().BeEquivalentTo(_deletions); + diff.Added.Select(a => a.Key).ToList().Should().BeEquivalentTo(_additions.Select(a => a.Key).ToList()); + diff.Modified.Select(a => a.Key).ToList().Should().BeEquivalentTo(_modifications.Select(a => a.Key).ToList()); + diff.Deleted.Select(a => a.Key).ToList().Should().BeEquivalentTo(_deletions); } } @@ -100,9 +100,9 @@ public async Task ShowsNoDifferences() => void testDiff(Diff diff) { - diff.Added.Should().BeEmpty(); - diff.Modified.Should().BeEmpty(); - diff.Deleted.Should().BeEmpty(); + diff.Added.ToList().Should().BeEmpty(); + diff.Modified.ToList().Should().BeEmpty(); + diff.Deleted.ToList().Should().BeEmpty(); } } diff --git a/Appy.GitDb.Tests/GettingAList.cs b/Appy.GitDb.Tests/GettingAList.cs index 2bb853d..c0c2ee8 100644 --- a/Appy.GitDb.Tests/GettingAList.cs +++ b/Appy.GitDb.Tests/GettingAList.cs @@ -12,9 +12,9 @@ namespace Appy.GitDb.Tests public class GettingAListOfFiles : WithRepo { readonly Dictionary _rootDocuments = Enumerable.Range(0, 3) - .ToDictionary(i => $@"{Directory}\{i}_key", i => i + "_value"); + .ToDictionary(i => $@"{Directory}/{i}_key", i => i + "_value"); readonly Dictionary _subDirectoryDocuments = Enumerable.Range(0, 3) - .ToDictionary(i => $@"{Directory}\subdirectory\{i}_key", i => i + "sub_value"); + .ToDictionary(i => $@"{Directory}/subdirectory/{i}_key", i => i + "sub_value"); IReadOnlyCollection _result; const string Directory = "directory"; protected override async Task Because() @@ -36,9 +36,9 @@ public void DoesNotRetrieveFilesRecursively() => public class GettingAListOfTypedFiles : WithRepo { readonly Dictionary _rootDocuments = Enumerable.Range(0, 3) - .ToDictionary(i => $@"{Directory}\{i}_key", i => new TestClass(i + "_value")); + .ToDictionary(i => $"{Directory}/{i}_key", i => new TestClass(i + "_value")); readonly Dictionary _subDirectoryDocuments = Enumerable.Range(0, 3) - .ToDictionary(i => $@"{Directory}\subdirectory\{i}_key", i => new TestClass(i + "sub_value")); + .ToDictionary(i => $"{Directory}/subdirectory/{i}_key", i => new TestClass(i + "sub_value")); IReadOnlyCollection _result; const string Directory = "subdirectory"; protected override async Task Because() @@ -56,14 +56,20 @@ public void RetrievesAllFilesInTheList() => [Fact] public void DoesNotRetrieveFilesRecursively() => _result.Select(r => r.Value).Should().NotContain(_subDirectoryDocuments.Values.Select(d => d.Value)); + + [Fact] + public void CloneRepoToNormal() + { + MoveToNormalRepo(@"C:\testrepo"); + } } public class GettingAListOfPagedFiles : WithRepo { readonly Dictionary _rootDocuments = Enumerable.Range(0, 50) - .ToDictionary(i => $@"{Directory}\{i}_key", i => i + "_value"); + .ToDictionary(i => $@"{Directory}/{i}_key", i => i + "_value"); readonly Dictionary _subDirectoryDocuments = Enumerable.Range(0, 5) - .ToDictionary(i => $@"{Directory}\subdirectory\{i}_key", i => i + "sub_value"); + .ToDictionary(i => $@"{Directory}/subdirectory/{i}_key", i => i + "sub_value"); List _result; const string Directory = "directory"; protected override async Task Because() diff --git a/Appy.GitDb.Tests/GettingAValue.cs b/Appy.GitDb.Tests/GettingAValue.cs index 68534f5..c1e3555 100644 --- a/Appy.GitDb.Tests/GettingAValue.cs +++ b/Appy.GitDb.Tests/GettingAValue.cs @@ -59,7 +59,7 @@ public void ReturnsNull() => public class GettingATypedValueForAKeyWithASlash : WithRepo { - const string Key = "key\\test.json"; + const string Key = "key/test.json"; readonly TestClass _value = new TestClass("value"); TestClass _result; protected override async Task Because() diff --git a/Appy.GitDb.Tests/Merging.cs b/Appy.GitDb.Tests/Merging.cs index aa9ec70..51b166c 100644 --- a/Appy.GitDb.Tests/Merging.cs +++ b/Appy.GitDb.Tests/Merging.cs @@ -58,7 +58,7 @@ async Task addItems(string branch) { foreach (var i in Enumerable.Range(_index, 5)) { - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = i.ToString() }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = i.ToString() }, Author); _removedFiles.Remove(i); _addedFiles.Add(i); } @@ -69,7 +69,7 @@ async Task removeItems(string branch, int start, int count) { foreach (var i in Enumerable.Range(start, count)) { - await Subject.Delete(branch, $"file\\key {i}", $"Deleted {i}({branch})", Author); + await Subject.Delete(branch, $"file/key {i}", $"Deleted {i}({branch})", Author); _addedFiles.Remove(i); _removedFiles.Add(i); } @@ -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() => @@ -115,7 +115,7 @@ public class MergingWhenThereAreNoChanges : WithRepo protected override async Task Because() { foreach (var i in Enumerable.Range(0, 5)) - await Subject.Save("master", $"Added {i} (master)", new Document { Key = $"file\\key {i}", Value = i.ToString() }, Author); + await Subject.Save("master", $"Added {i} (master)", new Document { Key = $"file/key {i}", Value = i.ToString() }, Author); _masterCommit = Repo.Branches["master"].Tip.Sha; @@ -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() => @@ -155,7 +155,7 @@ protected override async Task Because() async Task addItem(string branch, bool changeValuesForKeys = false) { foreach (var i in Enumerable.Range(1, 2)) - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); } static string MapToDocumentValue(int value, bool multiple = false) => @@ -179,13 +179,13 @@ 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", TargetBranch = "master", Status = MergeResult.Conflicts, - Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Change, Path = $"file\\key {i}" }).ToList() + Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Change, Path = $"file/key {i}" }).ToList() }); } } @@ -210,13 +210,13 @@ protected override async Task Because() async Task addItems(string branch, bool changeValuesForKeys = false) { foreach (var i in Enumerable.Range(1, 2)) - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); } async Task removeItems(string branch, int start, int count) { foreach (var i in Enumerable.Range(start, count)) - await Subject.Delete(branch, $"file\\key {i}", $"Deleted {i}({branch})", Author); + await Subject.Delete(branch, $"file/key {i}", $"Deleted {i}({branch})", Author); } static string MapToDocumentValue(int value, bool multiple = false) => (multiple ? value * 2 : value).ToString(); @@ -236,13 +236,13 @@ 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", TargetBranch = "master", Status = MergeResult.Conflicts, - Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Remove, Path = $"file\\key {i}" }).ToList() + Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Remove, Path = $"file/key {i}" }).ToList() }); } } diff --git a/Appy.GitDb.Tests/Properties/AssemblyInfo.cs b/Appy.GitDb.Tests/Properties/AssemblyInfo.cs index b47b640..14d26ae 100644 --- a/Appy.GitDb.Tests/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Tests/Properties/AssemblyInfo.cs @@ -2,36 +2,6 @@ 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..5482f95 100644 --- a/Appy.GitDb.Tests/Rebasing.cs +++ b/Appy.GitDb.Tests/Rebasing.cs @@ -42,7 +42,7 @@ async Task addItems(string branch) { foreach (var i in Enumerable.Range(_index, 5)) { - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = i.ToString() }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = i.ToString() }, Author); _removedFiles.Remove(i); _addedFiles.Add(i); } @@ -54,7 +54,7 @@ async Task removeItems(string branch, int start, int count) { foreach (var i in Enumerable.Range(start, count)) { - await Subject.Delete(branch, $"file\\key {i}", $"Deleted {i}({branch})", Author); + await Subject.Delete(branch, $"file/key {i}", $"Deleted {i}({branch})", Author); _addedFiles.Remove(i); _removedFiles.Add(i); } @@ -62,19 +62,19 @@ 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() => (await Subject.GetFiles("test", "file")) - .Select(int.Parse) + .Select(int.Parse).ToList() .Should() .Contain(_addedFiles); [Fact] public async Task RemovesTheCorrectFilesFromTheBranch() => (await Subject.GetFiles("test", "file")) - .Select(int.Parse) + .Select(int.Parse).ToList() .Should() .NotContain(_removedFiles); } @@ -89,7 +89,7 @@ public class RebasingWhenThereAreNoChanges : WithRepo protected override async Task Because() { foreach (var i in Enumerable.Range(0, 5)) - await Subject.Save("master", $"Added {i} (master)", new Document { Key = $"file\\key {i}", Value = i.ToString() }, Author); + await Subject.Save("master", $"Added {i} (master)", new Document { Key = $"file/key {i}", Value = i.ToString() }, Author); _masterCommit = Repo.Branches["master"].Tip.Sha; @@ -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() => @@ -126,7 +126,7 @@ protected override async Task Because() async Task addItem(string branch, bool changeValuesForKeys = false) { foreach (var i in Enumerable.Range(1, 2)) - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); } static string MapToDocumentValue(int value, bool multiple = false) => @@ -152,13 +152,13 @@ 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", TargetBranch = "master", Status = RebaseResult.Conflicts, - Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Change, Path = $"file\\key {i}" }).ToList() + Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Change, Path = $"file/key {i}" }).ToList() }); } } @@ -184,13 +184,13 @@ protected override async Task Because() async Task addItems(string branch, bool changeValuesForKeys = false) { foreach (var i in Enumerable.Range(1, 2)) - await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file\\key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); + await Subject.Save(branch, $"Added {i} ({branch})", new Document { Key = $"file/key {i}", Value = MapToDocumentValue(i, changeValuesForKeys) }, Author); } async Task removeItems(string branch, int start, int count) { foreach (var i in Enumerable.Range(start, count)) - await Subject.Delete(branch, $"file\\key {i}", $"Deleted {i}({branch})", Author); + await Subject.Delete(branch, $"file/key {i}", $"Deleted {i}({branch})", Author); } static string MapToDocumentValue(int value, bool multiple = false) => (multiple ? value * 2 : value).ToString(); @@ -210,13 +210,13 @@ 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", TargetBranch = "master", Status = RebaseResult.Conflicts, - Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Remove, Path = $"file\\key {i}" }).ToList() + Conflicts = Enumerable.Range(1, 2).Select(i => new ConflictInfo { Type = ConflictType.Remove, Path = $"file/key {i}" }).ToList() }); } } diff --git a/Appy.GitDb.Tests/Transactions.cs b/Appy.GitDb.Tests/Transactions.cs index 9f46ec6..391a269 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 @@ -112,8 +112,8 @@ protected override async Task Because() } [Fact] - public void DoesNotCreateACommit() => - Subject.Get(Branch, Key).Result.Should().BeNull(); + public async Task DoesNotCreateACommit() => + (await Subject.Get(Branch, Key)).Should().BeNull(); } public class AddingAFileToAnAbortedTransaction : WithRepo diff --git a/Appy.GitDb.Tests/Utils/WithRepo.cs b/Appy.GitDb.Tests/Utils/WithRepo.cs index b77497b..ff0996b 100644 --- a/Appy.GitDb.Tests/Utils/WithRepo.cs +++ b/Appy.GitDb.Tests/Utils/WithRepo.cs @@ -3,21 +3,38 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Appy.GitDb.Core.Interfaces; using Appy.GitDb.Core.Model; using Appy.GitDb.Local; using Appy.GitDb.Remote; -using Appy.GitDb.Server; -using Appy.GitDb.Server.Auth; using Appy.GitDb.Watcher; using LibGit2Sharp; -using Microsoft.Owin.Testing; using Xunit; +using Newtonsoft.Json.Linq; + +#if NETFX +using Appy.GitDb.Server; +using Appy.GitDb.Server.Auth; +using Microsoft.Owin.Testing; +#endif + +#if NETCORE +using Appy.GitDb.NetCore.Server.Auth; +using Appy.GitDb.NetCore.Server.GitDb; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Appy.GitDb.NetCore.Server; +#endif namespace Appy.GitDb.Tests.Utils { +#if NETFX public abstract class WithRepo : IAsyncLifetime { protected IGitDb Subject; @@ -25,17 +42,18 @@ public abstract class WithRepo : IAsyncLifetime protected Repository Repo; protected readonly Author Author = new Author("author", "author@mail.com"); protected readonly int TransactionTimeout = 1; + 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] }; - readonly IEnumerable _users = new List {Admin, ReadOnly, WriteOnly, ReadWrite}; + readonly IEnumerable _users = new List { Admin, ReadOnly, WriteOnly, ReadWrite }; protected virtual Task Because() => Task.CompletedTask; @@ -50,14 +68,144 @@ static void deleteReadOnlyDirectory(string 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 System.Net.Http.Headers.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 + 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); + Subject = new RemoteGitDb(_client); + Repo = new Repository(LocalPath); + await Because(); + } + + public Task DisposeAsync() + { + _server.Dispose(); + Subject.Dispose(); + Repo.Dispose(); + deleteReadOnlyDirectory(LocalPath); + 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", + IsBare = false + }); + } + } +#endif + +#if NETCORE + + public abstract class WithRepo : IAsyncLifetime + { + protected IGitDb Subject; + protected readonly string LocalPath = Path.GetTempPath() + Guid.NewGuid(); + protected Repository Repo; + protected readonly Author Author = new Author("author", "author@mail.com"); + protected readonly int TransactionTimeout = 1; + + TestServer _server; + HttpClient _client; + + protected static readonly GitApiUserSetting Admin = new GitApiUserSetting { UserName = "admin", Password = "admin", Roles = new List { "admin", "read", "write" } }; + protected static readonly GitApiUserSetting ReadOnly = new GitApiUserSetting { UserName = "readonly", Password = "readonly", Roles = new List { "read" } }; + protected static readonly GitApiUserSetting WriteOnly = new GitApiUserSetting { UserName = "writeonly", Password = "writeonly", Roles = new List { "write" } }; + protected static readonly GitApiUserSetting ReadWrite = new GitApiUserSetting { UserName = "readwrite", Password = "readwrite", Roles = new List { "read", "write" } }; + protected static readonly GitApiUserSetting None = new GitApiUserSetting { UserName = "", Password = "", Roles = new List() }; + readonly List _users = new List { Admin, ReadOnly, WriteOnly, ReadWrite}; + + protected virtual Task Because() => Task.CompletedTask; + + static void deleteReadOnlyDirectory(string directory) + { + Directory.EnumerateDirectories(directory) + .ForEach(deleteReadOnlyDirectory); + Directory.EnumerateFiles(directory).Select(file => new FileInfo(file) { Attributes = FileAttributes.Normal }) + .ForEach(fi => fi.Delete()); + + Directory.Delete(directory); + } + + protected void WithUser(GitApiUserSetting user) => + _client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user.UserName}:{user.Password}"))); + + static Dictionary toDictionary(object o, string settingName) => + addPropsToDic(JObject.FromObject(o), new Dictionary(), settingName); + + static Dictionary addPropsToDic(JObject jo, Dictionary dic, string prefix) + { + jo.Properties() + .Aggregate(dic, (d, jt) => + { + var value = jt.Value; + + if (value is JArray array) + { + for (var i = 0; i < array.Count; i++) + { + if (array[i].HasValues) + addPropsToDic((JObject)array[i], dic, $"{prefix}:{jt.Name}:{i}"); + else + dic.Add($"{prefix}:{jt.Name}:{i}", array[i].ToString()); + } + + return dic; + } + + var key = $"{prefix}:{jt.Name}"; + if (value.HasValues) + return addPropsToDic((JObject)jt.Value, dic, key); + + dic.Add(key, value.ToString()); + return dic; + }); + return dic; + } + + public async Task InitializeAsync() + { + var dbSettings = new GitDbSettings + { + GitHomePath = LocalPath, + TransactionsTimeout = TransactionTimeout, + Remote = new GitDbRemoteSettings() + }; + var authSettings = new GitApiAuthSettings + { + Users = _users + }; + + var dbSettingsDic = toDictionary(dbSettings, "GitDb"); + var authSettingsDic = toDictionary(authSettings , "GitApiAuth"); + var dics = new List> { dbSettingsDic, authSettingsDic }; + var settingsDic = dics.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value); + + // host builder + var hostBuilder = new WebHostBuilder() + .ConfigureAppConfiguration((hostingContext, config) => config + .AddInMemoryCollection(settingsDic)) // by memory + .UseStartup(); + + // test server + _server = new TestServer(hostBuilder); + _client = _server.CreateClient(); + _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + // subjects WithUser(Admin); Subject = new RemoteGitDb(_client); Repo = new Repository(LocalPath); @@ -86,4 +234,5 @@ protected void MoveToNormalRepo(string baseDir) }); } } -} \ No newline at end of file +#endif +} diff --git a/Appy.GitDb.Tests/Watcher/ModifyingBranches.cs b/Appy.GitDb.Tests/Watcher/ModifyingBranches.cs index e57db3f..f3d8e0e 100644 --- a/Appy.GitDb.Tests/Watcher/ModifyingBranches.cs +++ b/Appy.GitDb.Tests/Watcher/ModifyingBranches.cs @@ -57,7 +57,7 @@ protected override async Task Because() { using (var t = await GitDb.CreateTransaction("master")) { - await t.Add(new Document {Key = "subdir\\key", Value = "value"}); + await t.Add(new Document {Key = "subdir/key", Value = "value"}); await t.Delete("key"); await t.Commit("message", Author); } @@ -66,7 +66,7 @@ protected override async Task Because() [Fact] public void RaisesBranchChangedEvent() => BranchChanged.Should().Contain(args => args.Branch.Name == "master" && - args.Renamed.Any(item => item.Key == "subdir\\key" && + args.Renamed.Any(item => item.Key == "subdir/key" && item.OldKey == "key" && item.GetValue() == "value" && item.GetOldValue() == "value")); diff --git a/Appy.GitDb.Tests/packages.config b/Appy.GitDb.Tests/packages.config deleted file mode 100644 index 164799f..0000000 --- a/Appy.GitDb.Tests/packages.config +++ /dev/null @@ -1,25 +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 c8dca7d..ec6afa5 100644 --- a/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj +++ b/Appy.GitDb.Watcher/Appy.GitDb.Watcher.csproj @@ -1,70 +1,18 @@ - - - - + + - Debug - AnyCPU - {E3EBA271-650E-4482-BDEA-41F569051B4D} - Library - Properties - Appy.GitDb.Watcher - Appy.GitDb.Watcher - v4.6.1 - 512 - - + net461;netstandard2.0 + 1591 + AnyCpu + Library to watch a git repository and raise events whenever branches are added or deleted and when they are changed. This allows you to save the results in a read optimized store. - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\LibGit2Sharp.0.23.1\lib\net40\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/Appy.GitDb.Watcher.nuspec b/Appy.GitDb.Watcher/Appy.GitDb.Watcher.nuspec deleted file mode 100644 index 8329314..0000000 --- a/Appy.GitDb.Watcher/Appy.GitDb.Watcher.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - $id$ - $version$ - GitDb Watcher - Library to watch a git repository and raise events whenever branches are added or deleted and when they are changed. This allows you to save the results in a read optimized store. - $author$ - https://github.com/YellowLineParking/Appy.GitDb - https://github.com/YellowLineParking/Appy.GitDb/blob/master/LICENSE - https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/logo.png - $copyright$ - git database nosql - - \ No newline at end of file diff --git a/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs b/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs index 487dcd4..56cdb45 100644 --- a/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs +++ b/Appy.GitDb.Watcher/Properties/AssemblyInfo.cs @@ -1,17 +1,5 @@ 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.10.2")] diff --git a/Appy.GitDb.Watcher/packages.config b/Appy.GitDb.Watcher/packages.config deleted file mode 100644 index e62763d..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..41372b5 100644 --- a/Appy.GitDb.sln +++ b/Appy.GitDb.sln @@ -3,28 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Server", "Appy.GitDb.Server\Appy.GitDb.Server.csproj", "{A9FAB943-9F28-49F8-82FB-0380A22FD683}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Server", "Appy.GitDb.Server\Appy.GitDb.Server.csproj", "{A9FAB943-9F28-49F8-82FB-0380A22FD683}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Core", "Appy.GitDb.Core\Appy.GitDb.Core.csproj", "{72AE8930-98EF-446D-8167-7DD251BCF4E6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Core", "Appy.GitDb.Core\Appy.GitDb.Core.csproj", "{72AE8930-98EF-446D-8167-7DD251BCF4E6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Remote", "Appy.GitDb.Remote\Appy.GitDb.Remote.csproj", "{3F7A7050-68CB-4D7F-9149-A59095EB78A1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Remote", "Appy.GitDb.Remote\Appy.GitDb.Remote.csproj", "{3F7A7050-68CB-4D7F-9149-A59095EB78A1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Local", "Appy.GitDb.Local\Appy.GitDb.Local.csproj", "{37D87FB6-E7D6-4217-B021-38CEA7233E6F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Local", "Appy.GitDb.Local\Appy.GitDb.Local.csproj", "{37D87FB6-E7D6-4217-B021-38CEA7233E6F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{46D2F58C-5500-4E79-B015-858594C6D8A0}" ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml build.bat = build.bat config.yml = config.yml - Functions.ps1 = Functions.ps1 + functions.ps1 = functions.ps1 tasks.ps1 = tasks.ps1 EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Tests", "Appy.GitDb.Tests\Appy.GitDb.Tests.csproj", "{1C2D86C4-8CAD-457E-B4F1-86A5523B3112}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Tests", "Appy.GitDb.Tests\Appy.GitDb.Tests.csproj", "{1C2D86C4-8CAD-457E-B4F1-86A5523B3112}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Watcher", "Appy.GitDb.Watcher\Appy.GitDb.Watcher.csproj", "{E3EBA271-650E-4482-BDEA-41F569051B4D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Watcher", "Appy.GitDb.Watcher\Appy.GitDb.Watcher.csproj", "{E3EBA271-650E-4482-BDEA-41F569051B4D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Appy.GitDb.Benchmark", "Appy.GitDb.Benchmark\Appy.GitDb.Benchmark.csproj", "{314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.Benchmark", "Appy.GitDb.Benchmark\Appy.GitDb.Benchmark.csproj", "{314EE50D-F6E4-4CB6-AB00-22A7258CF8F5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E7B44F83-74DE-4DD0-9101-6290EB0DA145}" + ProjectSection(SolutionItems) = preProject + Shared.msbuild = Shared.msbuild + SharedAssemblyInfo.cs = SharedAssemblyInfo.cs + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Appy.GitDb.NetCore.Server", "Appy.GitDb.NetCore.Server\Appy.GitDb.NetCore.Server.csproj", "{2F130E21-4FAA-4CC0-ADB7-9367E82AB70A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -60,6 +68,10 @@ Global {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 + {2F130E21-4FAA-4CC0-ADB7-9367E82AB70A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F130E21-4FAA-4CC0-ADB7-9367E82AB70A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F130E21-4FAA-4CC0-ADB7-9367E82AB70A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F130E21-4FAA-4CC0-ADB7-9367E82AB70A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2207291 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,167 @@ +# Contributing to Appy.GitDb + +We'd love for you to contribute to our source code and to make Appy.GitDb even better than it is +today! Here are the guidelines we'd like you to follow: + + - [Code of Conduct](#coc) + - [Question or Problem?](#question) + - [Issues and Bugs](#issue) + - [Feature Requests](#feature) + - [Submission Guidelines](#submit) + - [Coding Rules](#rules) + - [Commit Message Guidelines](#commit) + +## Code of Conduct + +Help us keep the project open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md). + +## Got a Question or Problem? + + +## Found an Issue? + +If you find a bug in the source code or a mistake in the documentation, you can help us by +submitting an issue to our [GitHub Repository](https://github.com/YellowLineParking/Appy.GitDb). Even better you can submit a Pull Request +with a fix. + +**Please see the [Submission Guidelines](#submit) below.** + +## Want a Feature? + +You can request a new feature by submitting an issue to our [GitHub Repository](https://github.com/YellowLineParking/Appy.GitDb). If you +would like to implement a new feature then consider what kind of change it is: + + prevent duplication of work, and help you to craft the change so that it is successfully accepted + into the project. +* **Small Changes** can be crafted and submitted to the [GitHub Repository](https://github.com/YellowLineParking/Appy.GitDb) as a Pull + Request. + +## Submission Guidelines + +### Submitting an Issue + +If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize +the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. + +Providing the following information will increase the chances of your issue being dealt with +quickly: + +* **Overview of the Issue** - if an error is being thrown a stack trace helps +* **Motivation for or Use Case** - explain why this is a bug for you +* **Appy.GitDb Version(s)** - is it a regression? +* **Operating System** - is this a problem with all browsers or only specific ones? +* **Reproduce the Error** - provide a example or an unambiguous set of steps. +* **Related Issues** - has a similar issue been reported before? +* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be + causing the problem (line of code or commit) + +**If you get help, help others. Good karma rulez!** + +### Submitting a Pull Request +Before you submit your pull request consider the following guidelines: + +* Search [GitHub](https://github.com/YellowLineParking/Appy.GitDb/pulls) for an open or closed Pull Request + that relates to your submission. You don't want to duplicate effort. +* Make your changes in a new git branch: + + ```shell + git checkout -b my-fix-branch master + ``` + +* Create your patch, **including appropriate test cases**. +* Follow our [Coding Rules](#rules). +* Run the test suite, as described below. +* Commit your changes using a descriptive commit message that follows our + [commit message conventions](#commit). + + ```shell + git commit -a + ``` + Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. + +* Build your changes locally to ensure all the tests pass: + + ```shell + npm test + npm run build + ``` + +* Push your branch to GitHub: + + ```shell + git push origin my-fix-branch + ``` + +In GitHub, send a pull request to `Appy.GitDb:master`. + +If we suggest changes, then: + +* Make the required updates. +* Re-run the test suite to ensure tests are still passing. +* Commit your changes to your branch (e.g. `my-fix-branch`). +* Push the changes to your GitHub repository (this will update your Pull Request). + +If the PR gets too outdated we may ask you to rebase and force push to update the PR: + +```shell +git rebase master -i +git push origin my-fix-branch -f +``` + +_WARNING: Squashing or reverting commits and force-pushing thereafter may remove GitHub comments +on code that were previously made by you or others in your commits. Avoid any form of rebasing +unless necessary._ + +That's it! Thank you for your contribution! + +#### After your pull request is merged + +After your pull request is merged, you can safely delete your branch and pull the changes +from the main (upstream) repository: + +* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows: + + ```shell + git push origin --delete my-fix-branch + ``` + +* Check out the master branch: + + ```shell + git checkout master -f + ``` + +* Delete the local branch: + + ```shell + git branch -D my-fix-branch + ``` + +* Update your master with the latest upstream version: + + ```shell + git pull --ff upstream master + ``` + +## Coding Rules + +To ensure consistency throughout the source code, keep these rules in mind as you are working: + +* All features or bug fixes **must be tested** by one or more unit tests. +* All public API methods **must be documented** with XML documentation. + +### Subject +The subject contains succinct description of the change: + +* use the imperative, present tense: "change" not "changed" nor "changes" +* no dot (.) at the end + +### Body +Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". +The body should include the motivation for the change and contrast this with previous behavior. + +### Footer +The footer should contain any information about **Breaking Changes** and is also the place to +reference GitHub issues that this commit **Closes**. + +**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this. \ No newline at end of file diff --git a/Functions.ps1 b/Functions.ps1 index 3d15d17..935ffe2 100644 --- a/Functions.ps1 +++ b/Functions.ps1 @@ -1,45 +1,43 @@ -#region Variables +Param( + [Parameter(Mandatory=$True,Position=1)] + [string]$yamelDotNetVersion, + [Parameter(Mandatory=$True,Position=2)] + [string]$octopusToolsVersion, + [Parameter(Mandatory=$True,Position=3)] + [string]$artifactsPath +) -$script:artifactsPath = "artifacts" +$script:projectConfig = $null -#endregion +function Get-YamlDotNetPath() { + return ".\packages\YamlDotNet.$yamelDotNetVersion" +} -#region General -function Get-PackagePath($packageId, $project) { - if (!(Test-Path "$project\packages.config")) { - throw "Could not find a packages.config file at $project" - } - - [xml]$packagesXml = Get-Content "$project\packages.config" - $package = $packagesXml.packages.package | Where { $_.id -eq $packageId } - if (!$package) { - throw "$packageId is required in $project, but it is not installed. Please install $packageId in $project" - } - return "$packagesPath\$($package.id).$($package.version)" +function Get-OctopusToolsPath() { + return ".\packages\OctopusTools.$octopusToolsVersion" } -$script:projectConfig = $null -function Get-ProjectsForTask($task){ +function Get-ProjectsForTask($task) { $task = $task.ToLower() - if($projectConfig -eq $null){ - $yamlPackagePath = Get-PackagePath "YamlDotNet" $projectName - Add-Type -Path "$yamlPackagePath\lib\dotnet\yamldotnet.dll" + if($projectConfig -eq $null) { + $yamlPackagePath = Get-YamlDotNetPath; + Add-Type -Path "$yamlPackagePath\lib\net45\YamlDotNet.dll" $config = Resolve-Path ".\config.yml" $yaml = [IO.File]::ReadAllText($config).Replace("`t", " ") $stringReader = new-object System.IO.StringReader([string]$yaml) - $Deserializer = New-Object -TypeName YamlDotNet.Serialization.Deserializer -ArgumentList $null, $null, $false + $Deserializer = New-Object -TypeName YamlDotNet.Serialization.Deserializer $projectConfig = $Deserializer.Deserialize([System.IO.TextReader]$stringReader) } $config = @{ - "clean" = "EmbeddedWebJob", "StandaloneWebJob", "App", "VsTest", "XUnit", "VsTestAndXUnit", "Package"; - "compile" = "EmbeddedWebJob", "StandaloneWebJob", "App", "VsTest", "XUnit", "VsTestAndXUnit", "Package"; - "test" = "VsTest", "XUnit", "VsTestAndXUnit"; - "pack" = "StandaloneWebJob", "Package", "App"; - "push" = "StandaloneWebJob", "Package", "App"; - "release" = "StandaloneWebJob", "App"; - "deploy" = "StandaloneWebJob", "App"; + "clean" = "App", "XUnit", "Package", "AppPackage"; + "compile" = "App", "XUnit", "Package", "AppPackage"; + "test" = "XUnit"; + "pack" = "Package", "App", "AppPackage"; + "push" = "Package", "App", "AppPackage"; + "release" = "App"; + "deploy" = "App"; } $projectTypes = $config[$task] @@ -57,134 +55,106 @@ function Get-ProjectsForTask($task){ }} } -#endregion - -#region "Clean" - -function Clean-Folder($folder){ +function Clean-Folder($folder) { "Cleaning $folder" Remove-Item $folder -Recurse -Force -ErrorAction SilentlyContinue | Out-Null } -#endregion - -#region Compile - function Compile-Project($projectName) { use "15.0" MsBuild $projectFile = "$projectName\$projectName.csproj" - $isWebProject = (((Select-String -pattern ".+" -path $projectFile) -ne $null) -and ((Select-String -pattern "WinExe" -path $projectFile) -eq $null)) - $isWinProject = (((Select-String -pattern ".+" -path $projectFile) -eq $null) -and ((Select-String -pattern "WinExe" -path $projectFile) -ne $null)) - $isExeProject = (((Select-String -pattern ".+" -path $projectFile) -eq $null) -and ((Select-String -pattern "Exe" -path $projectFile) -ne $null)) + $version = Get-Version $projectName - if ($isWebProject) { - Write-Host "Compiling $projectName to $artifactsPath" - exec { MSBuild $projectFile /p:Configuration=$config /nologo /p:DebugType=None /p:Platform=AnyCpu /p:WebProjectOutputDir=..\$artifactsPath\$projectName /p:OutDir=$artifactsPath\bin /verbosity:quiet } - } - elseif ($isWinProject -or $isExeProject) { - Write-Host "Compiling $projectName to $artifactsPath" - exec { MSBuild $projectFile /p:Configuration=$config /nologo /p:DebugType=None /p:Platform=AnyCpu /p:OutDir=..\$artifactsPath\$projectName /verbosity:quiet /p:Disable_CopyWebApplication=True } - } - else{ - Write-Host "Compiling $projectName" - exec { MSBuild $projectFile /p:Configuration=$config /nologo /p:Platform=AnyCpu /verbosity:quiet } - } + Compile-Dotnet $projectFile $projectName $artifactsPath $config $version } -function Move-WebJob($projectName, $target, $type){ - Write-Host "Moving WebJob $projectName into $target as a $type job" - $sourcePath = "$artifactsPath/$projectName" - - if($projectName -eq $target){ - $tempPath = "$artifactsPath\temp" - New-Item $tempPath -ItemType Directory -Force - Move-Item -Path $sourcePath -Destination $tempPath | Out-Null - $sourcePath = "$artifactsPath\temp\*" +function Compile-Dotnet($projectFile, $projectName, $artifactsPath, $config, $version) { + Write-Host "Compiling Dotnet $projectName" + exec { dotnet build $projectFile --configuration $config } + + $isDotnetWebProject = (Select-String -pattern '' -path $projectFile) -ne $null + $isDotnetExeProject = (Select-String -pattern "Exe" -path $projectFile) -ne $null + if ($isDotnetWebProject -or $isDotnetExeProject) { + Write-Host "Publishing Dotnet $projectName to $artifactsPath" + exec { dotnet publish $projectFile --no-build --no-restore --configuration $config --output ..\$artifactsPath\$projectName --version-suffix $version } + return } - $targetPath = "$artifactsPath\$target\App_Data\Jobs\$type" - New-Item -Path $targetPath -ItemType Directory -Force - Copy-Item $sourcePath $targetPath -Force -Recurse | Out-Null - - if($projectName -eq $target){ - Remove-Item "$artifactsPath\temp" -Recurse -Force | Out-Null + $singleFramework = Get-Content $projectFile | Where { $_.Contains("") } + $multiFrameworks = Get-Content $projectFile | Where { $_.Contains("") } + $frameworks = If ($singleFramework -ne $null) { @(([xml]$singleFramework).TargetFramework) } Else { ([xml]$multiFrameworks).TargetFrameworks.Split(';') } + $frameworks | ForEach-Object { + $framework = $_ + Write-Host "Publishing Dotnet $projectName with framework $framework to $artifactsPath" + exec { dotnet publish $projectFile --no-build --no-restore --configuration $config --output ..\$artifactsPath\$projectName\$framework --framework $framework --version-suffix $version } } } -#endregion - -#region Tests - -function Execute-Xunit($projectName){ - $xunitRunnerPath = Get-PackagePath XUnit.Runner.Console "$projectName" - $runnerExecutable = "$xunitRunnerPath\tools\xunit.console.exe" - exec { & $runnerExecutable $projectName\bin\$config\$projectName.dll -xml "$artifactsPath\xunit_$projectName.xml" -html "$artifactsPath\xunit_$projectName.html" -nologo } +function Execute-Xunit($projectName) { + $projectFile = "$projectName\$projectName.csproj" + + Execute-Xunit-Dotnet $projectFile $projectName $artifactsPath $config } -function Execute-VsTest ($projectName){ - $vstestrunner = Resolve-VsTestRunner - - $path = Resolve-Path "$projectName\bin\$config\$projectName.dll" +function Execute-Xunit-Dotnet($projectFile, $projectName, $artifactsPath, $config) { + $singleFramework = Get-Content $projectFile | Where { $_.Contains("") } + $multiFrameworks = Get-Content $projectFile | Where { $_.Contains("") } + $frameworks = If ($singleFramework -ne $null) { @(([xml]$singleFramework).TargetFramework) } Else { ([xml]$multiFrameworks).TargetFrameworks.Split(';') } - try - { - if (Is-LocalBuild) { - $logger = "trx" - } - else { - $logger = "AppVeyor" - } - exec { & $vstestrunner $path /logger:$logger } - } - finally - { - # Move the files from the project dir to the output dir because trx does not support defining the outputfile - if(Test-Path "TestResults") - { - Get-ChildItem "TestResults" -Filter *.trx | - Foreach-Object { - Move-Item -Path $_.FullName -Destination $artifactsPath\$projectName.trx - } - } + $frameworks | ForEach-Object { + $framework = $_ + Write-Host "Executing tests for $projectName with framework $framework to $artifactsPath" + exec { dotnet test $projectFile --configuration $config --no-build --no-restore --framework $framework --test-adapter-path:. "--logger:xunit;LogFilePath=../$artifactsPath/xunit-$projectName-$framework.xml" --verbosity quiet } } } -function Resolve-VsTestRunner { - $vstestrunner = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" - if(Test-Path $vstestrunner){ - Write-Host "Using vstest.console.exe from VS 2017 Install" - return $vstestrunner - } - - $vstestrunner = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" - if(Test-Path $vstestrunner){ - Write-Host "Using vstest.console.exe from VS 2015 Install" - return $vstestrunner - } - - Write-Host "No VS install detected, Using vstest.console.exe from PATH" - return "vstest.console.exe" +$versions = @{} +function Get-VersionFromAssemblyInfo($path){ + if (!(Test-Path $path)) { + return + } + $line = Get-Content $path | Where { $_.Contains("AssemblyVersion") } + if ($line){ + return $line.Split('"')[1] + } + return } -#endregion +function Get-VersionFromCsProj($path){ + if (!(Test-Path $path)) { + return + } + $line = Get-Content $path | Where { $_.Contains("") } + if ($line){ + return ([xml]$line).Version + } + return +} -#region Push and Pack -$versions = @{} function Get-Version($projectName) { - if(!$versions.ContainsKey($projectName)){ - - $line = Get-Content "$projectName\Properties\AssemblyInfo.cs" | Where { $_.Contains("AssemblyVersion") } - if (!$line) { - throw "Couldn't find an AssemblyVersion attribute" + if (!$versions.ContainsKey($projectName)){ + + $version = Get-VersionFromAssemblyInfo "$projectName\Properties\AssemblyInfo.cs" + if (!$version) { + $version = Get-VersionFromAssemblyInfo "$projectName\..\SharedAssemblyInfo.cs" + } + if (!$version){ + $version = Get-VersionFromCsProj "$projectName\$projectName.csproj" + } + if (!$version) { + throw "Couldn't find an AssemblyVersion from the AssemblyInfo or .csproj file" } - $version = $line.Split('"')[1] + $branch = Get-CurrentBranch $isGeneralRelease = Is-GeneralRelease $branch $isLocalBuild = Is-LocalBuild - if($isLocalBuild -or !$isGeneralRelease){ - $version = "$($version.Replace("*", 0))-$(Get-PrereleaseNumber $branch)" - } else{ + if ($isLocalBuild -or !$isGeneralRelease){ + if ([String]::IsNullOrEmpty($script:prereleaseNumber)) { + $script:prereleaseNumber = Get-PrereleaseNumber $branch; + } + $version = "$($version.Replace("*", 0))-$script:prereleaseNumber" + } else { $version = $version.Replace("*", $env:APPVEYOR_BUILD_NUMBER) } $versions.Add($projectName, $version) @@ -193,7 +163,7 @@ function Get-Version($projectName) { return $versions[$projectName] } -function Get-CurrentBranch{ +function Get-CurrentBranch { if([String]::IsNullOrEmpty($env:APPVEYOR_REPO_BRANCH)){ $branch = git branch | Where {$_ -match "^\*(.*)"} | Select-Object -First 1 } else{ @@ -202,7 +172,7 @@ function Get-CurrentBranch{ return $branch } -function Is-GeneralRelease($branch){ +function Is-GeneralRelease($branch) { return ($branch -eq "develop" -or $branch -eq "master") } @@ -210,22 +180,29 @@ function Is-LocalBuild(){ return [String]::IsNullOrEmpty($env:APPVEYOR_REPO_BRANCH) } -function Get-PrereleaseNumber($branch){ +function Get-PrereleaseNumber($branch) { $branch = $branch.Replace("* ", "") - if($branch.IndexOf("/") -ne -1){ - $prefix = $branch.Substring(0, $branch.IndexOf("/") + 1) - }else{ + if ($branch.IndexOf("/") -ne -1){ + $prefix = $branch.Substring($branch.IndexOf("/") + 1) + } else { $prefix = $branch } - $prefix = $prefix.Substring(0, [System.Math]::Min(7, $prefix.Length)) + $prefix = $prefix.Substring(0, [System.Math]::Min(10, $prefix.Length)) return $prefix + "-" + $(Get-Date).ToString("yyMMddHHmmss") -Replace "[^a-zA-Z0-9-]", "" } -function Pack-Project($projectName){ - $version = Get-Version $projectName +function Pack-Project($projectName) { + use "15.0" MsBuild Write-Host "Packing $projectName $version to $artifactsPath" - exec { & NuGet pack $projectName\$projectName.csproj -Build -Properties Configuration=$config -OutputDirectory $artifactsPath -Version $version -IncludeReferencedProjects -NonInteractive } + $version = Get-Version $projectName + $projectFile = "$projectName\$projectName.csproj" + + Pack-Dotnet $projectFile $version +} + +function Pack-Dotnet($projectFile, $version) { + exec { dotnet pack $projectFile --configuration $config --no-build --no-restore --output ..\$artifactsPath -p:PackageVersion=$version } } function Push-Package($package, $nugetPackageSource, $nugetPackageSourceApiKey, $ignoreNugetPushErrors) { @@ -256,19 +233,15 @@ function Push-Package($package, $nugetPackageSource, $nugetPackageSourceApiKey, } } -#endregion - -#region Octopus Deploy - -function OctoPack-Project($project){ - $octopusToolsPath = Get-PackagePath OctopusTools $projectName +function OctoPack-Project($project) { + $octopusToolsPath = Get-OctopusToolsPath $version = Get-Version $project Write-Host "Packing $project $version to $artifactsPath" $releaseNotes = Get-ReleaseNotes exec { & $octopusToolsPath\tools\Octo.exe pack --basePath=$artifactsPath\$project --outFolder=$artifactsPath --id=$project --version=$version --releaseNotes=$releaseNotes --overwrite} } -function Get-ReleaseNotes(){ +function Get-ReleaseNotes() { $commit = git rev-parse HEAD $shortCommit = $commit.Substring(0,7) $branch = Get-CurrentBranch @@ -279,16 +252,16 @@ function Get-ReleaseNotes(){ return "Build date: $(Get-Date -f 'yyyy-MM-dd HH:mm:ss')`rBuilt on: $computerName`rCommit Author: $author`rCommit author Email: $authorEmail`rBranch: $branch`rCommit: $shortCommit" } -function Octo-CreateRelease($project){ - $octopusToolsPath = Get-PackagePath OctopusTools $projectName +function Octo-CreateRelease($project) { + $octopusToolsPath = Get-OctopusToolsPath $version = Get-Version $project $releaseNotes = Get-ReleaseNotes Write-Host "Creating release $version of $project on $env:ylp_octopusDeployServer" exec { & $octopusToolsPath\tools\Octo.exe create-release --server="$env:ylp_octopusDeployServer" --apiKey="$env:ylp_octopusDeployApiKey" --project="$project" --version="$version" --releaseNotes=$releaseNotes --packageVersion="$version" --ignoreexisting } } -function Octo-DeployRelease($project){ - $octopusToolsPath = Get-PackagePath OctopusTools $projectName +function Octo-DeployRelease($project) { + $octopusToolsPath = Get-OctopusToolsPath $version = Get-Version $project $environments = $env:ylp_environment.Split(",") @@ -298,6 +271,4 @@ function Octo-DeployRelease($project){ Write-Host "Deploying release $version of $project on $env:ylp_octopusDeployServer to $environment" exec { & $octopusToolsPath\tools\Octo.exe deploy-release --server="$env:ylp_octopusDeployServer" --apiKey="$env:ylp_octopusDeployApiKey" --project="$project" --version="$version" --deployto="$environment" } } -} - -#endregion \ No newline at end of file +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 3d34833..b506a16 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 YellowLineParking +Copyright (c) 2019 YellowLineParking Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 219aaac..5381431 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ - - - - -# Appy.GitDb +# Appy.GitDb Appy.GitDb is a set of packages and applications to use Git as a NoSql database +## Continuous Integration + +| Name | Operating System | Status | +| :--- | :--- | :--- | +| AppVeyor | Windows | [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/github/YellowLineParking/Appy.GitDb?branch=master&svg=true)](https://ci.appveyor.com/project/YellowLineParking/ylp-gitdb) | + + +## NuGet Packages + +| Name | Framework | NuGet | +| :--- | :--- | :--- +| ![Appy.GitDb Icon](https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/icon-16x16.png) [Appy.GitDb.Local](https://www.nuget.org/packages/Appy.GitDb.Local/) | net461/netstandard2.0 | [![Appy.GitDb.Local](https://img.shields.io/nuget/v/Appy.GitDb.Local.svg)](https://www.nuget.org/packages/Appy.GitDb.Local/) | +![Appy.GitDb Icon](https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/icon-16x16.png) [Appy.GitDb.Remote](https://www.nuget.org/packages/Appy.GitDb.Remote/) | net461/netstandard2.0 | [![Appy.GitDb.Remote](https://img.shields.io/nuget/v/Appy.GitDb.Remote.svg)](https://www.nuget.org/packages/Appy.GitDb.Remote/) | +![Appy.GitDb Icon](https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/icon-16x16.png) [Appy.GitDb.Watcher](https://www.nuget.org/packages/Appy.GitDb.Watcher/) | net461/netstandard2.0 | [![Appy.GitDb.Watcher](https://img.shields.io/nuget/v/Appy.GitDb.Watcher.svg)](https://www.nuget.org/packages/Appy.GitDb.Watcher/) | +![Appy.GitDb Icon](https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/icon-16x16.png) [Appy.GitDb.Server](https://www.nuget.org/packages/Appy.GitDb.Server/) | net461/MVC5 | [![Appy.GitDb.Server](https://img.shields.io/nuget/v/Appy.GitDb.Server.svg)](https://www.nuget.org/packages/Appy.GitDb.Server/) | +![Appy.GitDb Icon](https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/icon-16x16.png) [Appy.GitDb.NetCore](https://www.nuget.org/packages/Appy.GitDb.NetCore.Server/) | netcoreapp2.2 | [![Appy.GitDb.NetCore](https://img.shields.io/nuget/v/Appy.GitDb.NetCore.svg)](https://www.nuget.org/packages/Appy.GitDb.NetCore.Server/) | + ## Getting Started GitDb can be accessd in two different modes: @@ -17,13 +30,22 @@ GitDb can be accessd in two different modes: To start using GitDb locally, first install the `Appy.GitDb.Local` NuGet package: -``` +- Package Manager Console +```csharp Install-Package Appy.GitDb.Local ``` -Now you can use a local repository as a Git database: +or +- .NET CLI Console +``` +dotnet add package Appy.GitDb.Local ``` + + +Now you can use a local repository as a Git database: + +```csharp // 1. Instantiate a new instance of the local git database IGitDb db = new LocalGitDb(@"c:\path\to\a\repository"); @@ -40,8 +62,7 @@ var theObject = await db.Get("master", "key") ``` ### Remote mode - -In order to use Git as a remote database, you need to install the `Appy.GitDb.Server` project on a server. +In order to use Git as a remote database, you can use one of the servers projects `Appy.GitDb.Server` (MVC5) or `Appy.GitDb.NetCore.Server` (NETCore) as base or just the remote package on your own code. Once the server is installed, you can use the `Appy.GitDb.Remote` package to talk to the server: @@ -49,7 +70,7 @@ Once the server is installed, you can use the `Appy.GitDb.Remote` package to tal Install-Package Appy.GitDb.Remote ``` -``` +```csharp // 1. Instantiate a new instance of the remote git database IGitDb db = new RemoteGitDb("username", "password", "http://url-of-git-database.com"); @@ -68,7 +89,7 @@ var theObject = db.Get("master", "key") Both the local as well as the remote git db implement the same interface, so they're easily interchangeable. The interface is defined as follows: -``` +```csharp public interface IGitDb : IDisposable { Task Get(string branch, string key); @@ -99,7 +120,7 @@ public interface IGitDb : IDisposable In order to add, update or remove multiple files in a single commit, you need to create a transaction. You can do so by creating a transaction from the database: -``` +```csharp IGitDb db = // new LocalGitDb(...) or new RemoteGitDb(...) using(var transaction = await db.CreateTransaction("master")) @@ -118,7 +139,7 @@ using(var transaction = await db.CreateTransaction("master")) The transaction returned from the `CreateTransaction`-method implements the following interface (in local as well as in remote mode): -``` +```csharp public interface ITransaction : IDisposable { Task Add(Document document); @@ -138,7 +159,7 @@ public interface ITransaction : IDisposable ## Branch management Currently, GitDb supports the following methods for managing branches: -``` +```csharp // Creates a tag at the specified reference (commit, branch or other tag) Task Tag(Reference reference); @@ -177,7 +198,7 @@ Install-Package Appy.GitDb.Watcher Now, we can set up the watcher to check for changes: -``` +```csharp new Watcher( path: @"path\to\local\repository", interval: intervalToPollForChangesInMilliseconds, @@ -188,7 +209,7 @@ new Watcher( ``` The three methods will be called whenever the watcher detects any changes in the repository. They will be provided with the following arguments: -``` +```csharp public class BranchAdded { // Newly created branch @@ -252,3 +273,7 @@ build.bat dev This will execute the tasks `clean`, `compile`, `test`, `pack` You can execute any of these tasks separately by running `build ` +## Contribute +It would be awesome if you would like to contribute code or help with bugs. Just follow the guidelines [CONTRIBUTING](https://github.com/YellowLineParking/Appy.GitDb/blob/master/CONTRIBUTING.md). + + \ No newline at end of file diff --git a/Shared.msbuild b/Shared.msbuild new file mode 100644 index 0000000..3ad1e2a --- /dev/null +++ b/Shared.msbuild @@ -0,0 +1,122 @@ + + + + $(AssemblyName) + $(AssemblyName) + $(AssemblyName) + $(AssemblyName) + Copyright (c) Yellow Line Parking + Yellow Line Parking + Yellow Line Parking + https://raw.githubusercontent.com/YellowLineParking/Appy.GitDb/master/assets/logo.png + https://github.com/YellowLineParking/Appy.GitDb + https://github.com/YellowLineParking/Appy.GitDb.git + MIT + git + git;database;nosql + + + + 2.0.0 + + + + latest + + + + + $(DefineConstants);NETFX + + + $(DefineConstants);NETCORE + portable + + + $(DefineConstants);NETCORE + portable + + + + + $(DefineConstants);PLATFORM_X86 + + + + false + false + false + false + false + false + false + false + + + + + $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage + + + + + + + + + 4.2.0 + 4.0.0 + 3.1.0 + 3.0.1 + 2.1.0 + 1.0.0 + 1.0.15 + 5.2.3 + 1.0.1 + + + + 4.8.1 + 4.3.1 + 3.5.4 + 4.3.1 + 4.5.4 + 12.0.1 + 4.5.11 + 12.1.1 + 0.25.4 + 1.0.252 + 5.3.0 + 5.4.2 + + + + 2.2.2 + 4.7.1 + + + + 4.5.0 + + + + 15.9.0 + 2.2.0 + 2.3.1 + 5.5.3 + 4.10.1 + 1.2.0.120 + 2.4.1 + 2.4.1 + 2.4.1 + 2.1.26 + + + + + + Properties\SharedAssemblyInfo.cs + + + + \ No newline at end of file diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs new file mode 100644 index 0000000..efd92f8 --- /dev/null +++ b/SharedAssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyCompany("Yellow Line Parking")] +[assembly: AssemblyCopyright("Copyright (c) Yellow Line Parking")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.0")] \ No newline at end of file diff --git a/assets/icon-16x16.png b/assets/icon-16x16.png new file mode 100644 index 0000000..cd18867 Binary files /dev/null and b/assets/icon-16x16.png differ diff --git a/build.bat b/build.bat index ef84ac5..e070681 100644 --- a/build.bat +++ b/build.bat @@ -1,18 +1,32 @@ @echo off powershell -NoProfile -ExecutionPolicy Unrestricted -Command ^ $ErrorActionPreference = 'Stop'; ^ -if (!(Get-Command NuGet -ErrorAction SilentlyContinue) -and (!(Test-Path '%LocalAppData%\NuGet\NuGet.exe') -or !(get-item '%LocalAppData%\NuGet\nuget.exe').VersionInfo.FileVersion.StartsWith('4.1'))) { ^ +if (!(Get-Command NuGet -ErrorAction SilentlyContinue) -and (!(Test-Path '%LocalAppData%\NuGet\NuGet.exe') -or !(get-item '%LocalAppData%\NuGet\nuget.exe').VersionInfo.FileVersion.StartsWith('4.9'))) { ^ Write-Host 'Downloading NuGet.exe'; ^ - (New-Object system.net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/v4.1.0/nuget.exe', '%LocalAppData%\NuGet\NuGet.exe'); ^ + (New-Object system.net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/v4.9.2/nuget.exe', '%LocalAppData%\NuGet\NuGet.exe'); ^ } ^ if (Test-Path '%LocalAppData%\NuGet\NuGet.exe') { ^ Set-Alias NuGet (Resolve-Path %LocalAppData%\NuGet\NuGet.exe); ^ } ^ Write-Host 'Restoring NuGet packages'; ^ NuGet restore; ^ -. '.\Functions.ps1'; ^ -$projectName = 'Appy.GitDb.Server'; ^ + +$invokeBuildVesion = '5.4.2'; ^ +$yamelDotNetVersion = '5.4.0'; ^ +$octopusToolsVersion = '3.5.4'; ^ + +Write-Host 'Installing Invoke-Build'; ^ +NuGet install Invoke-Build -Version $invokeBuildVesion -Source https://api.nuget.org/v3/index.json -OutputDirectory packages; ^ + +Write-Host 'Installing YamlDotNet'; ^ +NuGet install YamlDotNet -Version $yamelDotNetVersion -OutputDirectory packages; ^ + +Write-Host 'Installing OctopusTools'; ^ +NuGet install OctopusTools -Version $octopusToolsVersion -OutputDirectory packages; ^ + +. '.\functions.ps1' -yamelDotNetVersion $yamelDotNetVersion -octopusToolsVersion $octopusToolsVersion -artifactsPath artifacts; ^ $packagesPath = '.\packages'; ^ -$invokeBuildPath = Get-PackagePath Invoke-Build $projectName; ^ -& $invokeBuildPath\tools\Invoke-Build.ps1 %* -File Tasks.ps1; +$invokeBuildPath = '.\packages\Invoke-Build.' + $invokeBuildVesion; ^ +& $invokeBuildPath\tools\Invoke-Build.ps1 %* -File tasks.ps1; exit /b %ERRORLEVEL% + diff --git a/config.yml b/config.yml index 0922065..dcb14e4 100644 --- a/config.yml +++ b/config.yml @@ -9,22 +9,7 @@ # Valid project types and their process: # App => Any Windows, Web or Console app | Process: Clean, Compile, Pack, Push, Release, Deploy # Package => NuGet package | Process: Clean, Compile, Pack, Push -# VsTest => Project with Ms Tests | Process: Clean, Compile, Test # Xunit => Project with Xunit Tests | Process: Clean, Compile, Test -# StandaloneWebJob => Project with an Azure Webjob that is deployed independently | Process: Clean, Compile, Pack, Push, Release, Deploy -# configuration sample: -# : -# Type: StandaloneWebJob -# Config: -# RunMode: -# Embedded => Project with an Azure Webjob that is deployed in another project in the solution | Process: Clean, Compile -# Configuration sample: -# : -# Type: EmbeddedWebJob -# Config: -# Target: -# RunMode: - Appy.GitDb.Benchmark: Type: App Exclude: "pack, push, release, deploy" @@ -38,3 +23,5 @@ Appy.GitDb.Tests: Type: Xunit Appy.GitDb.Watcher: Type: Package +Appy.GitDb.NetCore.Server: + Type: App, AppPackage diff --git a/global.json b/global.json new file mode 100644 index 0000000..a917a78 --- /dev/null +++ b/global.json @@ -0,0 +1,8 @@ +{ + "projects": [ + "." + ], + "sdk": { + "version": "2.2.102" + } +} \ No newline at end of file diff --git a/tasks.ps1 b/tasks.ps1 index 1b89056..ef0252e 100644 --- a/tasks.ps1 +++ b/tasks.ps1 @@ -1,6 +1,6 @@ $script:config = "Release" -task Clean{ +task Clean { Clean-Folder $artifactsPath Clean-Folder TestResults Get-ProjectsForTask "Clean" | ForEach-Object { @@ -14,28 +14,17 @@ task Compile { Get-ProjectsForTask "Compile" | ForEach-Object { Compile-Project $_.Name } - - Get-ProjectsForTask "Compile" | - Where { $_.Type -eq "EmbeddedWebJob" } | - ForEach-Object { - Move-WebJob $_.Name $_.Config["Target"] $_.Config["RunMode"] - } } -task Test{ - Get-ProjectsForTask "Test" | - Where { $_.Type -eq "VsTest"} | - ForEach-Object { - Execute-VsTest $_.Name - } - +task Test { Get-ProjectsForTask "Test" | Where { $_.Type -eq "XUnit"} | ForEach-Object { Execute-XUnit $_.Name } } -task Pack{ + +task Pack { Get-ProjectsForTask "Pack" | ForEach-Object { if($_.Type -eq "Package"){ Pack-Project $_.Name @@ -45,21 +34,21 @@ task Pack{ } } -task Push{ +task Push { Get-ProjectsForTask "Push" | - Where { $_.Type -eq "Package"} | - ForEach-Object{ + Where { $_.Type -eq "Package" -Or $_.Type -eq "AppPackage"} | + ForEach-Object { Push-Package $_.Name $env:ylp_nugetPackageSource $env:ylp_nugetPackageSourceApiKey "409" } Get-ProjectsForTask "Push" | Where { $_.Type -ne "Package"} | - ForEach-Object{ + ForEach-Object { Push-Package $_.Name $env:ylp_octopusDeployPackageSource $env:ylp_octopusDeployApiKey "409" } } -task Release{ +task Release { Get-ProjectsForTask "Release" | ForEach-Object { Octo-CreateRelease $_.Name }