Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Added new feature to the command "prepare-release" "--commit-message-…
…pattern"
  • Loading branch information
fmacavilca committed Nov 9, 2023
commit a76dc4088302313b137f5cd557a70a877272ef3d
17 changes: 17 additions & 0 deletions doc/nbgv-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,23 @@ For each branch, the following properties are provided:
**Note:** When the current branch is already the release branch for the current version, no new branch will be created.
In that case, the `NewBranch` property will be `null`.

### Customizing the `prepare-release` commit message

By default, the `prepare-release` command generates commit message with this format "Set version to {version}". So with this new option. You can add a prefix or suffix to the default commit message

For example, running the following command on `master`

```
nbgv prepare-release --commit-message-pattern "Custom commit message pattern - {0} custom message"
```

So your commit message is going to be this:

```
Custom commit message pattern - Set version to '1.0' custom message
```
**Note:** You must consider that commit message pattern must contain only one {0} and valid commit message characters.

## Creating a version tag

The `tag` command automates the task of tagging a commit with a version.
Expand Down
16 changes: 10 additions & 6 deletions src/NerdBank.GitVersioning/ReleaseManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ public enum ReleaseManagerOutputMode
/// <param name="outputMode">
/// The output format to use for writing to stdout.
/// </param>
public void PrepareRelease(string projectDirectory, string releaseUnstableTag = null, Version nextVersion = null, VersionOptions.ReleaseVersionIncrement? versionIncrement = null, ReleaseManagerOutputMode outputMode = default)
/// <param name="commitMessagePattern">
/// Custom pattern to add a prefix or suffix to the default commit message.
/// </param>
public void PrepareRelease(string projectDirectory, string releaseUnstableTag = null, Version nextVersion = null, VersionOptions.ReleaseVersionIncrement? versionIncrement = null, ReleaseManagerOutputMode outputMode = default, string commitMessagePattern = null)
{
Requires.NotNull(projectDirectory, nameof(projectDirectory));

Expand Down Expand Up @@ -168,7 +171,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
this.WriteToOutput(releaseInfo);
}

this.UpdateVersion(context, versionOptions.Version, releaseVersion);
this.UpdateVersion(context, versionOptions.Version, releaseVersion, commitMessagePattern);
return;
}

Expand All @@ -192,7 +195,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
// create release branch and update version
Branch releaseBranch = repository.CreateBranch(releaseBranchName);
global::LibGit2Sharp.Commands.Checkout(repository, releaseBranch);
this.UpdateVersion(context, versionOptions.Version, releaseVersion);
this.UpdateVersion(context, versionOptions.Version, releaseVersion, commitMessagePattern);

if (outputMode == ReleaseManagerOutputMode.Text)
{
Expand All @@ -201,7 +204,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =

// update version on main branch
global::LibGit2Sharp.Commands.Checkout(repository, originalBranchName);
this.UpdateVersion(context, versionOptions.Version, nextDevVersion);
this.UpdateVersion(context, versionOptions.Version, nextDevVersion, commitMessagePattern);

if (outputMode == ReleaseManagerOutputMode.Text)
{
Expand Down Expand Up @@ -261,7 +264,7 @@ private string GetReleaseBranchName(VersionOptions versionOptions)
return branchNameFormat.Replace("{version}", versionOptions.Version.Version.ToString());
}

private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, SemanticVersion newVersion)
private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, SemanticVersion newVersion, string commitMessagePattern)
{
Requires.NotNull(context, nameof(context));

Expand Down Expand Up @@ -290,7 +293,8 @@ private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, S
// Author a commit only if we effectively changed something.
if (!context.Repository.Head.Tip.Tree.Equals(context.Repository.Index.WriteToTree()))
{
context.Repository.Commit($"Set version to '{versionOptions.Version}'", signature, signature, new CommitOptions() { AllowEmptyCommit = false });
string commitMessage = string.Format(commitMessagePattern, $"Set version to '{versionOptions.Version}'");
context.Repository.Commit(commitMessage, signature, signature, new CommitOptions() { AllowEmptyCommit = false });
}
}
}
Expand Down
52 changes: 49 additions & 3 deletions src/nbgv/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
Expand Down Expand Up @@ -69,6 +70,7 @@ private enum ExitCodes
ShallowClone,
InternalError,
InvalidTagNameSetting,
InvalidCommitMessagePattern,
}

private static bool AlwaysUseLibGit2 => string.Equals(Environment.GetEnvironmentVariable("NBGV_GitEngine"), "LibGit2", StringComparison.Ordinal);
Expand Down Expand Up @@ -217,6 +219,7 @@ private static Parser BuildCommandLine()
var nextVersion = new Option<string>("--nextVersion", "The version to set for the current branch. If omitted, the next version is determined automatically by incrementing the current version.");
var versionIncrement = new Option<string>("--versionIncrement", "Overrides the 'versionIncrement' setting set in version.json for determining the next version of the current branch.");
var format = new Option<string>(new[] { "--format", "-f" }, $"The format to write information about the release. Allowed values are: {string.Join(", ", SupportedFormats)}. The default is {DefaultOutputFormat}.").FromAmong(SupportedFormats);
var commitMessagePattern = new Option<string>("--commit-message-pattern", "Custom pattern to add a prefix or suffix to the default commit message.");
var tagArgument = new Argument<string>("tag", "The prerelease tag to apply on the release branch (if any). If not specified, any existing prerelease tag will be removed. The preceding hyphen may be omitted.")
{
Arity = ArgumentArity.ZeroOrOne,
Expand All @@ -227,10 +230,11 @@ private static Parser BuildCommandLine()
nextVersion,
versionIncrement,
format,
commitMessagePattern,
tagArgument,
};

