From 69f872082dcdbf1b903abbeba23a219dde6b4450 Mon Sep 17 00:00:00 2001 From: Matthias Langhard Date: Sun, 31 Oct 2021 07:44:49 +0100 Subject: [PATCH] feat: implements first version --- .gitignore | 125 ++++++++++++++++ src/Cli/AppRunner.cs | 106 ++++++++++++++ src/Cli/Cli.csproj | 24 ++++ src/Cli/DependencyInjection.cs | 13 ++ src/Cli/Extensions/Semver.cs | 21 +++ src/Cli/Models/Selection.cs | 21 +++ src/Cli/Program.cs | 39 +++++ src/Core/Commands/AddTagToGitRepo.cs | 34 +++++ src/Core/Commands/PushCommitsToRemote.cs | 31 ++++ src/Core/Core.csproj | 14 ++ src/Core/DependencyInjection.cs | 14 ++ src/Core/Interfaces/IGitRepoReadService.cs | 10 ++ src/Core/Interfaces/IGitRepoWriteService.cs | 8 ++ src/Core/Models/Version.cs | 136 ++++++++++++++++++ src/Core/Models/VersionInformation.cs | 20 +++ src/Core/Queries/GetServicesFromGitRepo.cs | 37 +++++ .../Queries/GetVersionInformationFromRepo.cs | 51 +++++++ src/Domain/Domain.csproj | 7 + src/Infrastructure/DependencyInjection.cs | 16 +++ src/Infrastructure/Infrastructure.csproj | 17 +++ .../Services/GitRepoReadService.cs | 37 +++++ .../Services/GitRepoWriteService.cs | 20 +++ tests/update-tag.tests/VersionTests.cs | 65 +++++++++ .../update-tag.tests/update-tag.tests.csproj | 27 ++++ update-tag.sln | 86 +++++++++++ 25 files changed, 979 insertions(+) create mode 100644 .gitignore create mode 100644 src/Cli/AppRunner.cs create mode 100644 src/Cli/Cli.csproj create mode 100644 src/Cli/DependencyInjection.cs create mode 100644 src/Cli/Extensions/Semver.cs create mode 100644 src/Cli/Models/Selection.cs create mode 100644 src/Cli/Program.cs create mode 100644 src/Core/Commands/AddTagToGitRepo.cs create mode 100644 src/Core/Commands/PushCommitsToRemote.cs create mode 100644 src/Core/Core.csproj create mode 100644 src/Core/DependencyInjection.cs create mode 100644 src/Core/Interfaces/IGitRepoReadService.cs create mode 100644 src/Core/Interfaces/IGitRepoWriteService.cs create mode 100644 src/Core/Models/Version.cs create mode 100644 src/Core/Models/VersionInformation.cs create mode 100644 src/Core/Queries/GetServicesFromGitRepo.cs create mode 100644 src/Core/Queries/GetVersionInformationFromRepo.cs create mode 100644 src/Domain/Domain.csproj create mode 100644 src/Infrastructure/DependencyInjection.cs create mode 100644 src/Infrastructure/Infrastructure.csproj create mode 100644 src/Infrastructure/Services/GitRepoReadService.cs create mode 100644 src/Infrastructure/Services/GitRepoWriteService.cs create mode 100644 tests/update-tag.tests/VersionTests.cs create mode 100644 tests/update-tag.tests/update-tag.tests.csproj create mode 100644 update-tag.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6e7cfb --- /dev/null +++ b/.gitignore @@ -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 diff --git a/src/Cli/AppRunner.cs b/src/Cli/AppRunner.cs new file mode 100644 index 0000000..72500d6 --- /dev/null +++ b/src/Cli/AppRunner.cs @@ -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(); + 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() + .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() + .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() + .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); + } + } + } +} \ No newline at end of file diff --git a/src/Cli/Cli.csproj b/src/Cli/Cli.csproj new file mode 100644 index 0000000..f1ed9af --- /dev/null +++ b/src/Cli/Cli.csproj @@ -0,0 +1,24 @@ + + + + Exe + net5.0 + Cli + true + update-tag + + + + + + + + + + + + + + + + diff --git a/src/Cli/DependencyInjection.cs b/src/Cli/DependencyInjection.cs new file mode 100644 index 0000000..fb1cd54 --- /dev/null +++ b/src/Cli/DependencyInjection.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Cli +{ + public static class DependencyInjection + { + public static IServiceCollection AddCli(this IServiceCollection services) + { + return services + .AddTransient(); + } + } +} \ No newline at end of file diff --git a/src/Cli/Extensions/Semver.cs b/src/Cli/Extensions/Semver.cs new file mode 100644 index 0000000..fda06f4 --- /dev/null +++ b/src/Cli/Extensions/Semver.cs @@ -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; + } + } + } +} \ No newline at end of file diff --git a/src/Cli/Models/Selection.cs b/src/Cli/Models/Selection.cs new file mode 100644 index 0000000..20a51d8 --- /dev/null +++ b/src/Cli/Models/Selection.cs @@ -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}[/]"; + } + } +} \ No newline at end of file diff --git a/src/Cli/Program.cs b/src/Cli/Program.cs new file mode 100644 index 0000000..869d98b --- /dev/null +++ b/src/Cli/Program.cs @@ -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(); + + await appRunner.Run(args.FirstOrDefault() ?? Environment.CurrentDirectory); + } + + private static IHostBuilder CreateHostBuilder() + { + return Host.CreateDefaultBuilder() + .ConfigureServices( + (_, services) => + services + .AddCli() + .AddInfrastructure() + .AddCore() + ); + } + } +} \ No newline at end of file diff --git a/src/Core/Commands/AddTagToGitRepo.cs b/src/Core/Commands/AddTagToGitRepo.cs new file mode 100644 index 0000000..a4a1c3e --- /dev/null +++ b/src/Core/Commands/AddTagToGitRepo.cs @@ -0,0 +1,34 @@ +using Application.Interfaces; +using MediatR; + +namespace Application.Commands +{ + public class AddTagToGitRepo : RequestHandler + { + 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); + } + } +} \ No newline at end of file diff --git a/src/Core/Commands/PushCommitsToRemote.cs b/src/Core/Commands/PushCommitsToRemote.cs new file mode 100644 index 0000000..c21890d --- /dev/null +++ b/src/Core/Commands/PushCommitsToRemote.cs @@ -0,0 +1,31 @@ +using Application.Interfaces; +using MediatR; + +namespace Application.Commands +{ + public class PushCommitsToRemote : RequestHandler + { + 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); + } + } +} \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj new file mode 100644 index 0000000..db7c1c7 --- /dev/null +++ b/src/Core/Core.csproj @@ -0,0 +1,14 @@ + + + + net5.0 + Application + + + + + + + + + diff --git a/src/Core/DependencyInjection.cs b/src/Core/DependencyInjection.cs new file mode 100644 index 0000000..8205185 --- /dev/null +++ b/src/Core/DependencyInjection.cs @@ -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)); + } + } +} \ No newline at end of file diff --git a/src/Core/Interfaces/IGitRepoReadService.cs b/src/Core/Interfaces/IGitRepoReadService.cs new file mode 100644 index 0000000..b02dc8e --- /dev/null +++ b/src/Core/Interfaces/IGitRepoReadService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Application.Models; + +namespace Application.Interfaces +{ + public interface IGitRepoReadService + { + public IEnumerable GetAllVersions(string repoPath); + } +} \ No newline at end of file diff --git a/src/Core/Interfaces/IGitRepoWriteService.cs b/src/Core/Interfaces/IGitRepoWriteService.cs new file mode 100644 index 0000000..238e1f0 --- /dev/null +++ b/src/Core/Interfaces/IGitRepoWriteService.cs @@ -0,0 +1,8 @@ +namespace Application.Interfaces +{ + public interface IGitRepoWriteService + { + void AddTag(string repoPath, string tag); + void Push(string repoPath); + } +} \ No newline at end of file diff --git a/src/Core/Models/Version.cs b/src/Core/Models/Version.cs new file mode 100644 index 0000000..4ff53f5 --- /dev/null +++ b/src/Core/Models/Version.cs @@ -0,0 +1,136 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace Application.Models +{ + /// + /// Construct: + /// "Major.Minor.Patch-Rc+Service" + /// + /// Example: + /// "0.1.4-RC.4+ErpNext" + /// + 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; + } + } +} \ No newline at end of file diff --git a/src/Core/Models/VersionInformation.cs b/src/Core/Models/VersionInformation.cs new file mode 100644 index 0000000..2296d33 --- /dev/null +++ b/src/Core/Models/VersionInformation.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Core/Queries/GetServicesFromGitRepo.cs b/src/Core/Queries/GetServicesFromGitRepo.cs new file mode 100644 index 0000000..de57065 --- /dev/null +++ b/src/Core/Queries/GetServicesFromGitRepo.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; +using Application.Interfaces; +using MediatR; + +namespace Application.Queries +{ + public class GetServicesFromGitRepo : RequestHandler> + { + public class Query : IRequest> + { + public Query(string repositoryPath) + { + RepositoryPath = repositoryPath; + } + + public string RepositoryPath { get; } + } + + private readonly IGitRepoReadService _gitRepoReadService; + + public GetServicesFromGitRepo(IGitRepoReadService gitRepoReadService) + { + _gitRepoReadService = gitRepoReadService; + } + + protected override List Handle(Query request) + { + return _gitRepoReadService + .GetAllVersions(request.RepositoryPath) + .Select(v => v.Service) + .Distinct() + .OrderBy(v => v) + .ToList(); + } + } +} \ No newline at end of file diff --git a/src/Core/Queries/GetVersionInformationFromRepo.cs b/src/Core/Queries/GetVersionInformationFromRepo.cs new file mode 100644 index 0000000..beb5d95 --- /dev/null +++ b/src/Core/Queries/GetVersionInformationFromRepo.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using Application.Interfaces; +using Application.Models; +using MediatR; + +namespace Application.Queries +{ + public class GetVersionInformationFromRepo : RequestHandler + { + public class Query : IRequest + { + 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); + } + } +} \ No newline at end of file diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj new file mode 100644 index 0000000..cbfa581 --- /dev/null +++ b/src/Domain/Domain.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/src/Infrastructure/DependencyInjection.cs b/src/Infrastructure/DependencyInjection.cs new file mode 100644 index 0000000..9aa5c6b --- /dev/null +++ b/src/Infrastructure/DependencyInjection.cs @@ -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() + .AddSingleton(); + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..6435cd8 --- /dev/null +++ b/src/Infrastructure/Infrastructure.csproj @@ -0,0 +1,17 @@ + + + + net5.0 + + + + + + + + + + + + + diff --git a/src/Infrastructure/Services/GitRepoReadService.cs b/src/Infrastructure/Services/GitRepoReadService.cs new file mode 100644 index 0000000..2152576 --- /dev/null +++ b/src/Infrastructure/Services/GitRepoReadService.cs @@ -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 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; + } + } + } +} \ No newline at end of file diff --git a/src/Infrastructure/Services/GitRepoWriteService.cs b/src/Infrastructure/Services/GitRepoWriteService.cs new file mode 100644 index 0000000..60012f8 --- /dev/null +++ b/src/Infrastructure/Services/GitRepoWriteService.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/tests/update-tag.tests/VersionTests.cs b/tests/update-tag.tests/VersionTests.cs new file mode 100644 index 0000000..95c5ae3 --- /dev/null +++ b/tests/update-tag.tests/VersionTests.cs @@ -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()); + } + } +} \ No newline at end of file diff --git a/tests/update-tag.tests/update-tag.tests.csproj b/tests/update-tag.tests/update-tag.tests.csproj new file mode 100644 index 0000000..ff64426 --- /dev/null +++ b/tests/update-tag.tests/update-tag.tests.csproj @@ -0,0 +1,27 @@ + + + + net5.0 + UpdateTag.Tests + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/update-tag.sln b/update-tag.sln new file mode 100644 index 0000000..622615a --- /dev/null +++ b/update-tag.sln @@ -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