12 Commits

Author SHA1 Message Date
Matthias Langhard
8a8b16c655 feat: implements better commandline args parsing with '--version' and '--help' outputs 2021-11-04 21:13:40 +01:00
Matthias Langhard
c45ba92f41 feat: adds a 'v' prefix to the new version based on wether the newest last version had one 2021-11-04 17:00:49 +01:00
Matthias Langhard
5d2c1d8f63 feat: bumps version number 2021-11-04 13:39:10 +01:00
Matthias Langhard
aeae68b5c4 feat: allows pipeline to run with 'v' prefix 2021-11-04 13:38:51 +01:00
Matthias Langhard
3acdb3e38c fix: fixes missing 'v' prefix. Sorry mäge, force of habit :P 2021-11-04 11:50:02 +01:00
Matthias Langhard
350edb9763 fix: fixes bug in identifying neweset version. Adds a test for it 2021-11-04 10:53:30 +01:00
Matthias Langhard
9c313526e7 feat: implements option to add a service-name when initializing first tag 2021-11-04 08:22:09 +01:00
Matthias Langhard
bbf09be431 feat: improving push-functionality 2021-11-03 08:35:48 +01:00
Matthias Langhard
808f0e4564 chore: improves next version selection 2021-11-03 08:24:17 +01:00
Matthias Langhard
0ca934cfa8 chore: fixing package project-url 2021-11-03 08:11:31 +01:00
Matthias Langhard
189cbe0ae8 chore: updating tool to 0.1.4 2021-11-03 08:03:48 +01:00
Matthias Langhard
0cdd2aa4be chore: adds update command to README.md 2021-11-03 08:02:44 +01:00
18 changed files with 298 additions and 54 deletions

View File

@@ -10,7 +10,7 @@ running tests for tag:
publish to nuget:
only:
- /^\d*.\d*.\d*$/ # gets triggered if the commit tag is in the form n.n.n where n is any number
- /^v+\d*.\d*.\d*$/ # gets triggered if the commit tag is in the form n.n.n where n is any number
image: mcr.microsoft.com/dotnet/sdk:5.0
stage: publish
script:

View File

@@ -3,6 +3,13 @@
Updates the tag of a repo to the next chosen version according the semver symantic.
## Install
```
dotnet tool install --global Novaloop.UpdateTag
```
## Update
```
dotnet tool update --global Novaloop.UpdateTag
```

View File

