0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-21 22:10:39 +00: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}} ## {{if .Release}}{{.Release}}{{end}}{{if and .Date .Release}} ({{end}}{{.Date}}{{if and .Date .Release}}){{end}}
{{- $sections := .Sections }} {{- range $section := .Sections }}
{{- range $key := .Order }} {{- template "rn-md-section.tpl" $section }}
{{- template "rn-md-section.tpl" (index $sections $key) }}
{{- end}} {{- end}}
{{- template "rn-md-section-breaking-changes.tpl" .BreakingChanges}} {{- template "rn-md-section-breaking-changes.tpl" .BreakingChanges}}

View File

@ -3,15 +3,20 @@ package sv
import ( import (
"bytes" "bytes"
"io/fs" "io/fs"
"sort"
"strings"
"text/template" "text/template"
"github.com/Masterminds/semver/v3"
) )
type releaseNoteTemplateVariables struct { type releaseNoteTemplateVariables struct {
Release string Release string
Version *semver.Version
Date string Date string
Sections map[string]ReleaseNoteSection Sections []ReleaseNoteSection
Order []string
BreakingChanges BreakingChangeSection BreakingChanges BreakingChangeSection
AuthorNames []string
} }
// OutputFormatter output formatter interface. // OutputFormatter output formatter interface.
@ -60,17 +65,58 @@ func releaseNoteVariables(releasenote ReleaseNote) releaseNoteTemplateVariables
date = releasenote.Date.Format("2006-01-02") date = releasenote.Date.Format("2006-01-02")
} }
release := "" release := releasenote.Tag
if releasenote.Version != nil { if releasenote.Version != nil {
release = "v" + releasenote.Version.String() release = "v" + releasenote.Version.String()
} else if releasenote.Tag != "" {
release = releasenote.Tag
} }
return releaseNoteTemplateVariables{ return releaseNoteTemplateVariables{
Release: release, Release: release,
Version: releasenote.Version,
Date: date, Date: date,
Sections: releasenote.Sections, Sections: toTemplateSections(releasenote.Sections),
Order: []string{"feat", "fix", "refactor", "perf", "test", "build", "ci", "chore", "docs", "style"},
BreakingChanges: releasenote.BreakingChanges, 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 { func fullReleaseNote(tag string, date time.Time) ReleaseNote {
v, _ := semver.NewVersion(tag) v, _ := semver.NewVersion(tag)
sections := map[string]ReleaseNoteSection{ sections := map[string]ReleaseNoteSection{
"build": newReleaseNoteSection("Build", []GitCommitLog{commitlog("build", map[string]string{})}), "build": newReleaseNoteSection("Build", []string{"build"}, []GitCommitLog{commitlog("build", map[string]string{}, "a")}),
"feat": newReleaseNoteSection("Features", []GitCommitLog{commitlog("feat", map[string]string{})}), "feat": newReleaseNoteSection("Features", []string{"feat"}, []GitCommitLog{commitlog("feat", map[string]string{}, "a")}),
"fix": newReleaseNoteSection("Bug Fixes", []GitCommitLog{commitlog("fix", map[string]string{})}), "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) { func Test_checkTemplatesExecution(t *testing.T) {
@ -119,12 +119,11 @@ func releaseNotesVariables(release string) releaseNoteTemplateVariables {
return releaseNoteTemplateVariables{ return releaseNoteTemplateVariables{
Release: release, Release: release,
Date: "2006-01-02", Date: "2006-01-02",
Sections: map[string]ReleaseNoteSection{ Sections: []ReleaseNoteSection{
"build": newReleaseNoteSection("Build", []GitCommitLog{commitlog("build", map[string]string{})}), newReleaseNoteSection("Features", []string{"feat"}, []GitCommitLog{commitlog("feat", map[string]string{}, "a")}),
"feat": newReleaseNoteSection("Features", []GitCommitLog{commitlog("feat", map[string]string{})}), newReleaseNoteSection("Bug Fixes", []string{"fix"}, []GitCommitLog{commitlog("fix", map[string]string{}, "a")}),
"fix": newReleaseNoteSection("Bug Fixes", []GitCommitLog{commitlog("fix", map[string]string{})}), 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"}}, BreakingChanges: BreakingChangeSection{"Breaking Changes", []string{"break change message"}},
} }
} }

View File

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

View File

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

View File

@ -24,24 +24,32 @@ func TestReleaseNoteProcessorImpl_Create(t *testing.T) {
version: semver.MustParse("1.0.0"), version: semver.MustParse("1.0.0"),
tag: "v1.0.0", tag: "v1.0.0",
date: date, date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{})}, 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", []GitCommitLog{commitlog("t1", map[string]string{})})}, nil), 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", name: "unmapped tag",
version: semver.MustParse("1.0.0"), version: semver.MustParse("1.0.0"),
tag: "v1.0.0", tag: "v1.0.0",
date: date, date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{}), commitlog("unmapped", map[string]string{})}, 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", []GitCommitLog{commitlog("t1", map[string]string{})})}, nil), 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", name: "breaking changes tag",
version: semver.MustParse("1.0.0"), version: semver.MustParse("1.0.0"),
tag: "v1.0.0", tag: "v1.0.0",
date: date, date: date,
commits: []GitCommitLog{commitlog("t1", map[string]string{}), commitlog("unmapped", map[string]string{"breaking-change": "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", []GitCommitLog{commitlog("t1", map[string]string{})})}, []string{"breaks"}), 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 { 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", true, version("0.0.0"), []GitCommitLog{}, version("0.0.0"), false},
{"no update without version", true, nil, []GitCommitLog{}, nil, 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 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{})}, 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{})}, version("0.0.1"), true}, {"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{})}, 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{})}, nil, 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{}), commitlog("minor", map[string]string{})}, version("0.1.0"), 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{}), commitlog("major", map[string]string{})}, version("1.0.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{}), commitlog("patch", map[string]string{"breaking-change": "break"})}, 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {