feat: implements first version

This commit is contained in:
Matthias Langhard
2021-10-31 07:44:49 +01:00
parent 915e23cf24
commit 69f872082d
25 changed files with 979 additions and 0 deletions

125
.gitignore vendored Normal file
View File

@@ -0,0 +1,125 @@
update-tag.sln.DotSettings.user
# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,jetbrains+all,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,jetbrains+all,visualstudiocode
### DotnetCore ###
# .NET Core build folders
bin/
obj/
# Common node modules locations
/node_modules
/wwwroot/node_modules
### JetBrains+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,jetbrains+all,visualstudiocode

106
src/Cli/AppRunner.cs Normal file
View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Application.Commands;
using Application.Queries;
using Cli.Models;
using MediatR;
using Spectre.Console;
using Version = Application.Models.Version;
namespace Cli
{
public class AppRunner
{
private readonly IMediator _mediator;
public AppRunner(IMediator mediator)
{
_mediator = mediator;
}
public async Task Run(string repoPath)
{
// Check if git dir
var services = new List<string>();
try
{
services = await _mediator.Send(new GetServicesFromGitRepo.Query(repoPath));
}
catch (LibGit2Sharp.RepositoryNotFoundException)
{
AnsiConsole.Markup("[red]Error:[/] Unable to extract Versions. Are we running inside a git repository?\n\n");
Environment.Exit(1);
}
var chosenService = "";
if (services.Count > 1)
{
chosenService = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("Which [green]service[/] is the new version for?")
.PageSize(10)
.MoreChoicesText("[grey](Move up and down to reveal more services)[/]")
.AddChoices(services));
}
var versionInfo = await _mediator.Send(new GetVersionInformationFromRepo.Query(repoPath, chosenService));
Selection selection;
if (versionInfo != null)
{
selection = AnsiConsole.Prompt(
new SelectionPrompt<Selection>()
.Title($"Select new version. (Current version is [green]{versionInfo.CurrentVersion}[/])")
.PageSize(10)
.AddChoices(
new Selection("rc ", versionInfo.NextMinorRcVersion),
new Selection("patch", versionInfo.NextPatchVersion),
new Selection("minor", versionInfo.NextMinorVersion),
new Selection("major", versionInfo.NextMajorVersion)
)
);
}
else
{
selection = AnsiConsole.Prompt(
new SelectionPrompt<Selection>()
.Title("[red]Error evaluating version from newest tag.[/]\nAdd new version tag **AND** push to origin?)")
.PageSize(10)
.AddChoices(
new Selection("yes", new Version(0, 1, 0)),
new Selection("yes", new Version(0, 1, 0, 1)),
new Selection("no", null)
)
);
}
if (selection.Version == null)
{
Environment.Exit(0);
}
try
{
await _mediator.Send(new AddTagToGitRepo.Command(repoPath, selection.Version.ToString()));
}
catch (Exception ex)
{
AnsiConsole.Markup("[red]Error:[/] Unable to write Tag to repository\n\n");
AnsiConsole.WriteException(ex);
Environment.Exit(1);
}
try
{
await _mediator.Send(new PushCommitsToRemote.Command(repoPath));
}
catch (Exception ex)
{
AnsiConsole.Markup("[red]Error:[/] Tag was written but unable to push to remote\n\n");
AnsiConsole.WriteException(ex);
Environment.Exit(1);
}
}
}
}

24
src/Cli/Cli.csproj Normal file
View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>Cli</RootNamespace>
<PackAsTool>true</PackAsTool>
<ToolCommandName>update-tag</ToolCommandName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="Spectre.Console" Version="0.42.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
namespace Cli
{
public static class DependencyInjection
{
public static IServiceCollection AddCli(this IServiceCollection services)
{
return services
.AddTransient<AppRunner>();
}
}
}

View File

@@ -0,0 +1,21 @@
using Semver;
namespace Cli.Extensions
{
public static class SemverParser
{
public static bool TryParse(string version, out SemVersion semverVersion)
{
try
{
semverVersion = SemVersion.Parse(version.TrimStart('v').TrimStart('V'));
return true;
}
catch
{
semverVersion = null;
return false;
}
}
}
}

View File

@@ -0,0 +1,21 @@
using Application.Models;
namespace Cli.Models
{
public class Selection
{
public Selection(string title, Version version)
{
Title = title;
Version = version;
}
private string Title { get; }
public Version Version { get; }
public override string ToString()
{
return Version == null ? Title : $"{Title}\t [green]{Version}[/]";
}
}
}

39
src/Cli/Program.cs Normal file
View File

