diff --git a/github.go b/github.go index 9040d43..f6b702e 100644 --- a/github.go +++ b/github.go @@ -100,9 +100,10 @@ func (p *githubChangeProcessor) prChange(c *change, info pullRequestInfo, pr int c.IsDeprecation = true } else if strings.HasPrefix(l.Name, "area/") { if l.Description != "" { - c.Category = l.Description - } else { - c.Category = l.Name[5:] + if c.Categories == nil { + c.Categories = map[string]struct{}{} + } + c.Categories[l.Description] = struct{}{} } } } @@ -118,6 +119,13 @@ func (p *githubChangeProcessor) prChange(c *change, info pullRequestInfo, pr int c.Link = fmt.Sprintf("https://github.com/%s/pull/%d", p.repo, pr) } c.Formatted = fmt.Sprintf("%s ([%s#%d](%s))", c.Title, p.linkName, pr, c.Link) + releaseNote := getReleaseNote(info.Body) + if releaseNote != "" { + c.Highlight = fmt.Sprintf("%s ([%s#%d](%s))", releaseNote, p.linkName, pr, c.Link) + } else { + c.Highlight = c.Formatted + } + } type pullRequestLabel struct { @@ -128,6 +136,7 @@ type pullRequestLabel struct { type pullRequestInfo struct { Title string `json:"title"` Labels []pullRequestLabel `json:"labels"` + Body string `json:"body"` } // getPRInfo returns the Pull Request info from the github API diff --git a/main.go b/main.go index ca80831..0e52931 100644 --- a/main.go +++ b/main.go @@ -41,9 +41,9 @@ type change struct { Commit string `toml:"commit"` Description string `toml:"description"` - Title string - Category string - Link string + Title string + Categories map[string]struct{} + Link string IsMerge bool IsHighlight bool @@ -51,7 +51,12 @@ type change struct { IsDeprecation bool IsSecurity bool + // Formatted is formatted string for changelog or highlights if + // no release note is provided Formatted string + + // Highlight is used to provide highlight text from a release note + Highlight string } type dependency struct { diff --git a/template.go b/template.go index 5262825..461176a 100644 --- a/template.go +++ b/template.go @@ -37,7 +37,7 @@ Welcome to the {{.Tag}} release of {{.ProjectName}}! #### {{$highlight.Name}} {{- end}} {{ range $change := $highlight.Changes}} -* {{ $change.Change.Formatted }} +* {{ $change.Change.Highlight }} {{- end}} {{- end}} {{- end}} diff --git a/util.go b/util.go index cf54eef..5a06f81 100644 --- a/util.go +++ b/util.go @@ -704,11 +704,22 @@ func groupHighlights(changes []projectChange) []highlightCategory { if c.IsSecurity { security = append(security, getHighlightChange(project.Name, c)) } else if c.IsHighlight { - cc, ok := categories[c.Category] - if !ok { - categoryList = append(categoryList, c.Category) + if len(c.Categories) == 0 { + cc, ok := categories[""] + if !ok { + categoryList = append(categoryList, "") + } + categories[""] = append(cc, getHighlightChange(project.Name, c)) + } else { + for category := range c.Categories { + cc, ok := categories[category] + if !ok { + categoryList = append(categoryList, category) + } + categories[category] = append(cc, getHighlightChange(project.Name, c)) + } + } - categories[c.Category] = append(cc, getHighlightChange(project.Name, c)) } // Allow deprecation and breaking changes to show up twice @@ -822,3 +833,18 @@ func resolveGitURL(name string, cache Cache) (string, error) { } } } + +// TODO: Support inner blocks and match multiple numbers of backticks +var releaseRe = regexp.MustCompile("(?:\r?\n|\\A)``` ?(?:release-note|markdown changelog)\r?\n((?sU).*)\r?\n```(?:\r?\n|\\z)") + +func getReleaseNote(body string) string { + matches := releaseRe.FindStringSubmatch(strings.TrimSpace(body)) + if len(matches) == 2 { + rn := strings.TrimSpace(strings.ReplaceAll(matches[1], "\r\n", "\n")) + if strings.HasPrefix(rn, "- ") || strings.HasPrefix(rn, "* ") { + rn = rn[2:] + } + return rn + } + return "" +} diff --git a/util_test.go b/util_test.go index 5af9021..f8cf2cb 100644 --- a/util_test.go +++ b/util_test.go @@ -67,3 +67,30 @@ func TestGetGitURL(t *testing.T) { } } + +func TestReleaseNote(t *testing.T) { + for i, tc := range []struct { + body string + note string + }{ + {"", ""}, + {"not a release note", ""}, + {"```release-note\nJust a release note\n```", "Just a release note"}, + {"``` release-note\nJust a release note\n```", "Just a release note"}, + {"``` release-note\nJust a release note\n```\n", "Just a release note"}, + {"``` release-note\r\nJust a release note\r\n```", "Just a release note"}, + {"``` release-note\r\nJust a release note\r\n```\r\n", "Just a release note"}, + {"``` release-note\r\nJust a\r\nrelease note\r\n```", "Just a\nrelease note"}, + {"Pull request body\n\n```release-note\nMore than a\n**`release note`**\n```", "More than a\n**`release note`**"}, + {"Pull request body\n```release-note\nThe release note\n```\nNot Actually the end", "The release note"}, + {"Poorly formatted```release-note\nThe release note\n```\n", ""}, + {"Two blocks\n```release-note\nThe release note\n```\nAnd\n\n```something-else\ncode and more code\n```", "The release note"}, + {"Two release notes\n```release-note\nThe first release note\n```\nAnd\n\n```release-note\nThe second release note\n```", "The first release note"}, + } { + note := getReleaseNote(tc.body) + if note != tc.note { + t.Errorf("[%d] unexpected release note\n\t%q\nexpected\n\t%q", i, note, tc.note) + } + } + +}