0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-06-02 17:39:39 +02:00

refactor: remove order from templates variables and add authors

issue: #40
This commit is contained in:
Beatriz Vieira 2022-02-06 22:06:46 -03:00
parent 243d73b5c2
commit 94fff067a1
7 changed files with 97 additions and 38 deletions

View File

@ -1,6 +1,5 @@
## {{if .Release}}{{.Release}}{{end}}{{if and .Date .Release}} ({{end}}{{.Date}}{{if and .Date .Release}}){{end}}
{{- $sections := .Sections }}
{{- range $key := .Order }}
{{- template "rn-md-section.tpl" (index $sections $key) }}
{{- range $section := .Sections }}
{{- template "rn-md-section.tpl" $section }}
{{- end}}
{{- template "rn-md-section-breaking-changes.tpl" .BreakingChanges}}

View File

@ -3,15 +3,20 @@ package sv
import (
"bytes"
"io/fs"
"sort"
"strings"
"text/template"
"github.com/Masterminds/semver/v3"
)
type releaseNoteTemplateVariables struct {
Release string
Version *semver.Version
Date string
Sections map[string]ReleaseNoteSection
Order []string
Sections []ReleaseNoteSection
BreakingChanges BreakingChangeSection
AuthorNames []string
}
// OutputFormatter output formatter interface.
@ -60,17 +65,58 @@ func releaseNoteVariables(releasenote ReleaseNote) releaseNoteTemplateVariables
date = releasenote.Date.Format("2006-01-02")
}
release := ""
release := releasenote.Tag
if releasenote.Version != nil {
release = "v" + releasenote.Version.String()
} else if releasenote.Tag != "" {
release = releasenote.Tag
}
return releaseNoteTemplateVariables{
Release: release,
Version: releasenote.Version,
Date: date,
Sections: releasenote.Sections,
Order: []string{"feat", "fix", "refactor", "perf", "test", "build", "ci", "chore", "docs", "style"},
Sections: toTemplateSections(releasenote.Sections),
BreakingChanges: releasenote.BreakingChanges,
AuthorNames: toSortedArray(releasenote.AuthorsNames),
}
}
func toTemplateSections(sections map[string]ReleaseNoteSection) []ReleaseNoteSection {
result := make([]ReleaseNoteSection, len(sections))
i := 0
for _, section := range sections {
result[i] = section
i++
}
order := map[string]int{"feat": 0, "fix": 1, "refactor": 2, "perf": 3, "test": 4, "build": 5, "ci": 6, "chore": 7, "docs": 8, "style": 9}
sort.SliceStable(result, func(i, j int) bool {
priority1, disambiguity1 := priority(result[i].Types, order)
priority2, disambiguity2 := priority(result[j].Types, order)
if priority1 == priority2 {
return disambiguity1 < disambiguity2
}
return priority1 < priority2
})
return result
}
func priority(types []string, order map[string]int) (int, string) {
sort.Strings(types)
p := -1
for _, t := range types {
if p == -1 || order[t] < p {
p = order[t]
}
}
return p, strings.Join(types, "-")
}
func toSortedArray(input map[string]struct{}) []string {
result := make([]string, len(input))
i := 0
for k := range input {
result[i] = k
i++
}
sort.Strings(result)
return result
}

View File

