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:
parent
243d73b5c2
commit
94fff067a1
@ -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}}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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"}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user