From 5ada1ae15ff11a0ac5ad6f4d40e368145d6bdea2 Mon Sep 17 00:00:00 2001 From: bitbonk <bitbonk@msn.com> Date: Fri, 9 Sep 2022 18:07:28 +0200 Subject: [PATCH 1/4] Add demo test screnarion --- .../VersioningDemoScenario.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs diff --git a/src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs b/src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs new file mode 100644 index 0000000000..4bea88d09c --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs @@ -0,0 +1,114 @@ +using GitTools.Testing; +using GitVersion.Model.Configuration; +using GitVersion.OutputVariables; +using LibGit2Sharp; +using NUnit.Framework; +using Shouldly; + +namespace GitVersion.Core.Tests.IntegrationTests; + +public class VersioningDemoScenario +{ + [Test] + public void ReleaseAndDevelopProblemDemo() + { + var configuration = new Config + { + // Settings the below NextVersion results in an exception + // I wanted to set it to globally start with version 1 + // Setting the version to 1.0 (which is not a valid semantic version) + // works in GitVersion.yml but not here. + + // NextVersion = "1.0" + }; + + // create main and develop branch, develop is two commits ahead of main + using var fixture = new EmptyRepositoryFixture(); + fixture.Repository.MakeACommit(); + Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop")); + fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit(); + + var previousVersion = GetSemVer(fixture.GetVersion(configuration)); + + // make a 3rd commit on develop + fixture.Repository.MakeACommit(); + + var currentVersion = GetSemVer(fixture.GetVersion(configuration)); + + currentVersion.ShouldBeGreaterThan(previousVersion, + "the semver should be incremented after a commit on develop"); + + // we are ready to prepare the 1.0.0 release, create and checkout release/1.0.0 + Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("release/1.0.0")); + + fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1", + "the first semver on release/1.0.0 should be beta1"); + + // make another commit on release/1.0.0 to prepare the actual beta1 release + fixture.Repository.MakeACommit(); + + fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1", + "the semver on release/1.0.0 should still be be beta1"); + + Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]); + + previousVersion = currentVersion; + currentVersion = GetSemVer(fixture.GetVersion(configuration)); + + currentVersion.ShouldBe(previousVersion, + "the semver on develop should not have changed " + + "even when release/1.0.0 has new commits due to beta 1 preparations"); + + // now some other team member makes changes on develop that may or may not end up in 1.0.0 + fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit(); + + previousVersion = currentVersion; + currentVersion = GetSemVer(fixture.GetVersion(configuration)); + + currentVersion.ShouldBeGreaterThan(previousVersion, + "the semver should be incremented after a even more commit on develop"); + + Commands.Checkout(fixture.Repository, fixture.Repository.Branches["release/1.0.0"]); + + // now we release the beta 1 + fixture.Repository.ApplyTag("1.0.0-beta1"); + + fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1", + "the on release/1.0.0 should still be beta1 after the beta 1 tag"); + + // continue with more work on develop that may or may not end up in 1.0.0 + Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]); + fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit(); + + previousVersion = currentVersion; + currentVersion = GetSemVer(fixture.GetVersion(configuration)); + + currentVersion.ShouldBeGreaterThan(previousVersion, + "the semver should be incremented after a even more commit on develop"); + + // now we decide that the three commits on develop should be part of the beta 2 release + // se we merge it into release/1.0.0 with --no-ff because it is a protected branch + Commands.Checkout(fixture.Repository, fixture.Repository.Branches["release/1.0.0"]); + fixture.Repository.Merge( + fixture.Repository.Branches["develop"], + Generate.SignatureNow(), + new MergeOptions {FastForwardStrategy = FastForwardStrategy.NoFastForward}); + + fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.2", + "the next semver on release/1.0.0 should be beta2"); + + Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]); + previousVersion = currentVersion; + currentVersion = GetSemVer(fixture.GetVersion(configuration)); + + currentVersion.ShouldBeGreaterThanOrEqualTo(previousVersion, + "the semver should be incremented (or unchanged) " + + "after we merged develop into release/1.0.0"); + + static SemanticVersion GetSemVer(VersionVariables ver) + => SemanticVersion.Parse(ver.FullSemVer, null); + } +} From bc3204d8bbec61bdcc0994de6d316c04d7970e93 Mon Sep 17 00:00:00 2001 From: bitbonk <bitbonk@msn.com> Date: Sat, 10 Sep 2022 14:34:12 +0200 Subject: [PATCH 2/4] Add a reduced demo test --- .../ReducedReleaseWorkflowDemo.cs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs b/src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs new file mode 100644 index 0000000000..312b46d245 --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs @@ -0,0 +1,103 @@ +using GitTools.Testing; +using GitVersion.Model.Configuration; +using LibGit2Sharp; +using NUnit.Framework; +using Shouldly; + +namespace GitVersion.Core.Tests.IntegrationTests; + +public class ReducedReleaseWorkflowDemo : IDisposable +{ + private static readonly EmptyRepositoryFixture _fixture = new(); + + private static readonly Config _config = new() + { + // ❓ In my GitVersion.yml I actually have set the version to "1.0" + // but that will cause an exception when I do it here in the tests + NextVersion = "1.0.0" + }; + + public void Dispose() => _fixture.Dispose(); + + [Test] + public void Demo() + { + // create main and develop branches + // develop is one commits ahead of main + MakeACommit(); + CreateAndCheckoutBranch("develop"); + MakeACommit(); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-alpha.1"); + + // now we are ready to start with the preparation of the 1.0.0 release + CreateAndCheckoutBranch("release/1.0.0"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + + // make another commit on release/1.0.0 to prepare the actual beta1 release + MakeACommit(); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + + // now we makes changes on develop that may or may not end up in the 1.0.0 release + CheckoutBranch("develop"); + MakeACommit(); + + // ❌ fails! actual: "1.1.0-alpha.1" + // We have not released 1.0.0, not even a beta, so why increment to 1.1.0? + // Even though this surprising, it might actually be OK, + // at least the version is incremented, which is needed for the CI nuget feed + GetCurrentSemVer().ShouldBe("1.0.0-alpha.2"); + + // now we do the actual release of beta 1 + CheckoutBranch("release/1.0.0"); + ApplyTag("1.0.0-beta1"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + + // continue with more work on develop that may or may not end up in the 1.0.0 release + CheckoutBranch("develop"); + MakeACommit(); + + // ❌ fails! actual: "1.1.0-alpha.2" + // We still have not finally released 1.0.0 yet, only a beta, so why increment to 1.1.0? + // Even though this surprising, it might actually be OK, + // at least the version is incremented, which is needed for the CI nuget feed + GetCurrentSemVer().ShouldBe("1.0.0-alpha.3"); + + // now we decide that the new changes on develop should be part of the beta 2 release + // se we merge it into release/1.0.0 with --no-ff because it is a protected branch + // but we don't do the release of beta 2 jus yet + CheckoutBranch("release/1.0.0"); + MergeWithNoFF("develop"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.2"); + + CheckoutBranch("develop"); + + // ❌ fails! actual "1.0.0-alpha.3" + // This is now really a problem. Why did it decrement the minor version? + // All subsequent changes on develop will now a lower version that previously + // and the nuget packages end up on the CI feed with a lower version + // so users of that feed would need to _downgrade_ to get a _newer_ version. + GetCurrentSemVer().ShouldBe("1.1.0-alpha.2"); + } + + private static void MakeACommit() => _fixture.Repository.MakeACommit(); + + private void CheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.Branches[branchName]); + + private void CreateAndCheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.CreateBranch(branchName)); + + private string GetCurrentSemVer() => _fixture.GetVersion(_config).SemVer; + + private void ApplyTag(string tag) => _fixture.Repository.ApplyTag(tag); + + private void MergeWithNoFF(string sourceBranch) => _fixture.Repository.MergeNoFF(sourceBranch); +} From e78e9d25e0dc2791c693a50b7021b283ed77a21f Mon Sep 17 00:00:00 2001 From: bitbonk <bitbonk@msn.com> Date: Sun, 11 Sep 2022 11:56:17 +0200 Subject: [PATCH 3/4] Add full release workflow demo with workaround --- .../ReleaseWorkflowDemoWithWorkaround.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs b/src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs new file mode 100644 index 0000000000..6db72bec81 --- /dev/null +++ b/src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs @@ -0,0 +1,141 @@ +using GitTools.Testing; +using GitVersion.Model.Configuration; +using LibGit2Sharp; +using NUnit.Framework; +using Shouldly; + +namespace GitVersion.Core.Tests.IntegrationTests; + +public class ReleaseWorkflowDemoWithWorkaround : IDisposable +{ + private readonly EmptyRepositoryFixture _fixture = new(); + + private readonly Config _config = new() + { + // ❓ In my GitVersion.yml I actually have set the version to "1.0" + // but that will cause an exception when I do it here in the tests + NextVersion = "1.0.0" + }; + + public void Dispose() => _fixture.Dispose(); + + [Test] + public void Demo() + { + // create main and develop branches + // develop is one commits ahead of main + MakeACommit("Commit 1 (made on main)"); + CreateAndCheckoutBranch("develop"); + MakeACommit("Commit 2 (made on develop)"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-alpha.1"); + + // now we are ready to start with the preparation of the 1.0.0 release + CreateAndCheckoutBranch("release/1.0.0"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + GetCurrentSemVer("develop").ShouldBe("1.0.0-alpha.1"); + + // make another commit on release/1.0.0 to prepare the actual beta1 release + MakeACommit("Commit 3 (made on release/1.0.0)"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + GetCurrentSemVer("develop").ShouldBe("1.0.0-alpha.1"); + + // now we makes changes on develop that may or may not end up in the 1.0.0 release + CheckoutBranch("develop"); + MakeACommit("Commit 4 (made on develop)"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.1.0-alpha.1"); + GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.1"); + + // now we do the actual release of beta 1 + CheckoutBranch("release/1.0.0"); + ApplyTag("1.0.0-beta1"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.1"); + GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.1"); + + // continue with more work on develop that may or may not end up in the 1.0.0 release + CheckoutBranch("develop"); + MakeACommit("Commit 5 (made on develop)"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.1.0-alpha.2"); + GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.1"); + + // now we decide that Commit 5 made on develop should be part of the beta 2 release + // se we cherry pick it + CheckoutBranch("release/1.0.0"); + CherryPickLatestCommitFromBranch("develop"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.2"); + GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.2"); + + // now we do an important bugfix that we found while preparing beta 2 + MakeACommit("Commit 6 (made on release/1.0.0)"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.0.0-beta.2"); + GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.2"); + + // we want everything (Commit 3 and 6) that we made only on release/1.0.0 be in develop + CheckoutBranch("develop"); + MergeWithNoFF("release/1.0.0"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.1.0-alpha.6"); + GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.2"); + + // now we do the actual 1.0.0 release + CheckoutBranch("release/1.0.0"); + MakeACommit("Commit 7 (made on release/1.0.0)"); + CheckoutBranch("main"); + MergeWithNoFF("release/1.0.0"); + ApplyTag("1.0.0"); + CheckoutBranch("develop"); + MergeWithNoFF("release/1.0.0"); + DeleteBranch("relesase/1.0.0"); + + // ✅ succeeds as expected + GetCurrentSemVer().ShouldBe("1.1.0-alpha.8"); + GetCurrentSemVer("main").ShouldBe("1.0.0"); + + + } + + private void DeleteBranch(string branch) => _fixture.Repository.Branches.Remove(branch); + + private void MakeACommit(string? message = null) => _fixture.Repository.MakeACommit(message); + + private void CheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.Branches[branchName]); + + private void CreateAndCheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.CreateBranch(branchName)); + + private string GetCurrentSemVer(string? branch = null) + { + if (branch == null) + { + return _fixture.GetVersion(_config).SemVer; + } + + if (_fixture.Repository.Branches.All(b => b.FriendlyName != branch)) + { + throw new InvalidOperationException($"Branch {branch} does not exist"); + } + + return _fixture.GetVersion(_config, branch: branch).SemVer; + } + + private void ApplyTag(string tag) => _fixture.Repository.ApplyTag(tag); + + private void MergeWithNoFF(string sourceBranch) => _fixture.Repository.MergeNoFF(sourceBranch); + + private void CherryPickLatestCommitFromBranch(string sourceBranch) => this._fixture.Repository.CherryPick(this._fixture.Repository.Branches[sourceBranch].Commits.First(), Generate.SignatureNow()); +} From b8192fb58719b366dc3de3dd1dc39d5b4ea54903 Mon Sep 17 00:00:00 2001 From: bitbonk <bitbonk@msn.com> Date: Sun, 11 Sep 2022 17:08:31 +0200 Subject: [PATCH 4/4] Do some more cleanup --- ... => FullReleaseGitFlowWithCherryPickWorkaround.cs} | 11 +++++++++-- ...moScenario.cs => GitFlowScenarioFromDiscussion.cs} | 10 ++++++++-- ...easeWorkflowDemo.cs => ReducedGitFlowWithMerge.cs} | 9 +++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) rename src/GitVersion.Core.Tests/IntegrationTests/{ReleaseWorkflowDemoWithWorkaround.cs => FullReleaseGitFlowWithCherryPickWorkaround.cs} (89%) rename src/GitVersion.Core.Tests/IntegrationTests/{VersioningDemoScenario.cs => GitFlowScenarioFromDiscussion.cs} (90%) rename src/GitVersion.Core.Tests/IntegrationTests/{ReducedReleaseWorkflowDemo.cs => ReducedGitFlowWithMerge.cs} (91%) diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs b/src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs similarity index 89% rename from src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs rename to src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs index 6db72bec81..20b76f3b5c 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/ReleaseWorkflowDemoWithWorkaround.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs @@ -6,7 +6,14 @@ namespace GitVersion.Core.Tests.IntegrationTests; -public class ReleaseWorkflowDemoWithWorkaround : IDisposable +/// <summary> +/// This demonstrates a full cycle to the release of 1.0.0 with the git flow workflow. +/// Since merging stuff to release/* from develop has a bug then decrements versions on develop +/// (as shown in the failing test <see cref="ReducedReleaseWorkflowDemo"/>), +/// we use cherry picking instead of merging to get stuff from develop to release/*. +/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches. +/// </summary> +public class FullReleaseGitFlowWithCherryPickWorkaround : IDisposable { private readonly EmptyRepositoryFixture _fixture = new(); @@ -20,7 +27,7 @@ public class ReleaseWorkflowDemoWithWorkaround : IDisposable public void Dispose() => _fixture.Dispose(); [Test] - public void Demo() + public void Demonstrate() { // create main and develop branches // develop is one commits ahead of main diff --git a/src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs similarity index 90% rename from src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs rename to src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs index 4bea88d09c..af8a3696aa 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/VersioningDemoScenario.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs @@ -7,10 +7,16 @@ namespace GitVersion.Core.Tests.IntegrationTests; -public class VersioningDemoScenario +/// <summary> +/// This demonstrates exactly the git flow described in https://github.com/GitTools/GitVersion/discussions/3177 +/// The assertions expect that the version on develop always increments on develop. +/// The eventually fail (at the end of this test) after we merged develop -> release/1.0.0. +/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches. +/// </summary> +public class GitFlowScenarioFromDiscussion { [Test] - public void ReleaseAndDevelopProblemDemo() + public void Demonstrate() { var configuration = new Config { diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs b/src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs similarity index 91% rename from src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs rename to src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs index 312b46d245..dd9e14612d 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/ReducedReleaseWorkflowDemo.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs @@ -6,7 +6,12 @@ namespace GitVersion.Core.Tests.IntegrationTests; -public class ReducedReleaseWorkflowDemo : IDisposable +/// <summary> +/// This demonstrates a bug that decrements the version on develop when we merge stuff from develop to release/* +/// in the default git flow workflow. +/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches. +/// </summary> +public class ReducedGitFlowWithMerge : IDisposable { private static readonly EmptyRepositoryFixture _fixture = new(); @@ -20,7 +25,7 @@ public class ReducedReleaseWorkflowDemo : IDisposable public void Dispose() => _fixture.Dispose(); [Test] - public void Demo() + public void Demonstrate() { // create main and develop branches // develop is one commits ahead of main