@ -84,11 +84,11 @@ func emptyReleaseNote(tag string, date time.Time) ReleaseNote {
func fullReleaseNote(tag string, date time.Time) ReleaseNote {
v, _ := semver.NewVersion(tag)
sections := map[string]ReleaseNoteSection{
"build": newReleaseNoteSection("Build", []GitCommitLog{commitlog("build", map[string]string{})}),
"feat": newReleaseNoteSection("Features", []GitCommitLog{commitlog("feat", map[string]string{})}),
"fix": newReleaseNoteSection("Bug Fixes", []GitCommitLog{commitlog("fix", map[string]string{})}),
"build": newReleaseNoteSection("Build", []string{"build"}, []GitCommitLog{commitlog("build", map[string]string{}, "a")}),
"feat": newReleaseNoteSection("Features", []string{"feat"}, []GitCommitLog{commitlog("feat", map[string]string{}, "a")}),
"fix": newReleaseNoteSection("Bug Fixes", []string{"fix"}, []GitCommitLog{commitlog("fix", map[string]string{}, "a")}),
}
return releaseNote(v, tag, date, sections, []string{"break change message"})
return releaseNote(v, tag, date, sections, []string{"break change message"}, map[string]struct{}{"a": {}})
}
func Test_checkTemplatesExecution(t *testing.T) {
@ -119,12 +119,11 @@ func releaseNotesVariables(release string) releaseNoteTemplateVariables {
return releaseNoteTemplateVariables{
Release: release,
Date: "2006-01-02",
Sections: map[string]ReleaseNoteSection{
"build": newReleaseNoteSection("Build", []GitCommitLog{commitlog("build", map[string]string{})}),
"feat": newReleaseNoteSection("Features", []GitCommitLog{commitlog("feat", map[string]string{})}),
"fix": newReleaseNoteSection("Bug Fixes", []GitCommitLog{commitlog("fix", map[string]string{})}),
Sections: []ReleaseNoteSection{
newReleaseNoteSection("Features", []string{"feat"}, []GitCommitLog{commitlog("feat", map[string]string{}, "a")}),
newReleaseNoteSection("Bug Fixes", []string{"fix"}, []GitCommitLog{commitlog("fix", map[string]string{}, "a")}),
newReleaseNoteSection("Build", []string{"build"}, []GitCommitLog{commitlog("build", map[string]string{}, "a")}),
},
Order: []string{"feat", "fix", "refactor", "perf", "test", "build", "ci", "chore", "docs", "style"},
BreakingChanges: BreakingChangeSection{"Breaking Changes", []string{"break change message"}},
}
}

View File

@ -11,7 +11,7 @@ func version(v string) *semver.Version {
return r
}
func commitlog(ctype string, metadata map[string]string) GitCommitLog {
func commitlog(ctype string, metadata map[string]string, author string) GitCommitLog {
breaking := false
if _, found := metadata[breakingChangeMetadataKey]; found {
breaking = true
@ -23,10 +23,11 @@ func commitlog(ctype string, metadata map[string]string) GitCommitLog {
IsBreakingChange: breaking,
Metadata: metadata,
},
AuthorName: author,
}
}
func releaseNote(version *semver.Version, tag string, date time.Time, sections map[string]ReleaseNoteSection, breakingChanges []string) ReleaseNote {
func releaseNote(version *semver.Version, tag string, date time.Time, sections map[string]ReleaseNoteSection, breakingChanges []string, authorsNames map[string]struct{}) ReleaseNote {
var bchanges BreakingChangeSection
if len(breakingChanges) > 0 {
bchanges = BreakingChangeSection{Name: "Breaking Changes", Messages: breakingChanges}
@ -37,12 +38,14 @@ func releaseNote(version *semver.Version, tag string, date time.Time, sections m
Date: date.Truncate(time.Minute),
Sections: sections,
BreakingChanges: bchanges,
AuthorsNames: authorsNames,
}
}
func newReleaseNoteSection(name string, items []GitCommitLog) ReleaseNoteSection {
func newReleaseNoteSection(name string, types []string, items []GitCommitLog) ReleaseNoteSection {
return ReleaseNoteSection{
Name: name,
Types: types,
Items: items,
}
}

View File

@ -24,12 +24,14 @@ func NewReleaseNoteProcessor(cfg ReleaseNotesConfig) *ReleaseNoteProcessorImpl {
// Create create a release note based on commits.
func (p ReleaseNoteProcessorImpl) Create(version *semver.Version, tag string, date time.Time, commits []GitCommitLog) ReleaseNote {
sections := make(map[string]ReleaseNoteSection)
authors := make(map[string]struct{})
var breakingChanges []string
for _, commit := range commits {
authors[commit.AuthorName] = struct{}{}
if name, exists := p.cfg.Headers[commit.Message.Type]; exists {
section, sexists := sections[commit.Message.Type]
if !sexists {
section = ReleaseNoteSection{Name: name}
section = ReleaseNoteSection{Name: name, Types: []string{commit.Message.Type}} //TODO: change to support more than one type per section
}
section.Items = append(section.Items, commit)
sections[commit.Message.Type] = section
@ -44,7 +46,7 @@ func (p ReleaseNoteProcessorImpl) Create(version *semver.Version, tag string, da
if name, exists := p.cfg.Headers[breakingChangeMetadataKey]; exists && len(breakingChanges) > 0 {
breakingChangeSection = BreakingChangeSection{Name: name, Messages: breakingChanges}
}
return ReleaseNote{Version: version, Tag: tag, Date: date.Truncate(time.Minute), Sections: sections, BreakingChanges: breakingChangeSection}
return ReleaseNote{Version: version, Tag: tag, Date: date.Truncate(time.Minute), Sections: sections, BreakingChanges: breakingChangeSection, AuthorsNames: authors}
}
// ReleaseNote release note.
@ -54,6 +56,7 @@ type ReleaseNote struct {
Date time.Time
Sections map[string]ReleaseNoteSection
BreakingChanges BreakingChangeSection
AuthorsNames map[string]struct{}
}
// BreakingChangeSection breaking change section.
@ -65,5 +68,6 @@ type BreakingChangeSection struct {
// ReleaseNoteSection release note section.
type ReleaseNoteSection struct {
Name string
Types []string
Items []GitCommitLog
}

View File

@ -24,24 +24,32 @@ func TestReleaseNoteProcessorImpl_Create(t *testing.T) {
version: semver.MustParse("1.0.0"),
tag: "v1.0.0",
date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{})},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []GitCommitLog{commitlog("t1", map[string]string{})})}, nil),
commits: []GitCommitLog{commitlog("t1", map[string]string{}, "a")},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []string{"t1"}, []GitCommitLog{commitlog("t1", map[string]string{}, "a")})}, nil, map[string]struct{}{"a": {}}),
},
{
name: "unmapped tag",
version: semver.MustParse("1.0.0"),
tag: "v1.0.0",
date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{}), commitlog("unmapped", map[string]string{})},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []GitCommitLog{commitlog("t1", map[string]string{})})}, nil),
commits: []GitCommitLog{commitlog("t1", map[string]string{}, "a"), commitlog("unmapped", map[string]string{}, "a")},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []string{"t1"}, []GitCommitLog{commitlog("t1", map[string]string{}, "a")})}, nil, map[string]struct{}{"a": {}}),
},
{
name: "breaking changes tag",
version: semver.MustParse("1.0.0"),
tag: "v1.0.0",
date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{}), commitlog("unmapped", map[string]string{"breaking-change": "breaks"})},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []GitCommitLog{commitlog("t1", map[string]string{})})}, []string{"breaks"}),
commits: []GitCommitLog{commitlog("t1", map[string]string{}, "a"), commitlog("unmapped", map[string]string{"breaking-change": "breaks"}, "a")},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []string{"t1"}, []GitCommitLog{commitlog("t1", map[string]string{}, "a")})}, []string{"breaks"}, map[string]struct{}{"a": {}}),
},
{
name: "multiple authors",
version: semver.MustParse("1.0.0"),
tag: "v1.0.0",
date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{}, "author3"), commitlog("t1", map[string]string{}, "author2"), commitlog("t1", map[string]string{}, "author1")},
want: releaseNote(semver.MustParse("1.0.0"), "v1.0.0", date, map[string]ReleaseNoteSection{"t1": newReleaseNoteSection("Tag 1", []string{"t1"}, []GitCommitLog{commitlog("t1", map[string]string{}, "author3"), commitlog("t1", map[string]string{}, "author2"), commitlog("t1", map[string]string{}, "author1")})}, nil, map[string]struct{}{"author1": {}, "author2": {}, "author3": {}}),
},
}
for _, tt := range tests {

View File

@ -18,14 +18,14 @@ func TestSemVerCommitsProcessorImpl_NextVersion(t *testing.T) {
}{
{"no update", true, version("0.0.0"), []GitCommitLog{}, version("0.0.0"), false},
{"no update without version", true, nil, []GitCommitLog{}, nil, false},
{"no update on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.0"), false},
{"no update on unmapped known type", false, version("0.0.0"), []GitCommitLog{commitlog("none", map[string]string{})}, version("0.0.0"), false},
{"update patch on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.1"), true},
{"patch update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{})}, version("0.0.1"), true},
{"patch update without version", false, nil, []GitCommitLog{commitlog("patch", map[string]string{})}, nil, true},
{"minor update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("minor", map[string]string{})}, version("0.1.0"), true},
{"major update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("major", map[string]string{})}, version("1.0.0"), true},
{"breaking change update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("patch", map[string]string{"breaking-change": "break"})}, version("1.0.0"), true},
{"no update on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{}, "a")}, version("0.0.0"), false},
{"no update on unmapped known type", false, version("0.0.0"), []GitCommitLog{commitlog("none", map[string]string{}, "a")}, version("0.0.0"), false},
{"update patch on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{}, "a")}, version("0.0.1"), true},
{"patch update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}, "a")}, version("0.0.1"), true},
{"patch update without version", false, nil, []GitCommitLog{commitlog("patch", map[string]string{}, "a")}, nil, true},
{"minor update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}, "a"), commitlog("minor", map[string]string{}, "a")}, version("0.1.0"), true},
{"major update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}, "a"), commitlog("major", map[string]string{}, "a")}, version("1.0.0"), true},
{"breaking change update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}, "a"), commitlog("patch", map[string]string{"breaking-change": "break"}, "a")}, version("1.0.0"), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {