Skip to content

Commit 97c562a

Browse files
committed
Fix changelog parsing
1 parent 175d414 commit 97c562a

File tree

4 files changed

+208
-25
lines changed

4 files changed

+208
-25
lines changed

build/Changelog.cs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2024 © Sandro Figo
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Text;
6+
using System.Text.RegularExpressions;
7+
using Nuke.Common.IO;
8+
using Nuke.Common.Utilities;
9+
using Nuke.Common.Utilities.Collections;
10+
11+
public class Changelog
12+
{
13+
public ChangelogTextSection Header;
14+
public List<ChangelogReleaseSection> Sections = new();
15+
16+
public static Changelog FromFile(AbsolutePath changelogFile)
17+
{
18+
string[] lines = changelogFile.ReadAllLines();
19+
20+
return new Changelog
21+
{
22+
Header = GetHeader(lines),
23+
Sections = GetSections(lines)
24+
};
25+
}
26+
27+
static bool IsSectionStart(string line)
28+
{
29+
return line.StartsWith("## ");
30+
}
31+
32+
static bool IsSubSectionStart(string line)
33+
{
34+
return line.StartsWith("### ");
35+
}
36+
37+
static bool IsSubSectionEntry(string line)
38+
{
39+
return line.StartsWith("-");
40+
}
41+
42+
static bool IsVersionComparison(string line)
43+
{
44+
return line.StartsWith("[");
45+
}
46+
47+
static ChangelogTextSection GetHeader(IEnumerable<string> lines)
48+
{
49+
var header = new ChangelogTextSection();
50+
51+
foreach (string l in lines)
52+
{
53+
if (IsSectionStart(l))
54+
{
55+
return header;
56+
}
57+
58+
header.Lines.Add(l);
59+
}
60+
61+
return header;
62+
}
63+
64+
65+
static List<ChangelogReleaseSection> GetSections(IEnumerable<string> lines)
66+
{
67+
var sections = new List<ChangelogReleaseSection>();
68+
69+
string currentSubSection = "";
70+
71+
foreach (string l in lines)
72+
{
73+
if (IsSectionStart(l))
74+
{
75+
string currentSectionVersion = Regex.Match(l, @"\[(?<version>.*)\]").Groups["version"].Value.Trim();
76+
string currentSectionReleaseDate = Regex.Match(l, @"(?<release_date>\d+-\d+-\d+)").Groups["release_date"].Value.Trim();
77+
78+
sections.Add(new ChangelogReleaseSection
79+
{
80+
Version = currentSectionVersion,
81+
ReleaseDate = currentSectionReleaseDate
82+
});
83+
84+
currentSubSection = "";
85+
}
86+
else if (IsSubSectionStart(l) && sections.Count > 0)
87+
{
88+
string subSectionName = l.Replace("###", "").Trim().ToLowerInvariant();
89+
90+
currentSubSection = subSectionName;
91+
}
92+
else if (IsSubSectionEntry(l) && !currentSubSection.IsNullOrWhiteSpace())
93+
{
94+
string entry = l.TrimStart("-").Trim();
95+
96+
if (Enum.TryParse(currentSubSection, true, out EntryType entryType))
97+
{
98+
sections[^1].Entries[entryType].Add(entry);
99+
}
100+
}
101+
}
102+
103+
return sections;
104+
}
105+
106+
public override string ToString()
107+
{
108+
var stringBuilder = new StringBuilder();
109+
110+
if (Header != null)
111+
stringBuilder.AppendLine(Header.ToString());
112+
113+
stringBuilder.AppendJoin(System.Environment.NewLine, Sections);
114+
115+
return stringBuilder.ToString();
116+
}
117+
}
118+
119+
public class ChangelogReleaseSection
120+
{
121+
public string Version { get; set; }
122+
public string ReleaseDate { get; set; }
123+
124+
public Dictionary<EntryType, List<string>> Entries { get; set; } = new()
125+
{
126+
{ EntryType.Added, new List<string>() },
127+
{ EntryType.Changed, new List<string>() },
128+
{ EntryType.Deprecated, new List<string>() },
129+
{ EntryType.Removed, new List<string>() },
130+
{ EntryType.Fixed, new List<string>() },
131+
{ EntryType.Security, new List<string>() }
132+
};
133+
134+
public override string ToString()
135+
{
136+
var lines = new List<string>();
137+
138+
lines.Add($"## [{Version}]{(ReleaseDate.IsNullOrWhiteSpace() ? "" : $" - {ReleaseDate}")}");
139+
lines.Add("");
140+
141+
foreach (var group in Entries)
142+
{
143+
if (!group.Value.IsEmpty())
144+
{
145+
lines.Add($"### {group.Key}");
146+
lines.Add("");
147+
foreach (string s in group.Value)
148+
lines.Add($"- {s}");
149+
lines.Add("");
150+
}
151+
}
152+
153+
var stringBuilder = new StringBuilder();
154+
155+
return stringBuilder.AppendJoin(System.Environment.NewLine, lines).ToString();
156+
}
157+
}
158+
159+
public class ChangelogTextSection
160+
{
161+
public List<string> Lines { get; } = new();
162+
163+
public override string ToString()
164+
{
165+
var stringBuilder = new StringBuilder();
166+
167+
stringBuilder.AppendJoin(System.Environment.NewLine, Lines);
168+
169+
return stringBuilder.ToString();
170+
}
171+
}
172+
173+
public enum EntryType
174+
{
175+
Added,
176+
Changed,
177+
Deprecated,
178+
Removed,
179+
Fixed,
180+
Security,
181+
}