@@ -26,7 +26,8 @@ namespace Cli
var chosenService = await ChooseService(repoBasePath);
var selection = await SelectVersion(repoBasePath, chosenService);
await AddVersionTagToRepo(repoBasePath, selection.Version.ToString());
await PushToRemote(repoBasePath);
await PushTagsToRemote(repoBasePath);
await PushCommitsToRemote(repoBasePath);
}
private async Task<string> GetRepoBasePath(string workingDir)
@@ -97,11 +98,20 @@ namespace Cli
.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("yes", new Version(0, 1, 0, true)),
new Selection("yes", new Version(0, 1, 0, 1, true)),
new Selection("no", null)
)
);
var serviceName = AnsiConsole.Prompt(
new TextPrompt<string>("[grey][[Optional]][/] Enter [green]service name[/]:")
.AllowEmpty()
);
if (!string.IsNullOrWhiteSpace(serviceName))
{
selection.Version.SetService(serviceName.Trim());
}
}
if (selection.Version == null)
@@ -112,11 +122,27 @@ namespace Cli
return selection;
}
private async Task PushToRemote(string workingDir)
private async Task PushTagsToRemote(string workingDir)
{
try
{
await _mediator.Send(new PushCommitsToRemote.Command(workingDir));
var output = await _mediator.Send(new PushTagsToRemote.Command(workingDir));
AnsiConsole.Write(output);
}
catch (Exception ex)
{
AnsiConsole.Markup("[red]Error:[/] Tag was written but unable to push to remote\n\n");
AnsiConsole.WriteException(ex);
Environment.Exit(1);
}
}
private async Task PushCommitsToRemote(string workingDir)
{
try
{
var output = await _mediator.Send(new PushCommitsToRemote.Command(workingDir));
AnsiConsole.Write(output);
}
catch (Exception ex)
{

View File

@@ -6,18 +6,20 @@
<RootNamespace>Cli</RootNamespace>
<PackAsTool>true</PackAsTool>
<ToolCommandName>update-tag</ToolCommandName>
<AssemblyTitle>update-tag</AssemblyTitle>
<PackageId>Novaloop.UpdateTag</PackageId>
<title>Updates the tag of a repo to the next chosen version according the semver symantic.</title>
<PackageTags>semver;update-tag;tag;git</PackageTags>
<Version>0.1.2</Version>
<Version>0.5.0</Version>
<Authors>Matthias Langhard</Authors>
<Company>Novaloop AG</Company>
<PackageProjectUrl>https://gitlab.com/novaloop-oss/novaloop.updatetag</PackageProjectUrl>
<PackageProjectUrl>https://gitlab.com/novaloop-oss/novaloop.update-tag</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0"/>
<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"/>

View File

@@ -0,0 +1,10 @@
using CommandLine;
namespace Cli.Models
{
public class CliParams
{
[Option('r', "repository-path", Required = false, HelpText = "Run update-tag on a git repository other than the current directory.")]
public string RepositoryPath { get; set; }
}
}

View File

@@ -1,7 +1,8 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Application;
using Cli.Models;
using CommandLine;
using Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -21,9 +22,14 @@ namespace Cli
.ServiceProvider
.GetRequiredService<AppRunner>();
await appRunner.Run(args.FirstOrDefault() ?? Environment.CurrentDirectory);
await Parser.Default
.ParseArguments<CliParams>(args)
.WithParsedAsync(
async options => { await appRunner.Run(options.RepositoryPath ?? Environment.CurrentDirectory); }
);
}
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()

View File

@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
namespace Application.Interfaces
@@ -5,6 +6,7 @@ namespace Application.Interfaces
public interface IGitRepoWriteService
{
void AddTag(string repoPath, string tag);
Task Push(string repoPath);
Task<string> PushTags(string repoPath, CancellationToken cancellationToken);
Task<string> Push(string repoPath, CancellationToken ct);
}
}

View File

@@ -13,37 +13,41 @@ namespace Application.Models
/// </summary>
public class Version
{
public Version(int major, int minor, int patch)
public Version(int major, int minor, int patch, bool hasVPrefix)
{
Major = major;
Minor = minor;
Patch = patch;
_hasVPrefix = hasVPrefix;
}
public Version(int major, int minor, int patch, int? rc)
public Version(int major, int minor, int patch, int? rc, bool hasVPrefix)
{
Major = major;
Minor = minor;
Patch = patch;
Rc = rc;
_hasVPrefix = hasVPrefix;
}
public Version(int major, int minor, int patch, string rc, string service)
public Version(int major, int minor, int patch, string rc, string service, bool hasVPrefix)
{
Major = major;
Minor = minor;
Patch = patch;
Rc = rc == null ? null : ExtractNumberFromRcString(rc);
Service = service ?? "";
_hasVPrefix = hasVPrefix;
}
public Version(int major, int minor, int patch, int? rc, string service)
public Version(int major, int minor, int patch, int? rc, string service, bool hasVPrefix)
{
Major = major;
Minor = minor;
Patch = patch;
Rc = rc;
Service = service ?? "";
_hasVPrefix = hasVPrefix;
}
private static int? ExtractNumberFromRcString(string rc)
@@ -57,12 +61,17 @@ namespace Application.Models
public int Minor { get; private set; }
public int Patch { get; private set; }
public int? Rc { get; private set; }
public string Service { get; }
public string Service { get; private set; }
private readonly bool _hasVPrefix;
public override string ToString()
{
var sb = new StringBuilder();
if (_hasVPrefix)
{
sb.Append('v');
}
sb.Append(Major);
sb.Append('.');
sb.Append(Minor);
@@ -87,7 +96,7 @@ namespace Application.Models
private Version Copy()
{
return new Version(Major, Minor, Patch, Rc, Service);
return new Version(Major, Minor, Patch, Rc, Service, _hasVPrefix);
}
public Version NextMajor()
@@ -141,7 +150,7 @@ namespace Application.Models
return nextVersion;
}
public Version CreateRc()
public Version CreatePatchRc()
{
if (Rc != null)
{
@@ -149,6 +158,36 @@ namespace Application.Models
}
var nextVersion = Copy();
nextVersion.Patch++;
nextVersion.Rc = 0;
return nextVersion;
}
public Version CreateMinorRc()
{
if (Rc != null)
{
throw new ArgumentException("Cannot create RC. Already an RC.");
}
var nextVersion = Copy();
nextVersion.Minor++;
nextVersion.Patch = 0;
nextVersion.Rc = 0;
return nextVersion;
}
public Version CreateMajorRc()
{
if (Rc != null)
{
throw new ArgumentException("Cannot create RC. Already an RC.");
}
var nextVersion = Copy();
nextVersion.Major++;
nextVersion.Minor = 0;
nextVersion.Patch = 0;
nextVersion.Rc = 0;
return nextVersion;
}
@@ -160,7 +199,7 @@ namespace Application.Models
throw new ArgumentException("Cannot release RC. Not an RC.");
}
var nextVersion = new Version(Major, Minor, Patch, (string)null, Service);
var nextVersion = new Version(Major, Minor, Patch, (string)null, Service, _hasVPrefix);
return nextVersion;
}
@@ -168,5 +207,10 @@ namespace Application.Models
{
return Rc != null;
}
public void SetService(string service)
{
Service = service;
}
}
}

