Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Downstream merge #61

Closed
wants to merge 11 commits into from
4 changes: 4 additions & 0 deletions src/PostSharp.Engineering.BuildTools/AppExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public static void AddProductCommands( this CommandApp app, Product? product = n
.WithDescription( "Bumps the version of this product" );
}

root.AddCommand<DownstreamMergeCommand>( "merge-downstream" )
.WithData( product )
.WithDescription( "Merges the code to the subsequent development branch." );

root.AddBranch(
"dependencies",
dependencies =>
Expand Down
19 changes: 15 additions & 4 deletions src/PostSharp.Engineering.BuildTools/Build/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ public class BuildContext
/// </summary>
public string Branch { get; }

// TODO: Read the version from this.Branch in 2023.1.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a smell. It should almost certainly not be here.

public string CurrentBranchVersion { get; } = "2023.0";

public string CurrentBranchVersionWithoutDot { get; }

public string DownstreamBranchVersion { get; }

public string DownstreamBranchVersionWithoutDot { get; }

public CommandContext CommandContext { get; }

/// <summary>
Expand Down Expand Up @@ -62,6 +71,9 @@ private BuildContext( ConsoleHelper console, string repoDirectory, Product produ
this.RepoDirectory = repoDirectory;
this.Product = product;
this.Branch = branch;
this.CurrentBranchVersionWithoutDot = this.CurrentBranchVersion.Replace( ".", "", StringComparison.Ordinal );
this.DownstreamBranchVersion = product.GetNextBranchVersion( this.CurrentBranchVersion );
this.DownstreamBranchVersionWithoutDot = this.DownstreamBranchVersion.Replace( ".", "", StringComparison.Ordinal );
this.CommandContext = commandContext;
}

Expand All @@ -83,15 +95,14 @@ public static bool TryCreate(
return false;
}

if ( !ToolInvocationHelper.InvokeTool( console, "git", "rev-parse --abbrev-ref HEAD", repoDirectory, out var gitExitCode, out var gitOutput )
|| gitExitCode != 0 )
if ( !VcsHelper.TryGetCurrentBranch( console, repoDirectory, out var currentBranch ) )
{
buildContext = null;

return false;
}

buildContext = new BuildContext( console, repoDirectory, (Product) commandContext.Data!, gitOutput.Trim(), commandContext );
buildContext = new BuildContext( console, repoDirectory, (Product) commandContext.Data!, currentBranch, commandContext );

return true;
}
Expand Down
29 changes: 24 additions & 5 deletions src/PostSharp.Engineering.BuildTools/Build/Model/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public string ProductName

public static ConfigurationSpecific<BuildConfigurationInfo> DefaultConfigurations { get; }
= new(
debug: new BuildConfigurationInfo( MSBuildName: "Debug", BuildTriggers: new IBuildTrigger[] { new SourceBuildTrigger() } ),
debug: new BuildConfigurationInfo( MSBuildName: "Debug" ), // The SourceBuildTrigger is not set here, because this configuration gets triggered as a dependency of DownstreamMerge configuration.
release: new BuildConfigurationInfo( MSBuildName: "Release", RequiresSigning: true, ExportsToTeamCityBuild: false ),
@public: new BuildConfigurationInfo(
MSBuildName: "Release",
Expand Down Expand Up @@ -1848,7 +1848,8 @@ public bool GenerateTeamcityConfiguration( BuildContext context )
var dependencies =
dependenciesOverrideFile.Dependencies.Select(
x => (Name: x.Key,
Definition: BuildTools.Dependencies.Model.Dependencies.All.Single( d => d.Name == x.Key ),
Definition: BuildTools.Dependencies.Model.Dependencies.All.SingleOrDefault( d => d.Name == x.Key )
?? TestDependencies.All.Single( d => d.Name == x.Key ),
Source: x.Value) )
.Where( d => d.Definition.GenerateSnapshotDependency )
.Select( x => (x.Name, x.Definition, Configuration: GetDependencyConfiguration( x.Definition, x.Source )) )
Expand Down Expand Up @@ -1930,9 +1931,7 @@ public bool GenerateTeamcityConfiguration( BuildContext context )
}

// Create a TeamCity configuration for Swap.
if (
buildTeamCityConfiguration != null
&& configurationInfo is { Swappers: { }, SwapAfterPublishing: false } )
if ( configurationInfo is { Swappers: { }, SwapAfterPublishing: false } )
{
var swapDependencies = new List<TeamCitySnapshotDependency>();

Expand Down Expand Up @@ -1969,6 +1968,19 @@ public bool GenerateTeamcityConfiguration( BuildContext context )
}
}

teamCityBuildConfigurations.Add(
new TeamCityBuildConfiguration(
this,
"DownstreamMerge",
"Downstream Merge",
"merge-downstream",
this.BuildAgentType )
{
IsDeployment = true,
Dependencies = new[] { new TeamCitySnapshotDependency( $"{BuildConfiguration.Debug}Build", true ) },
BuildTriggers = new IBuildTrigger[] { new SourceBuildTrigger() }
} );

