Skip to content
8 changes: 8 additions & 0 deletions docs/input/docs/configuration/default-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ control the look and feel of the generated release notes.
no issues are found to be associated with a milestone. The contents of the
empty release can be controlled via the associated Scriban template.
**NOTE:** This configuration option was added in version 0.20.0 of GitReleaseManager.
- **sort-issues-by**
- A string value which indicates the name of the field used to sort the issues. The
possible values are: Title, Id.
**NOTE:** This configuration option was added in version 0.21.0 of GitReleaseManager.
- **sort-issues-direction**
- A string value which indicates how the issues are sorted. The posible values are:
Ascending, Descending.
**NOTE:** This configuration option was added in version 0.21.0 of GitReleaseManager.

See the [example create configuration section](create-configuration) to see an
example of how a footer can be configured.
Expand Down
2 changes: 2 additions & 0 deletions src/GitReleaseManager.Core/Configuration/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public Config()
AllowUpdateToPublishedRelease = false,
AllowMilestonesWithoutIssues = false,
IncludeContributors = false,
SortIssuesBy = Model.SortIssuesBy.Id,
SortIssuesDirection = Model.SortDirection.Ascending,
};

Export = new ExportConfig
Expand Down
7 changes: 7 additions & 0 deletions src/GitReleaseManager.Core/Configuration/CreateConfig.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel;
using GitReleaseManager.Core.Attributes;
using GitReleaseManager.Core.Model;
using YamlDotNet.Serialization;

namespace GitReleaseManager.Core.Configuration
Expand Down Expand Up @@ -40,5 +41,11 @@ public class CreateConfig

[YamlMember(Alias = "include-contributors")]
public bool IncludeContributors { get; set; }

[YamlMember(Alias = "sort-issues-by")]
public SortIssuesBy SortIssuesBy { get; set; }

[YamlMember(Alias = "sort-issues-direction")]
public SortDirection SortIssuesDirection { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/GitReleaseManager.Core/Model/SortDirection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace GitReleaseManager.Core.Model
{
public enum SortDirection
{
/// <summary>Ascending.</summary>
Ascending = 0,

/// <summary>Descending.</summary>
Descending = 1,
}
}
15 changes: 15 additions & 0 deletions src/GitReleaseManager.Core/Model/SortIssuesBy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace GitReleaseManager.Core.Model
{
public enum SortIssuesBy
{
/// <summary>
/// Sort by title of the issue
/// </summary>
Title,

/// <summary>
/// Sort by the Id of the issue
/// </summary>
Id,
}
}
25 changes: 23 additions & 2 deletions src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,34 @@
var issueLabels = _configuration.IssueLabelsInclude;
var excludedIssueLabels = _configuration.IssueLabelsExclude;

Func<SortIssuesBy, string> getSortPropertyName = (sortBy) =>
{
return sortBy switch
{
SortIssuesBy.Title => "Title",
SortIssuesBy.Id => "PublicNumber",
_ => throw new NotSupportedException($"'{sortBy}' is an unknown sort property."),
};
};

Func<Issue, object?> keySelector = (i) => typeof(Issue).GetProperty(getSortPropertyName(_configuration.Create.SortIssuesBy)).GetValue(i, null);

Check warning on line 160 in src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

var issuesByLabel = issues
.Where(o => !o.Labels.Any(l => excludedIssueLabels.Any(eil => string.Equals(eil, l.Name, StringComparison.OrdinalIgnoreCase))))
.SelectMany(o => o.Labels, (issue, label) => new { Label = label.Name, Issue = issue })
.Where(o => issueLabels.Any(il => string.Equals(il, o.Label, StringComparison.OrdinalIgnoreCase)))
.GroupBy(o => o.Label, o => o.Issue)
.OrderBy(o => o.Key)
.ToDictionary(o => GetValidLabel(o.Key, o.Count()), o => o.OrderBy(issue => issue.PublicNumber).ToList());
.OrderBy(o => o.Key) // Sort the labels alphabetically
.ToDictionary(o => GetValidLabel(o.Key, o.Count()), o =>
{
// Sort the issues within each group based on configuration
return _configuration.Create.SortIssuesDirection switch
{
SortDirection.Ascending => o.OrderBy(i => keySelector(i)).ToList(),
SortDirection.Descending => o.OrderByDescending(i => keySelector(i)).ToList(),
_ => throw new NotSupportedException($"'{_configuration.Create.SortIssuesDirection}' is an unknown sort direction."),
};
});

return issuesByLabel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public async Task SingleMilestone()
// Indicate that we want to include the 'Contributors' section in the release notes
configuration.Create.IncludeContributors = true;

// Configure sorting
configuration.Create.SortIssuesBy = Core.Model.SortIssuesBy.Title;
configuration.Create.SortIssuesDirection = Core.Model.SortDirection.Descending;

var vcsProvider = new GitHubProvider(_gitHubClient, _mapper, _graphQlClient);
var releaseNotesBuilder = new ReleaseNotesBuilder(vcsProvider, _logger, fileSystem, configuration, new TemplateFactory(fileSystem, configuration, TemplateKind.Create));
var result = await releaseNotesBuilder.BuildReleaseNotesAsync("GitTools", "GitReleaseManager", "0.12.0", string.Empty).ConfigureAwait(false); // 0.12.0 contains a mix of issues and PRs
Expand Down
41 changes: 41 additions & 0 deletions src/GitReleaseManager.Tests/ConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Text;
using GitReleaseManager.Core.Configuration;
using GitReleaseManager.Core.Model;
using NUnit.Framework;

namespace GitReleaseManager.Tests
Expand Down Expand Up @@ -127,5 +128,45 @@ public void Should_WriteSample_Multiline_String_Values()
Environment.NewLine);
Assert.That(text, Contains.Substring(expectedText));
}

[Test]
[TestCase(SortIssuesBy.Id, "Id")]
[TestCase(SortIssuesBy.Title, "Title")]
public void Should_Write_SortIssuesBy_Values(SortIssuesBy sortBy, string expected)
{
// Given
var config = new Config();
config.Create.SortIssuesBy = sortBy;

// When
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
ConfigSerializer.Write(config, writer);
}

// Then
Assert.That(builder.ToString(), Contains.Substring($"sort-issues-by: {expected}"));
}

[Test]
[TestCase(SortDirection.Ascending, "Ascending")]
[TestCase(SortDirection.Descending, "Descending")]
public void Should_Write_SortIssuesDirection_Values(SortDirection sortDirection, string expected)
{
// Given
var config = new Config();
config.Create.SortIssuesDirection = sortDirection;

// When
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
ConfigSerializer.Write(config, writer);
}

// Then
Assert.That(builder.ToString(), Contains.Substring($"sort-issues-direction: {expected}"));
}
}
}
Loading