Skip to content

Commit

Permalink
Teach the CLI tool about backport/ branches
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-davidson committed Jan 30, 2020
1 parent 149643b commit da22c38
Show file tree
Hide file tree
Showing 19 changed files with 355 additions and 69 deletions.
18 changes: 9 additions & 9 deletions Bluewire.Conventions.UnitTests/BranchSemanticsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public struct Case
public string Raw { get; set; }
public SemanticVersion Parsed { get; set; }
public string BranchStart { get; set; }
public string BranchEnd { get; set; }
public string[] BranchEnds { get; set; }

public override string ToString()
{
Expand All @@ -20,10 +20,10 @@ public override string ToString()
}

public static Case[] Cases = {
new Case { Raw = "17.05.123-beta", Parsed = new SemanticVersion("17","05",123,"beta"), BranchStart = "17.05", BranchEnd = "master" },
new Case { Raw = "17.05.123-rc", Parsed = new SemanticVersion("17","05",123,"rc"), BranchStart = "17.05", BranchEnd = "candidate/17.05" },
new Case { Raw = "17.05.123-canary", Parsed = new SemanticVersion("17","05",123,"canary"), BranchStart = "17.05", BranchEnd = null },
new Case { Raw = "17.05.123-release", Parsed = new SemanticVersion("17","05",123,"release"), BranchStart = "17.05", BranchEnd = "release/17.05" }
new Case { Raw = "17.05.123-beta", Parsed = new SemanticVersion("17","05",123,"beta"), BranchStart = "17.05", BranchEnds = new[] { "backport/17.05", "master" } },
new Case { Raw = "17.05.123-rc", Parsed = new SemanticVersion("17","05",123,"rc"), BranchStart = "17.05", BranchEnds = new[] { "candidate/17.05" } },
new Case { Raw = "17.05.123-canary", Parsed = new SemanticVersion("17","05",123,"canary"), BranchStart = "17.05", BranchEnds = new string[0] },
new Case { Raw = "17.05.123-release", Parsed = new SemanticVersion("17","05",123,"release"), BranchStart = "17.05", BranchEnds = new[] { "release/17.05" } }
};

[Test]
Expand All @@ -32,7 +32,7 @@ public void CanIdentifyStartOfBranch([ValueSource(nameof(Cases))] Case testCase)
var sut = new BranchSemantics();
var parsed = SemanticVersion.FromString(testCase.Raw);
Assert.That(sut.GetVersionZeroBranchName(parsed), Is.EqualTo(testCase.BranchStart));
Assert.That(sut.GetVersionLatestBranchName(parsed), Is.EqualTo(testCase.BranchEnd));
Assert.That(sut.GetVersionLatestBranchNames(parsed), Is.EqualTo(testCase.BranchEnds));
}

[Test]
Expand All @@ -43,7 +43,7 @@ public void ThrowsExceptionIfSemTagIsMissing()
Assert.Throws<InvalidOperationException>(() =>
{
var semVer = new SemanticVersion("17", "05", 123, null);
sut.GetVersionLatestBranchName(semVer);
sut.GetVersionLatestBranchNames(semVer);
});
}

Expand All @@ -55,7 +55,7 @@ public void ThrowsExceptionIfSemTagIsEmpty()
Assert.Throws<InvalidOperationException>(() =>
{
var semVer = new SemanticVersion("17", "05", 123, "");
sut.GetVersionLatestBranchName(semVer);
sut.GetVersionLatestBranchNames(semVer);
});
}

Expand All @@ -67,7 +67,7 @@ public void ThrowsExceptionIfSemTagIsUnknown()
Assert.Throws<InvalidOperationException>(() =>
{
var semVer = new SemanticVersion("17", "05", 123, "unknownsemtagfortesting");
sut.GetVersionLatestBranchName(semVer);
sut.GetVersionLatestBranchNames(semVer);
});
}
}
Expand Down
26 changes: 19 additions & 7 deletions Bluewire.Conventions/BranchSemantics.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Bluewire.Conventions
Expand All @@ -14,6 +15,7 @@ public BranchType GetBranchType(StructuredBranch branch)

switch (lastNamespacePart)
{
case "backport": return BranchType.Beta;
case "candidate": return BranchType.ReleaseCandidate;
case "release": return BranchType.Release;
case "canary": return BranchType.Canary;
Expand All @@ -23,12 +25,12 @@ public BranchType GetBranchType(StructuredBranch branch)

public string[] GetBranchFilters(params BranchType[] types)
{
return types.Distinct().Select(t => t.BranchFilter).Where(b => !String.IsNullOrWhiteSpace(b)).ToArray();
return types.Distinct().SelectMany(t => t.BranchFilters).Where(b => !String.IsNullOrWhiteSpace(b)).ToArray();
}

public string[] GetRemoteBranchFilters(params BranchType[] types)
{
return types.Distinct().Select(t => t.BranchFilter).Where(b => !String.IsNullOrWhiteSpace(b)).Select(b => $"*/{b}").ToArray();
return types.Distinct().SelectMany(t => t.BranchFilters).Where(b => !String.IsNullOrWhiteSpace(b)).Select(b => $"*/{b}").ToArray();
}


Expand All @@ -38,15 +40,25 @@ public string GetVersionZeroBranchName(SemanticVersion semVer)
return string.Format("{0}.{1}", semVer.Major, semVer.Minor);
}