View File

@@ -14,10 +14,12 @@ namespace Application.Models
}
else
{
NextVersions.Add(new NextVersion("rc", currentVersion.CreateRc()));
NextVersions.Add(new NextVersion("patch", currentVersion.NextPatch()));
NextVersions.Add(new NextVersion("minor", currentVersion.NextMinor()));
NextVersions.Add(new NextVersion("major", currentVersion.NextMajor()));
NextVersions.Add(new NextVersion("patch-rc", currentVersion.CreatePatchRc()));
NextVersions.Add(new NextVersion("minor-rc", currentVersion.CreateMinorRc()));
NextVersions.Add(new NextVersion("minor-rc", currentVersion.CreateMajorRc()));
NextVersions.Add(new NextVersion("patch ", currentVersion.NextPatch()));
NextVersions.Add(new NextVersion("minor ", currentVersion.NextMinor()));
NextVersions.Add(new NextVersion("major ", currentVersion.NextMajor()));
}
}

View File

@@ -1,12 +1,14 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Application.Interfaces;
using Application.Models;
using MediatR;
namespace Application.Queries
{
public class GetVersionInformationFromRepo : RequestHandler<GetVersionInformationFromRepo.Query, VersionInformation>
public class GetVersionInformationFromRepo : IRequestHandler<GetVersionInformationFromRepo.Query, VersionInformation>
{
public class Query : IRequest<VersionInformation>
{
@@ -27,7 +29,7 @@ namespace Application.Queries
_gitRepoReadService = gitRepoReadService;
}
protected override VersionInformation Handle(Query request)
public async Task<VersionInformation> Handle(Query request, CancellationToken cancellationToken)
{
var versions = _gitRepoReadService
.GetAllVersions(request.RepositoryPath);
@@ -42,10 +44,11 @@ namespace Application.Queries
.OrderByDescending(v => v.Major)
.ThenByDescending(v => v.Minor)
.ThenByDescending(v => v.Patch)
.ThenByDescending(v => v.Rc == null)
.ThenByDescending(v => v.Rc)
.FirstOrDefault();
return currentVersion == null ? null : new VersionInformation(currentVersion);
return await Task.FromResult(currentVersion == null ? null : new VersionInformation(currentVersion));
}
}
}

View File