@@ -0,0 +1,39 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Application;
using Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Cli
{
class Program
{
static async Task Main(string[] args)
{
using var host = CreateHostBuilder()
.Build();
var appRunner = host
.Services
.CreateScope()
.ServiceProvider
.GetRequiredService<AppRunner>();
await appRunner.Run(args.FirstOrDefault() ?? Environment.CurrentDirectory);
}
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices(
(_, services) =>
services
.AddCli()
.AddInfrastructure()
.AddCore()
);
}
}
}

View File

@@ -0,0 +1,34 @@
using Application.Interfaces;
using MediatR;
namespace Application.Commands
{
public class AddTagToGitRepo : RequestHandler<AddTagToGitRepo.Command>
{
private readonly IGitRepoWriteService _gitRepoWriteService;
public AddTagToGitRepo(IGitRepoWriteService gitRepoWriteService)
{
_gitRepoWriteService = gitRepoWriteService;
}
public class Command : IRequest
{
public string RepoPath { get; }
public string Tag { get; }
public Command(string repoPath, string tag)
{
RepoPath = repoPath;
Tag = tag;
}
}
protected override void Handle(Command request)
{
_gitRepoWriteService.AddTag(request.RepoPath, request.Tag);
}
}
}

View File

@@ -0,0 +1,31 @@
using Application.Interfaces;
using MediatR;
namespace Application.Commands
{
public class PushCommitsToRemote : RequestHandler<PushCommitsToRemote.Command>
{
private readonly IGitRepoWriteService _gitRepoWriteService;
public PushCommitsToRemote(IGitRepoWriteService gitRepoWriteService)
{
_gitRepoWriteService = gitRepoWriteService;
}
public class Command : IRequest
{
public string RepoPath { get; }
public Command(string repoPath)
{
RepoPath = repoPath;
}
}
protected override void Handle(Command request)
{
_gitRepoWriteService.Push(request.RepoPath);
}
}
}

