From a23abbd51798bb2c78fc90a1fe873e368c9a5d58 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 18 Nov 2025 13:41:37 -0500 Subject: [PATCH 1/5] Allow the sort field and sort direction to be configurable --- .../Configuration/CreateConfig.cs | 7 ++++ .../Model/SortDirection.cs | 11 +++++ .../Model/SortIssuesBy.cs | 15 +++++++ .../ConfigurationTests.cs | 41 +++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 src/GitReleaseManager.Core/Model/SortDirection.cs create mode 100644 src/GitReleaseManager.Core/Model/SortIssuesBy.cs diff --git a/src/GitReleaseManager.Core/Configuration/CreateConfig.cs b/src/GitReleaseManager.Core/Configuration/CreateConfig.cs index 06b27389..58ae6c0a 100644 --- a/src/GitReleaseManager.Core/Configuration/CreateConfig.cs +++ b/src/GitReleaseManager.Core/Configuration/CreateConfig.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using GitReleaseManager.Core.Attributes; +using GitReleaseManager.Core.Model; using YamlDotNet.Serialization; namespace GitReleaseManager.Core.Configuration @@ -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; } = SortIssuesBy.Id; + + [YamlMember(Alias = "sort-issues-direction")] + public SortDirection SortIssuesDirection { get; set; } = SortDirection.Ascending; } } \ No newline at end of file diff --git a/src/GitReleaseManager.Core/Model/SortDirection.cs b/src/GitReleaseManager.Core/Model/SortDirection.cs new file mode 100644 index 00000000..5a498eb0 --- /dev/null +++ b/src/GitReleaseManager.Core/Model/SortDirection.cs @@ -0,0 +1,11 @@ +namespace GitReleaseManager.Core.Model +{ + public enum SortDirection + { + /// Ascending. + Ascending = 0, + + /// Descending. + Descending = 1, + } +} \ No newline at end of file diff --git a/src/GitReleaseManager.Core/Model/SortIssuesBy.cs b/src/GitReleaseManager.Core/Model/SortIssuesBy.cs new file mode 100644 index 00000000..02e4b1aa --- /dev/null +++ b/src/GitReleaseManager.Core/Model/SortIssuesBy.cs @@ -0,0 +1,15 @@ +namespace GitReleaseManager.Core.Model +{ + public enum SortIssuesBy + { + /// + /// Sort by title of the issue + /// + Title, + + /// + /// Sort by the Id of the issue + /// + Id, + } +} \ No newline at end of file diff --git a/src/GitReleaseManager.Tests/ConfigurationTests.cs b/src/GitReleaseManager.Tests/ConfigurationTests.cs index 4ebfc358..e21499a3 100644 --- a/src/GitReleaseManager.Tests/ConfigurationTests.cs +++ b/src/GitReleaseManager.Tests/ConfigurationTests.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text; using GitReleaseManager.Core.Configuration; +using GitReleaseManager.Core.Model; using NUnit.Framework; namespace GitReleaseManager.Tests @@ -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}")); + } } } \ No newline at end of file From 3fdbb9a40c9d75b1d554cd67012c25610118683b Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 18 Nov 2025 13:56:18 -0500 Subject: [PATCH 2/5] Sort the issues based on configuration --- .../ReleaseNotes/ReleaseNotesBuilder.cs | 25 +++++++++++++++++-- .../ReleaseNotesBuilderIntegrationTests.cs | 4 +++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs b/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs index b8fbb815..d8f82fce 100644 --- a/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs +++ b/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs @@ -147,13 +147,34 @@ private Dictionary> GetIssuesDict(List issues) var issueLabels = _configuration.IssueLabelsInclude; var excludedIssueLabels = _configuration.IssueLabelsExclude; + Func getSortPropertyName = (sortBy) => + { + return sortBy switch + { + SortIssuesBy.Title => "Title", + SortIssuesBy.Id => "PublicNumber", + _ => throw new NotSupportedException($"'{sortBy}' is an unknown sort property."), + }; + }; + + Func keySelector = (i) => typeof(Issue).GetProperty(getSortPropertyName(_configuration.Create.SortIssuesBy)).GetValue(i, null); + 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 label 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; } diff --git a/src/GitReleaseManager.IntegrationTests/ReleaseNotesBuilderIntegrationTests.cs b/src/GitReleaseManager.IntegrationTests/ReleaseNotesBuilderIntegrationTests.cs index 2415e5c6..04c85cb3 100644 --- a/src/GitReleaseManager.IntegrationTests/ReleaseNotesBuilderIntegrationTests.cs +++ b/src/GitReleaseManager.IntegrationTests/ReleaseNotesBuilderIntegrationTests.cs @@ -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 From 09b923d6c69b1fc83fb2a1e6f22e8ccb4c054266 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 18 Nov 2025 13:58:08 -0500 Subject: [PATCH 3/5] xml comment --- src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs b/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs index d8f82fce..20a7cf76 100644 --- a/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs +++ b/src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs @@ -167,7 +167,7 @@ private Dictionary> GetIssuesDict(List issues) .OrderBy(o => o.Key) // Sort the labels alphabetically .ToDictionary(o => GetValidLabel(o.Key, o.Count()), o => { - // Sort the issues within each label group based on configuration + // Sort the issues within each group based on configuration return _configuration.Create.SortIssuesDirection switch { SortDirection.Ascending => o.OrderBy(i => keySelector(i)).ToList(), From b226e1dd03474d25da5bbacc1d49258f110e1b6b Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 18 Nov 2025 14:01:29 -0500 Subject: [PATCH 4/5] Follow the convention to set default values --- src/GitReleaseManager.Core/Configuration/Config.cs | 2 ++ src/GitReleaseManager.Core/Configuration/CreateConfig.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GitReleaseManager.Core/Configuration/Config.cs b/src/GitReleaseManager.Core/Configuration/Config.cs index 51da2a99..e25aa88f 100644 --- a/src/GitReleaseManager.Core/Configuration/Config.cs +++ b/src/GitReleaseManager.Core/Configuration/Config.cs @@ -29,6 +29,8 @@ public Config() AllowUpdateToPublishedRelease = false, AllowMilestonesWithoutIssues = false, IncludeContributors = false, + SortIssuesBy = Model.SortIssuesBy.Id, + SortIssuesDirection = Model.SortDirection.Ascending, }; Export = new ExportConfig diff --git a/src/GitReleaseManager.Core/Configuration/CreateConfig.cs b/src/GitReleaseManager.Core/Configuration/CreateConfig.cs index 58ae6c0a..65d355f0 100644 --- a/src/GitReleaseManager.Core/Configuration/CreateConfig.cs +++ b/src/GitReleaseManager.Core/Configuration/CreateConfig.cs @@ -43,9 +43,9 @@ public class CreateConfig public bool IncludeContributors { get; set; } [YamlMember(Alias = "sort-issues-by")] - public SortIssuesBy SortIssuesBy { get; set; } = SortIssuesBy.Id; + public SortIssuesBy SortIssuesBy { get; set; } [YamlMember(Alias = "sort-issues-direction")] - public SortDirection SortIssuesDirection { get; set; } = SortDirection.Ascending; + public SortDirection SortIssuesDirection { get; set; } } } \ No newline at end of file From 0dcfbef0becd641b47b995fdb11e086aa77b2944 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 18 Nov 2025 14:08:55 -0500 Subject: [PATCH 5/5] Documentation --- docs/input/docs/configuration/default-configuration.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/input/docs/configuration/default-configuration.md b/docs/input/docs/configuration/default-configuration.md index 79b90b78..9dbe9a86 100644 --- a/docs/input/docs/configuration/default-configuration.md +++ b/docs/input/docs/configuration/default-configuration.md @@ -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.