@@ -3,9 +3,9 @@ using System.Threading.Tasks;
using Application.Interfaces;
using MediatR;
namespace Application.Commands
namespace Application.Queries
{
public class PushCommitsToRemote : AsyncRequestHandler<PushCommitsToRemote.Command>
public class PushCommitsToRemote : IRequestHandler<PushCommitsToRemote.Command, string>
{
private readonly IGitRepoWriteService _gitRepoWriteService;
@@ -14,7 +14,7 @@ namespace Application.Commands
_gitRepoWriteService = gitRepoWriteService;
}
public class Command : IRequest
public class Command : IRequest<string>
{
public string RepoPath { get; }
@@ -24,9 +24,10 @@ namespace Application.Commands
}
}
protected override async Task Handle(Command request, CancellationToken cancellationToken)
public async Task<string> Handle(Command request, CancellationToken ct)
{
await _gitRepoWriteService.Push(request.RepoPath);
return await _gitRepoWriteService.Push(request.RepoPath, ct);
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Threading;
using System.Threading.Tasks;
using Application.Interfaces;
using MediatR;
namespace Application.Queries
{
public class PushTagsToRemote : IRequestHandler<PushTagsToRemote.Command, string>
{
private readonly IGitRepoWriteService _gitRepoWriteService;
public PushTagsToRemote(IGitRepoWriteService gitRepoWriteService)
{
_gitRepoWriteService = gitRepoWriteService;
}
public class Command : IRequest<string>
{
public string RepoPath { get; }
public Command(string repoPath)
{
RepoPath = repoPath;
}
}
public async Task<string> Handle(Command request, CancellationToken ct)
{
return await _gitRepoWriteService.PushTags(request.RepoPath, ct);
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Application.Interfaces;
@@ -27,21 +28,23 @@ namespace Infrastructure.Services
{
if (TryParse(tag.FriendlyName, out var semver))
{
yield return new Version(semver.Major, semver.Minor, semver.Patch, semver.Prerelease, semver.Build);
yield return semver;
}
}
}
private static bool TryParse(string version, out SemVersion semverVersion)
private static bool TryParse(string versionStr, out Version version)
{
try
{
semverVersion = SemVersion.Parse(version.TrimStart('v').TrimStart('V'));
var semver = SemVersion.Parse(versionStr.TrimStart('v').TrimStart('V'));
var hasVPrefix = versionStr.StartsWith("v", StringComparison.InvariantCultureIgnoreCase);
version = new Version(semver.Major, semver.Minor, semver.Patch, semver.Prerelease, semver.Build, hasVPrefix);
return true;
}
catch
{
semverVersion = null;
version = null;
return false;
}
}

View File

@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using Application.Interfaces;
using CliWrap;
@@ -14,12 +15,23 @@ namespace Infrastructure.Services
repo.ApplyTag(tag);
}
public async Task Push(string repoPath)
public async Task<string> PushTags(string repoPath, CancellationToken ct)
{
await Cli.Wrap("git")
var result = await Cli.Wrap("git")
.WithArguments("push --tags")
.WithWorkingDirectory(repoPath)
.ExecuteBufferedAsync();
.ExecuteBufferedAsync(ct);
return result.StandardOutput.Trim();
}
public async Task<string> Push(string repoPath, CancellationToken ct)
{
var result = await Cli.Wrap("git")
.WithArguments("push")
.WithWorkingDirectory(repoPath)
.ExecuteBufferedAsync(ct);
return result.StandardOutput.Trim();
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Threading;
using Application.Interfaces;
using Application.Models;
using Application.Queries;
using Moq;
using Xunit;
namespace UpdateTag.Tests
{
public class GetVersionInformationFromRepoTests
{
[Fact]
public async void DoesReadCurrentVersionCorrectly()
{
// Arrange
var mockedVersionList = new List<Version>
{
new Version(0, 1, 5, false),
new Version(0, 1, 7, false),
new Version(0, 2, 0, false),
new Version(0, 2, 0, 0, false),
new Version(0, 2, 0, 1, false),
new Version(0, 2, 0, 2, false)
};
var gitRepoMock = new Mock<IGitRepoReadService>();
gitRepoMock.Setup(m => m.GetAllVersions(It.IsAny<string>()))
.Returns(mockedVersionList);
var handler = new GetVersionInformationFromRepo(gitRepoMock.Object);
var query = new GetVersionInformationFromRepo.Query("");
// Act
var versionInformation = await handler.Handle(query, CancellationToken.None);
// Assert
Assert.Equal("0.2.0", versionInformation.CurrentVersion.ToString());
}
[Theory]
[InlineData(0, 2, 0, true, "v0.2.0")]
[InlineData(0, 2, 0, false, "0.2.0")]
public async void AddVPrefixToNextVersionIfCurrentVersionHasOne(int major, int minor, int patch, bool hasVPrefix,
string expectedVersionOutput)
{
// Arrange
var mockedVersionList = new List<Version>
{
new Version(major, minor, patch, hasVPrefix)
};
var gitRepoMock = new Mock<IGitRepoReadService>();
gitRepoMock.Setup(m => m.GetAllVersions(It.IsAny<string>()))
.Returns(mockedVersionList);
var handler = new GetVersionInformationFromRepo(gitRepoMock.Object);
var query = new GetVersionInformationFromRepo.Query("");
// Act
var versionInformation = await handler.Handle(query, CancellationToken.None);
// Assert
Assert.Equal(expectedVersionOutput, versionInformation.CurrentVersion.ToString());
}
}
}

View File

@@ -11,24 +11,24 @@ namespace UpdateTag.Tests
{
// Arrange
var version = new Version(1, 0, 1, 1);
var version = new Version(1, 0, 1, 1, false);
// Act
var versionInformation = new VersionInformation(version);
var versions = versionInformation.NextVersions.Select(nv => nv.Version.ToString()).ToList();
// Assert
Assert.Contains("1.0.1-RC.2", versions);
Assert.Contains("1.0.1", versions);
Assert.Contains("1.0.1-RC.2", versions); // next rc
Assert.Contains("1.0.1", versions); // release rc
Assert.Equal(2, versions.Count);
}
[Fact]
public void CorrectNextVersionsFromMinor()
public void CorrectNextVersionsFromNonRc()
{
// Arrange
var version = new Version(1, 0, 1);
var version = new Version(1, 0, 1, false);
// Act
var versionInformation = new VersionInformation(version);
@@ -36,11 +36,13 @@ namespace UpdateTag.Tests
// Assert
Assert.Contains("1.0.1-RC.0", versions);
Assert.Contains("1.0.2", versions);
Assert.Contains("1.1.0", versions);
Assert.Contains("2.0.0", versions);
Assert.Equal(4, versions.Count);
Assert.Contains("1.0.2-RC.0", versions); // patch RC
Assert.Contains("1.1.0-RC.0", versions); // minor RC
Assert.Contains("2.0.0-RC.0", versions); // major RC
Assert.Contains("1.0.2", versions); // next patch
Assert.Contains("1.1.0", versions); // next minor
Assert.Contains("2.0.0", versions); // next major
Assert.Equal(6, versions.Count);
}
}
}

View File

@@ -12,7 +12,7 @@ namespace UpdateTag.Tests
public void NextMajor(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service).NextMajor();
var version = new Version(major, minor, patch, rc, service, false).NextMajor();
Assert.Equal(expected, version.ToString());
}
@@ -24,7 +24,7 @@ namespace UpdateTag.Tests
public void NextMinor(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service).NextMinor();
var version = new Version(major, minor, patch, rc, service, false).NextMinor();
Assert.Equal(expected, version.ToString());
}
@@ -35,7 +35,7 @@ namespace UpdateTag.Tests
public void NextPatch(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service).NextPatch();
var version = new Version(major, minor, patch, rc, service, false).NextPatch();
Assert.Equal(expected, version.ToString());
}
@@ -45,7 +45,34 @@ namespace UpdateTag.Tests
public void NextRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service).NextRc();
var version = new Version(major, minor, patch, rc, service, false).NextRc();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 1, 1, null, "", "1.1.2-RC.0")]
[InlineData(1, 1, 1, null, "ErpNext", "1.1.2-RC.0+ErpNext")]
public void CreatePatchRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service, false).CreatePatchRc();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 1, 1, null, "", "1.2.0-RC.0")]
[InlineData(1, 1, 1, null, "ErpNext", "1.2.0-RC.0+ErpNext")]
public void CreateMinorRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service, false).CreateMinorRc();
Assert.Equal(expected, version.ToString());
}
[Theory]
[InlineData(1, 1, 1, null, "", "2.0.0-RC.0")]
[InlineData(1, 1, 1, null, "ErpNext", "2.0.0-RC.0+ErpNext")]
public void CreateMajroRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service, false).CreateMajorRc();
Assert.Equal(expected, version.ToString());
}
@@ -54,7 +81,7 @@ namespace UpdateTag.Tests
[InlineData(1, 1, 1, "RC.4", "ErpNext", "1.1.1+ErpNext")]
public void ReleaseRc(int major, int minor, int patch, string rc, string service, string expected)
{
var version = new Version(major, minor, patch, rc, service).ReleaseRc();
var version = new Version(major, minor, patch, rc, service, false).ReleaseRc();
Assert.Equal(expected, version.ToString());
}
}

View File

@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>