14
src/Core/Core.csproj Normal file
View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>Application</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;
namespace Application
{
public static class DependencyInjection
{
public static IServiceCollection AddCore(this IServiceCollection services)
{
return services
.AddMediatR(typeof(DependencyInjection));
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using Application.Models;
namespace Application.Interfaces
{
public interface IGitRepoReadService
{
public IEnumerable<Version> GetAllVersions(string repoPath);
}
}

View File

@@ -0,0 +1,8 @@
namespace Application.Interfaces
{
public interface IGitRepoWriteService
{
void AddTag(string repoPath, string tag);
void Push(string repoPath);
}
}

136
src/Core/Models/Version.cs Normal file
View File

@@ -0,0 +1,136 @@
using System.Text;
using System.Text.RegularExpressions;
namespace Application.Models
{
/// <summary>
/// Construct:
/// "Major.Minor.Patch-Rc+Service"
///
/// Example:
/// "0.1.4-RC.4+ErpNext"
/// </summary>
public class Version
{
public Version(int major, int minor, int patch)
{
Major = major;
Minor = minor;
Patch = patch;
}
public Version(int major, int minor, int patch, int rc)
{
Major = major;
Minor = minor;
Patch = patch;
Rc = rc;
}
public Version(int major, int minor, int patch, string rc, string service)
{
Major = major;
Minor = minor;
Patch = patch;
Rc = ExtractNumberFromRcString(rc);
Service = service ?? "";
}
private static int? ExtractNumberFromRcString(string rc)
{
var resultString = Regex.Match(rc, @"\d+").Value;
return int.TryParse(resultString, out var rcNumber) ? rcNumber : null;
}
public int Major { get; private set; }
public int Minor { get; private set; }
public int Patch { get; private set; }
public int? Rc { get; private set; }
public string Service { get; }
public override string ToString()
{
var sb = new StringBuilder();
sb.Append(Major);
sb.Append('.');
sb.Append(Minor);
sb.Append('.');
sb.Append(Patch);
if (Rc != null)
{
sb.Append('-');
sb.Append("RC.");
sb.Append(Rc);
}
if (Service != null && Service.Length > 0)
{
sb.Append('+');
sb.Append(Service);
}
return sb.ToString();
}
public Version NextMajor()
{
var nextVersion = new Version(Major, Minor, Patch, Rc.ToString(), Service);
nextVersion.BumpMajor();
return nextVersion;
}
public Version NextMinor()
{
var nextVersion = new Version(Major, Minor, Patch, Rc.ToString(), Service);
nextVersion.BumpMinor();
return nextVersion;
}
public Version NextPatch()
{
var nextVersion = new Version(Major, Minor, Patch, Rc.ToString(), Service);
nextVersion.BumpPatch();
return nextVersion;
}
public Version NextRc()
{
var nextVersion = new Version(Major, Minor, Patch, Rc.ToString(), Service);
nextVersion.BumpRc();
return nextVersion;
}
public void BumpMajor()
{
Major++;
Minor = 0;
Patch = 0;
Rc = null;
}
public void BumpMinor()
{
Minor++;
Patch = 0;
Rc = null;
}
public void BumpPatch()
{
Patch++;
Rc = null;
}
public void BumpRc()
{
if (Rc == null)
{
BumpMinor();
}
Rc = Rc == null ? 0 : Rc + 1;
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Application.Models
{
public class VersionInformation
{
public VersionInformation(Version currentVersion)
{
CurrentVersion = currentVersion;
NextMajorVersion = currentVersion.NextMajor();
NextMinorVersion = currentVersion.NextMinor();
NextPatchVersion = currentVersion.NextPatch();
NextMinorRcVersion = currentVersion.NextRc();
}
public Version CurrentVersion { get; }
public Version NextMajorVersion { get; }
public Version NextMinorVersion { get; }
public Version NextPatchVersion { get; }
public Version NextMinorRcVersion { get; }
}
}

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
using Application.Interfaces;
using MediatR;
namespace Application.Queries
{
public class GetServicesFromGitRepo : RequestHandler<GetServicesFromGitRepo.Query, List<string>>
{
public class Query : IRequest<List<string>>
{
public Query(string repositoryPath)
{
RepositoryPath = repositoryPath;
}
public string RepositoryPath { get; }
}
private readonly IGitRepoReadService _gitRepoReadService;
public GetServicesFromGitRepo(IGitRepoReadService gitRepoReadService)
{
_gitRepoReadService = gitRepoReadService;
}
protected override List<string> Handle(Query request)
{
return _gitRepoReadService
.GetAllVersions(request.RepositoryPath)
.Select(v => v.Service)
.Distinct()
.OrderBy(v => v)
.ToList();
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Linq;
using Application.Interfaces;
using Application.Models;
using MediatR;
namespace Application.Queries
{
public class GetVersionInformationFromRepo : RequestHandler<GetVersionInformationFromRepo.Query, VersionInformation>
{
public class Query : IRequest<VersionInformation>
{
public Query(string repositoryPath, string onlyForService = "")
{
RepositoryPath = repositoryPath;
OnlyForService = onlyForService;
}
public string RepositoryPath { get; }
public string OnlyForService { get; }
}
private readonly IGitRepoReadService _gitRepoReadService;
public GetVersionInformationFromRepo(IGitRepoReadService gitRepoReadService)
{
_gitRepoReadService = gitRepoReadService;
}
protected override VersionInformation Handle(Query request)
{
var versions = _gitRepoReadService
.GetAllVersions(request.RepositoryPath);
if (!string.IsNullOrWhiteSpace(request.OnlyForService))
{
versions = versions
.Where(v => v.Service.Equals(request.OnlyForService, StringComparison.InvariantCultureIgnoreCase));
}
var currentVersion = versions
.OrderByDescending(v => v.Major)
.ThenByDescending(v => v.Minor)
.ThenByDescending(v => v.Patch)
.ThenByDescending(v => v.Rc)
.FirstOrDefault();
return currentVersion == null ? null : new VersionInformation(currentVersion);
}
}
}

7
src/Domain/Domain.csproj Normal file
View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,16 @@
using Application.Interfaces;
using Infrastructure.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Infrastructure
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
return services
.AddSingleton<IGitRepoReadService, GitRepoReadService>()
.AddSingleton<IGitRepoWriteService, GitRepoWriteService>();
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="LibGit2Sharp" Version="0.26.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="semver" Version="2.0.6" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using Application.Interfaces;
using LibGit2Sharp;
using Semver;
using Version = Application.Models.Version;
namespace Infrastructure.Services
{
public class GitRepoReadService : IGitRepoReadService
{
public IEnumerable<Version> GetAllVersions(string repoPath)
{
using var repo = new Repository(repoPath);
foreach (var tag in repo.Tags)
{
if (TryParse(tag.FriendlyName, out var semver))
{
yield return new Version(semver.Major, semver.Minor, semver.Patch, semver.Prerelease, semver.Build);
}
}
}
private static bool TryParse(string version, out SemVersion semverVersion)
{
try
{
semverVersion = SemVersion.Parse(version.TrimStart('v').TrimStart('V'));
return true;
}
catch
{
semverVersion = null;
return false;
}
}
}
}

View File

@@ -0,0 +1,20 @@
using Application.Interfaces;
using LibGit2Sharp;
namespace Infrastructure.Services
{
public class GitRepoWriteService : IGitRepoWriteService
{
public void AddTag(string repoPath, string tag)
{
using var repo = new Repository(repoPath);
repo.ApplyTag(tag);
}
public void Push(string repoPath)
{
using var repo = new Repository(repoPath);
repo.Network.Push(repo.Head);
}
}
}

View File

@@ -0,0 +1,65 @@
using Xunit;
using Version = Application.Models.Version;
namespace UpdateTag.Tests
{
public class VersionTests
{
[Theory]
[InlineData(1, 0, 0, "", "", "2.0.0")]
[InlineData(1, 1, 0, "", "", "2.0.0")]
[InlineData(1, 1, 1, "", "", "2.0.0")]
[InlineData(1, 1, 1, "RC.4", "", "2.0.0")]
[InlineData(1, 1, 1, "RC.4", "ErpNext", "2.0.0+ErpNext")]
public void BumpMajor(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service);
version.BumpMajor();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 0, 0, "", "", "1.1.0")]
[InlineData(1, 1, 0, "", "", "1.2.0")]
[InlineData(1, 1, 1, "", "", "1.2.0")]
[InlineData(1, 1, 1, "RC.4", "", "1.2.0")]
[InlineData(1, 1, 1, "RC.4", "ErpNext", "1.2.0+ErpNext")]
public void BumpMinor(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service);
version.BumpMinor();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 0, 0, "", "", "1.0.1")]
[InlineData(1, 1, 0, "", "", "1.1.1")]
[InlineData(1, 1, 1, "", "", "1.1.2")]
[InlineData(1, 1, 1, "RC.4", "", "1.1.2")]
[InlineData(1, 1, 1, "RC.4", "ErpNext", "1.1.2+ErpNext")]
public void BumpPatch(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service);
version.BumpPatch();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 0, 0, "", "", "1.1.0-RC.0")]
[InlineData(1, 1, 0, "", "", "1.2.0-RC.0")]
[InlineData(1, 1, 1, "", "", "1.2.0-RC.0")]
[InlineData(1, 1, 1, "RC.4", "", "1.1.1-RC.5")]
[InlineData(1, 1, 1, "RC.4", "ErpNext", "1.1.1-RC.5+ErpNext")]
public void BumpRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service);
version.BumpRc();
Assert.Equal(expected, version.ToString());
}
}
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>UpdateTag.Tests</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Core\Core.csproj" />
</ItemGroup>
</Project>

86
update-tag.sln Normal file
View File

@@ -0,0 +1,86 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{50940826-54B4-446A-B27A-8B4693F4697A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cli", "src\Cli\Cli.csproj", "{91DAAA93-C852-4E09-AA5A-64B9D02C448D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{476F6D7F-1C9D-49EE-ACD1-49C13721D930}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "update-tag.tests", "tests\update-tag.tests\update-tag.tests.csproj", "{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{35A56961-3C0B-466E-8CE8-FF0660EED834}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|x64.ActiveCfg = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|x64.Build.0 = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|x86.ActiveCfg = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Debug|x86.Build.0 = Debug|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|Any CPU.Build.0 = Release|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|x64.ActiveCfg = Release|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|x64.Build.0 = Release|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|x86.ActiveCfg = Release|Any CPU
{91DAAA93-C852-4E09-AA5A-64B9D02C448D}.Release|x86.Build.0 = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|x64.ActiveCfg = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|x64.Build.0 = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|x86.ActiveCfg = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Debug|x86.Build.0 = Debug|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|Any CPU.Build.0 = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|x64.ActiveCfg = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|x64.Build.0 = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|x86.ActiveCfg = Release|Any CPU
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B}.Release|x86.Build.0 = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|x64.ActiveCfg = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|x64.Build.0 = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|x86.ActiveCfg = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Debug|x86.Build.0 = Debug|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|Any CPU.Build.0 = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|x64.ActiveCfg = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|x64.Build.0 = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|x86.ActiveCfg = Release|Any CPU
{35A56961-3C0B-466E-8CE8-FF0660EED834}.Release|x86.Build.0 = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|x64.ActiveCfg = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|x64.Build.0 = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|x86.ActiveCfg = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Debug|x86.Build.0 = Debug|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|Any CPU.Build.0 = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|x64.ActiveCfg = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|x64.Build.0 = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|x86.ActiveCfg = Release|Any CPU
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{91DAAA93-C852-4E09-AA5A-64B9D02C448D} = {50940826-54B4-446A-B27A-8B4693F4697A}
{8C8A5F65-5D2C-45D2-85A4-B0EF38E6D65B} = {476F6D7F-1C9D-49EE-ACD1-49C13721D930}
{35A56961-3C0B-466E-8CE8-FF0660EED834} = {50940826-54B4-446A-B27A-8B4693F4697A}
{7C947404-498C-40F4-A9AA-8ABCBAFD2DB6} = {50940826-54B4-446A-B27A-8B4693F4697A}
EndGlobalSection
EndGlobal