mirror of
https://github.com/thegeeklab/git-sv.git
synced 2024-11-21 12:00:40 +00:00
refactor: remove order from templates variables and add authors
issue: #40
This commit is contained in:
parent
243d73b5c2
commit
94fff067a1
@ -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}}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"}},
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user