build/ChangelogExtensions.cs

Lines changed: 0 additions & 11 deletions
This file was deleted.

build/IChangelogVersionMatchesGitTagVersion.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using NuGet.Versioning;
1+
using System.Linq;
2+
using NuGet.Versioning;
23
using Nuke.Common;
3-
using Nuke.Common.ChangeLog;
44
using Nuke.Common.Git;
55
using Nuke.Common.IO;
66
using Serilog;
@@ -28,6 +28,6 @@ static SemanticVersion GetLatestVersionFromChangelog(AbsolutePath pathToChangelo
2828
{
2929
Assert.True(pathToChangelogFile != null, "No path has been provided!");
3030

31-
return ChangelogTasks.ReadChangelog(pathToChangelogFile).GetLatestReleaseNotes()?.Version;
31+
return SemanticVersion.Parse(Changelog.FromFile(pathToChangelogFile).Sections.First().Version);
3232
}
3333
}

build/IPublishGitHubRelease.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
using System;
2-
using System.Linq;
1+
using System.Linq;
2+
using System.Text;
33
using NuGet.Versioning;
44
using Nuke.Common;
5-
using Nuke.Common.ChangeLog;
65
using Nuke.Common.CI.GitHubActions;
76
using Nuke.Common.Git;
87
using Nuke.Common.IO;
98
using Nuke.Common.Tools.GitHub;
10-
using Nuke.Common.Utilities.Collections;
119
using Octokit;
10+
using Octokit.Internal;
1211
using Serilog;
1312

1413
[ParameterPrefix(nameof(PublishGitHubRelease))]
1514
interface IPublishGitHubRelease : INukeBuild
1615
{
1716
[GitRepository] private GitRepository GitRepository => TryGetValue(() => GitRepository);
1817

19-
[Parameter] AbsolutePath ChangelogFile => TryGetValue(() => ChangelogFile);
18+
[Nuke.Common.Parameter] AbsolutePath ChangelogFile => TryGetValue(() => ChangelogFile);
2019

2120
Target PublishGitHubRelease => _ => _
2221
.OnlyWhenStatic(() => GitRepository.CurrentCommitHasVersionTag())
@@ -27,11 +26,25 @@ interface IPublishGitHubRelease : INukeBuild
2726
{
2827
Assert.True(ChangelogFile != null, "No path has been provided!");
2928

30-
ChangeLog changelog = ChangelogTasks.ReadChangelog(ChangelogFile);
31-
ReleaseNotes latestReleaseNotes = changelog.GetLatestReleaseNotes();
32-
var trimmedNotes = latestReleaseNotes.Notes.SkipUntil(n => !string.IsNullOrWhiteSpace(n)).Reverse().SkipUntil(n => !string.IsNullOrWhiteSpace(n)).Reverse();
29+
Changelog changelog = Changelog.FromFile(ChangelogFile);
3330

34-
string changelogBody = string.Join(Environment.NewLine, trimmedNotes);
31+
var changelogBody = new StringBuilder();
32+
33+
foreach (var entry in changelog.Sections.First().Entries)
34+
{
35+
if (entry.Value.Count == 0)
36+
continue;
37+
38+
changelogBody.AppendLine($"### {entry.Key.ToString()}");
39+
changelogBody.AppendLine();
40+
41+
foreach (string s in entry.Value)
42+
{
43+
changelogBody.AppendLine(s);
44+
}
45+
46+
changelogBody.AppendLine();
47+
}
3548

3649
SemanticVersion version = GitRepository.GetLatestVersionTagOnCurrentCommit();
3750

@@ -40,14 +53,14 @@ interface IPublishGitHubRelease : INukeBuild
4053
Draft = true,
4154
Name = $"v{version}",
4255
Prerelease = version.IsPrerelease,
43-
Body = changelogBody
56+
Body = changelogBody.ToString()
4457
};
4558

4659
string owner = GitRepository.GetGitHubOwner();
4760
string name = GitRepository.GetGitHubName();
4861

4962
var credentials = new Credentials(GitHubActions.Instance.Token);
50-
GitHubTasks.GitHubClient = new GitHubClient(new ProductHeaderValue(nameof(NukeBuild)), new Octokit.Internal.InMemoryCredentialStore(credentials));
63+
GitHubTasks.GitHubClient = new GitHubClient(new ProductHeaderValue(nameof(NukeBuild)), new InMemoryCredentialStore(credentials));
5164

5265
Log.Information("Creating GitHub release...");
5366

0 commit comments

Comments
 (0)