prepareRelease.SetHandler(OnPrepareReleaseCommand, project, nextVersion, versionIncrement, format, tagArgument);
prepareRelease.SetHandler(OnPrepareReleaseCommand, project, nextVersion, versionIncrement, format, tagArgument, commitMessagePattern);
}

var root = new RootCommand($"{ThisAssembly.AssemblyTitle} v{ThisAssembly.AssemblyInformationalVersion}")
Expand Down Expand Up @@ -710,7 +714,7 @@ private static Task<int> OnCloudCommand(string project, string[] metadata, strin
return Task.FromResult((int)ExitCodes.OK);
}

private static Task<int> OnPrepareReleaseCommand(string project, string nextVersion, string versionIncrement, string format, string tag)
private static Task<int> OnPrepareReleaseCommand(string project, string nextVersion, string versionIncrement, string format, string tag, string commitMessagePattern)
{
// validate project path property
string searchPath = GetSpecifiedOrCurrentDirectoryPath(project);
Expand Down Expand Up @@ -763,11 +767,29 @@ private static Task<int> OnPrepareReleaseCommand(string project, string nextVers
return Task.FromResult((int)ExitCodes.UnsupportedFormat);
}

// validate commit message pattern
if (string.IsNullOrEmpty(commitMessagePattern))
{
commitMessagePattern = "{0}";
}

if (AreCurlyBracesBalanced(commitMessagePattern))
{
Console.Error.WriteLine("Commit message pattern contains unbalanced curly braces.");
return Task.FromResult((int)ExitCodes.InvalidCommitMessagePattern);
}

if (!Regex.IsMatch(commitMessagePattern, @"^(?=.*\{0\})(?!.*\{[1-9]\})(?!.*\{\{0\}\})(?!.*\{0\}.*\{0\})[^\x00-\x08\x0B\x0C\x0E-\x1F<>'\""\\]*(?:\{0\}[^\x00-\x08\x0B\x0C\x0E-\x1F<>'\""\\]*)*(?!\{[1-9]\})*$"))
{
Console.Error.WriteLine("Commit message pattern must contain only one {0} and valid commit message characters.");
return Task.FromResult((int)ExitCodes.InvalidCommitMessagePattern);
}

// run prepare-release
try
{
var releaseManager = new ReleaseManager(Console.Out, Console.Error);
releaseManager.PrepareRelease(searchPath, tag, nextVersionParsed, versionIncrementParsed, outputMode);
releaseManager.PrepareRelease(searchPath, tag, nextVersionParsed, versionIncrementParsed, outputMode, commitMessagePattern);
return Task.FromResult((int)ExitCodes.OK);
}
catch (ReleaseManager.ReleasePreparationException ex)
Expand Down Expand Up @@ -889,5 +911,29 @@ private static void PrintCommits(bool quiet, GitContext context, IEnumerable<Lib
}
}
}

private static bool AreCurlyBracesBalanced(string text)
{
var curlyBraceCount = 0;

foreach (char c in text)
{
if (c == '{')
{
curlyBraceCount++;
}
else if (c == '}')
{
curlyBraceCount--;

if (curlyBraceCount < 0)
{
return true;
}
}
}

return curlyBraceCount != 0;
}
}
}
28 changes: 28 additions & 0 deletions test/Nerdbank.GitVersioning.Tests/ReleaseManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,34 @@ public void PrepareRelease_ResetsVersionHeightOffset()
Assert.Equal(expectedReleaseVersionOptions, releaseVersion);
}

[Theory]
[InlineData("1.0-beta", "{0} Custom commit message pattern", "Set version to '1.0' Custom commit message pattern")]
[InlineData("1.0-beta", "Custom commit message pattern - {0} custom message", "Custom commit message pattern - Set version to '1.0' custom message")]
[InlineData("1.0-beta", "Custom commit message pattern - {0}", "Custom commit message pattern - Set version to '1.0'")]
[InlineData("1.0-beta", "{0}", "Set version to '1.0'")]
public void PrepareRelease_WithCustomCommitMessagePattern(string initialVersion, string commitMessagePattern, string expectedCommitMessage)
{
// Create and configure the repository
this.InitializeSourceControl();

var versionOptions = new VersionOptions()
{
Version = SemanticVersion.Parse(initialVersion),
};

this.WriteVersionFile(versionOptions);

// Run PrepareRelease with the custom commit message pattern
var releaseManager = new ReleaseManager();
releaseManager.PrepareRelease(this.RepoPath, commitMessagePattern: commitMessagePattern);

// Verify that the commit message on the release branch matches the expected pattern
string releaseBranchName = "v1.0";
Branch releaseBranch = this.LibGit2Repository.Branches[releaseBranchName];
Commit releaseBranchCommit = releaseBranch.Tip;
Assert.Equal(expectedCommitMessage, releaseBranchCommit.MessageShort);
}

/// <inheritdoc/>
protected override void InitializeSourceControl(bool withInitialCommit = true)
{
Expand Down