public string[] GetVersionLatestBranchNames(SemanticVersion semVer) => GetVersionLatestBranchNamesInternal(semVer).ToArray();

// Assumes sementics defined in BranchType.cs
public string GetVersionLatestBranchName(SemanticVersion semVer)
private IEnumerable<string> GetVersionLatestBranchNamesInternal(SemanticVersion semVer)
{
switch (semVer.SemanticTag)
{
case "beta": return "master";
case "rc": return string.Format("candidate/{0}.{1}", semVer.Major, semVer.Minor);
case "release": return string.Format("release/{0}.{1}", semVer.Major, semVer.Minor);
case "canary": return null;
case "beta":
yield return $"backport/{semVer.Major}.{semVer.Minor}";
yield return "master";
break;
case "rc":
yield return $"candidate/{semVer.Major}.{semVer.Minor}";
break;
case "release":
yield return $"release/{semVer.Major}.{semVer.Minor}";
break;
case "canary":
break;
default: throw new InvalidOperationException("Unknown semantic tag value");
}
}
Expand Down
8 changes: 4 additions & 4 deletions Bluewire.Conventions/BranchType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
{
public struct BranchType
{
private BranchType(string semanticTag, string branchFilter)
private BranchType(string semanticTag, params string[] branchFilters)
{
SemanticTag = semanticTag;
BranchFilter = branchFilter;
BranchFilters = branchFilters;
}

public string SemanticTag { get; }
public string BranchFilter { get; }
public string[] BranchFilters { get; }

public static BranchType None = default(BranchType);
public static BranchType Beta = new BranchType("beta", "master");
public static BranchType Beta = new BranchType("beta", "backport/*", "master");
public static BranchType Master = new BranchType("beta", "master"); // Alias.
public static BranchType ReleaseCandidate = new BranchType("rc", "candidate/*");
public static BranchType Release = new BranchType("release", "release/*");
Expand Down
4 changes: 2 additions & 2 deletions Bluewire.Conventions/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// 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")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyVersion("2.0.0")]
[assembly: AssemblyFileVersion("2.0.0")]
4 changes: 2 additions & 2 deletions Bluewire.Tools.Builds/Bluewire.Tools.Builds.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,12 @@
<ItemGroup>
<Compile Include="FindBuild\BuildVersionFinder.cs" />
<Compile Include="FindBuild\IntegrationPoint.cs" />
<Compile Include="Shared\RepositoryStructureException.cs" />
<Compile Include="Shared\RepositoryStructureInspector.cs" />
<Compile Include="FindBuild\ResolveBuildVersionsFromGitHubPullRequest.cs" />
<Compile Include="FindBuild\ResolveBuildVersionsFromTicketIdentifier.cs" />
<Compile Include="FindBuild\TargetBranchResolver.cs" />
<Compile Include="FindBuild\IBuildVersionResolutionJob.cs" />
<Compile Include="FindBuild\RepositoryStructureException.cs" />
<Compile Include="FindBuild\RepositoryStructureInspector.cs" />
<Compile Include="FindBuild\ResolveBuildVersionsFromCommit.cs" />
<Compile Include="FindTickets\ITicketsResolutionJob.cs" />
<Compile Include="FindTickets\ResolveTicketsFromSemanticVersions.cs" />
Expand Down
1 change: 1 addition & 0 deletions Bluewire.Tools.Builds/FindBuild/TargetBranchResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Bluewire.Common.GitWrapper;
using Bluewire.Common.GitWrapper.Model;
using Bluewire.Conventions;
using Bluewire.Tools.Builds.Shared;

namespace Bluewire.Tools.Builds.FindBuild
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Collections.Generic;
using Bluewire.Tools.Builds.Shared;
using System;
using System.Linq;
using Bluewire.Tools.Builds.FindBuild;

namespace Bluewire.Tools.Builds.FindCommits
{
Expand Down Expand Up @@ -67,30 +69,16 @@ private async Task<Ref> FindRemoteCommit(GitSession session, IGitFilesystemConte
var branchSemantics = new BranchSemantics();

var startBranchName = new Ref(branchSemantics.GetVersionZeroBranchName(semVer));
var endLocalBranchName = branchSemantics.GetVersionLatestBranchName(semVer);
if (string.IsNullOrEmpty(endLocalBranchName) || string.IsNullOrEmpty(startBranchName)) return null;
if (string.IsNullOrEmpty(startBranchName)) return null;

var startRef = new Ref(startBranchName);
var endRef = await GetEndRef(session, repository, semVer, endLocalBranchName);
var endRef = await new RepositoryStructureInspector(session).ResolveTagOrTipOfBranchForVersion(repository, semVer);

// It's less likely that the end ref will exist
if (!await session.RefExists(repository, endRef)) return null;
if (endRef == null) return null;
if (!await session.RefExists(repository, startRef)) return null;

return await resolver.FindCommit(repository, startRef, endRef, semVer.Build);
}

private static async Task<Ref> GetEndRef(GitSession session, IGitFilesystemContext repository, SemanticVersion semVer, string endLocalBranchName)
{
if (endLocalBranchName == "master")
{
var maintTag = new Ref($"tags/maint/{semVer.Major}.{semVer.Minor}");
if (await session.TagExists(repository, maintTag))
{
return RefHelper.GetRemoteRef(new Ref(maintTag));
}
}
return RefHelper.GetRemoteRef(new Ref(endLocalBranchName));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;

namespace Bluewire.Tools.Builds.FindBuild
namespace Bluewire.Tools.Builds.Shared
{
public class RepositoryStructureException : ApplicationException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
using Bluewire.Common.GitWrapper;
using Bluewire.Common.GitWrapper.Model;
using Bluewire.Conventions;
using Bluewire.Tools.Builds.FindBuild;
using Bluewire.Tools.GitRepository;

namespace Bluewire.Tools.Builds.FindBuild
namespace Bluewire.Tools.Builds.Shared
{
public class RepositoryStructureInspector
{
Expand Down Expand Up @@ -75,6 +76,33 @@ public async Task<TagDetails> ResolveBaseTagForCommit(Common.GitWrapper.GitRepos
return await ResolveBaseTagForVersion(repository, versionNumber);
}

public async Task<Ref> ResolveTagOrTipOfBranchForVersion(IGitFilesystemContext repository, SemanticVersion semanticVersion)
{
var branchSemantics = new BranchSemantics();
var endLocalBranchNames = branchSemantics.GetVersionLatestBranchNames(semanticVersion);
foreach (var endLocalBranchName in endLocalBranchNames)
{
var endRef = await GetEndRef(endLocalBranchName);
if (await gitSession.RefExists(repository, endRef)) return endRef;
}
return null;

async Task<Ref> GetEndRef(string endLocalBranchName)
{
if (endLocalBranchName == "master")
{
// This is only applicable for versions which predate the use of backport/* branches, where the
// beta version terminates at maint/*.
var maintTag = new Ref($"tags/maint/{semanticVersion.Major}.{semanticVersion.Minor}");
if (await gitSession.TagExists(repository, maintTag))
{
return RefHelper.GetRemoteRef(new Ref(maintTag));
}
}
return RefHelper.GetRemoteRef(new Ref(endLocalBranchName));
}
}

public async Task<IntegrationQueryResult[]> QueryIntegrationPoints(Common.GitWrapper.GitRepository repository, Ref subject, StructuredBranch[] targetBranches)
{
var baseTag = await ResolveBaseTagForCommit(repository, subject);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A4214FB7-545D-4653-9F0C-970443C13C46}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bluewire.Tools.Runner.IntegrationTests</RootNamespace>
<AssemblyName>Bluewire.Tools.Runner.IntegrationTests</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Bluewire.Common.Console.NUnit3, Version=9.1.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Bluewire.Common.Console.NUnit3.9.1.5\lib\net45\Bluewire.Common.Console.NUnit3.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="FindCommitsTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestInfrastructure\Attributes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bluewire.Common.GitWrapper.IntegrationTests\Bluewire.Common.GitWrapper.IntegrationTests.csproj">
<Project>{B9DBC4C3-7733-49BA-B797-E9CB9D616403}</Project>
<Name>Bluewire.Common.GitWrapper.IntegrationTests</Name>
</ProjectReference>
<ProjectReference Include="..\Bluewire.Common.GitWrapper\Bluewire.Common.GitWrapper.csproj">
<Project>{815563F8-0AF9-4E56-9160-A26CDD1DB163}</Project>
<Name>Bluewire.Common.GitWrapper</Name>
</ProjectReference>
<ProjectReference Include="..\Bluewire.Tools.GitRepository\Bluewire.Tools.GitRepository.csproj">
<Project>{F9238A4E-6EAD-4E8A-84B5-5F7400B1A065}</Project>
<Name>Bluewire.Tools.GitRepository</Name>
</ProjectReference>
<ProjectReference Include="..\Bluewire.Tools.Runner\Bluewire.Tools.Runner.csproj">
<Project>{07FD50C6-7569-49DB-9926-127E2E7F4339}</Project>
<Name>Bluewire.Tools.Runner</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Loading

0 comments on commit da22c38

Please sign in to comment.