// Add from extensions.
foreach ( var extension in this.Extensions )
{
Expand Down Expand Up @@ -2337,5 +2349,12 @@ private bool TryGetPreparedVersionInfo(

return true;
}

public virtual string GetNextBranchVersion( string originalBranchVersion )
{
var originalVersionParts = originalBranchVersion.Split( '.', 2 );

return $"{originalVersionParts[0]}.{int.Parse( originalVersionParts[1], CultureInfo.InvariantCulture ) + 1}";
}
}
}
143 changes: 11 additions & 132 deletions src/PostSharp.Engineering.BuildTools/Build/Publishers/MergePublisher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using PostSharp.Engineering.BuildTools.ContinuousIntegration;
using PostSharp.Engineering.BuildTools.Dependencies.Model;
using PostSharp.Engineering.BuildTools.Utilities;
using System;
using System.IO;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -47,151 +46,29 @@ public override SuccessCode Execute(
}
}

// Returns the reference name of the current branch.
ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"branch --show-current",
context.RepoDirectory,
out var gitExitCode,
out var gitOutput );

if ( gitExitCode != 0 )
{
context.Console.WriteError( gitOutput );

return SuccessCode.Error;
}

var currentBranch = gitOutput.Trim();

context.Console.WriteHeading( $"Merging branch '{currentBranch}' to 'master' after publishing artifacts." );
context.Console.WriteHeading( $"Merging branch '{context.Branch}' to 'master' after publishing artifacts." );

// Checkout to master branch and pull to update the local repository.
if ( !TryCheckoutAndPullMaster( context ) )
if ( !VcsHelper.TryCheckoutAndPull( context, "master" ) )
{
return SuccessCode.Error;
}

// Merge current branch to master.
if ( !MergeBranchToMaster( context, settings, currentBranch ) )
if ( !VcsHelper.TryMerge( context, context.Branch, "master", "--strategy-option theirs" ) )
{
return SuccessCode.Error;
}

context.Console.WriteSuccess( "MergePublisher has finished successfully." );

return SuccessCode.Success;
}

private static bool TryCheckoutAndPullMaster( BuildContext context )
{
// Add origin/master branch to the list of currently tracked branches because local repository may be initialized with only the default branch.
if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"remote set-branches --add origin master",
context.RepoDirectory ) )
{
return false;
}

if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"fetch",
context.RepoDirectory ) )
{
return false;
}

// Switch to the master branch before we do merge.
if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"checkout master",
context.RepoDirectory ) )
{
return false;
}

// Pull remote master changes because local master may not contain all changes or upstream may not be set for local master.
if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"pull origin master",
context.RepoDirectory ) )
// Push master.
if ( !VcsHelper.TryPush( context, settings ) )
{
return false;
}

return true;
}

private static bool MergeBranchToMaster( BuildContext context, BaseBuildSettings settings, string branchToMerge )
{
// Attempts merging branch to master, forcing conflicting hunks to be auto-resolved in favour of the branch being merged.
if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"merge {branchToMerge} --strategy-option theirs",
context.RepoDirectory ) )
{
return false;
}

// Returns the remote origin.
ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"remote get-url origin",
context.RepoDirectory,
out var gitExitCode,
out var gitOutput );

if ( gitExitCode != 0 )
{
context.Console.WriteError( gitOutput );

return false;
}

var gitOrigin = gitOutput.Trim();

var isHttps = gitOrigin.StartsWith( "https", StringComparison.InvariantCulture );

// When on TeamCity, origin will be updated to form including Git authentication credentials.
if ( TeamCityHelper.IsTeamCityBuild( settings ) )
{
if ( isHttps )
{
if ( !TeamCityHelper.TryGetTeamCitySourceWriteToken(
out var teamcitySourceWriteTokenEnvironmentVariableName,
out var teamcitySourceCodeWritingToken ) )
{
context.Console.WriteImportantMessage(
$"{teamcitySourceWriteTokenEnvironmentVariableName} environment variable is not set. Using default credentials." );
}
else
{
gitOrigin = gitOrigin.Insert( 8, $"teamcity%40postsharp.net:{teamcitySourceCodeWritingToken}@" );
}
}
}

// Push completed merge operation to remote.
if ( !ToolInvocationHelper.InvokeTool(
context.Console,
"git",
$"push {gitOrigin}",
context.RepoDirectory ) )
{
return false;
return SuccessCode.Error;
}

context.Console.WriteSuccess( $"Merging '{branchToMerge}' into 'master' branch was successful." );
context.Console.WriteSuccess( $"Merging '{context.Branch}' into 'master' branch was successful." );

return true;
return SuccessCode.Success;
}

private static bool TryParseAndVerifyDependencies( BuildContext context, PublishSettings settings, out bool dependenciesUpdated )
Expand Down Expand Up @@ -223,7 +100,9 @@ private static bool TryParseAndVerifyDependencies( BuildContext context, Publish
foreach ( var dependencyOverride in dependenciesOverrideFile.Dependencies )
{
var dependencySource = dependencyOverride.Value;
var dependency = Dependencies.Model.Dependencies.All.Single( d => d.Name == dependencyOverride.Key );

var dependency = Dependencies.Model.Dependencies.All.SingleOrDefault( d => d.Name == dependencyOverride.Key )
?? TestDependencies.All.Single( d => d.Name == dependencyOverride.Key );

// We don't automatically change version of Feed or Local dependencies.
if ( dependencySource.SourceKind is DependencySourceKind.Feed )
Expand Down
Loading