feat(changelog): use newly markdown parser and generate

main
hau 2020-11-28 00:36:34 +07:00
parent c8e5bf3ffc
commit 25ead5004d
11 changed files with 72 additions and 190 deletions

14
main.go
View File

@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
@ -155,10 +156,17 @@ func (a *action) generateChangelog(c *cli.Context, path string, commits []conven
return fmt.Errorf("invalid semver %s", version)
}
markdownGenerator := changelog.NewMarkdownGenerator(changelogPath, version, time.Now())
var oldData string
bytes, err := ioutil.ReadFile(changelogPath)
if err == nil {
oldData = string(bytes)
}
if err := markdownGenerator.Generate(commits); err != nil {
return err
markdownGenerator := changelog.NewMarkdownGenerator(oldData, version, time.Now())
newData := markdownGenerator.Generate(commits)
if err := ioutil.WriteFile(changelogPath, []byte(newData), 0644); err != nil {
return fmt.Errorf("failed to write file %s: %w", changelogPath, err)
}
return nil

View File

@ -2,135 +2,113 @@ package changelog
import (
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/haunt98/changeloguru/pkg/convention"
"github.com/haunt98/changeloguru/pkg/markdown"
)
// https://guides.github.com/features/mastering-markdown/
const (
markdownTitle = "# CHANGELOG"
title = "CHANGELOG"
defaultLinesLen = 10
defaultBasesLen = 10
)
type MarkdownGenerator struct {
path string
oldData string
version string
t time.Time
}
func NewMarkdownGenerator(path string, version string, t time.Time) *MarkdownGenerator {
func NewMarkdownGenerator(oldData, version string, t time.Time) *MarkdownGenerator {
return &MarkdownGenerator{
path: path,
oldData: oldData,
version: version,
t: t,
}
}
func (g *MarkdownGenerator) Generate(commits []convention.Commit) error {
lines := g.getLines(commits)
if len(lines) == 0 {
return nil
func (g *MarkdownGenerator) Generate(commits []convention.Commit) string {
newBases := g.getNewMarkdownBases(commits)
if len(newBases) == 0 {
return ""
}
previousLines := g.getPreviousLines()
bases := make([]markdown.Base, 0, defaultBasesLen)
lines = append(lines, previousLines...)
// title
bases = append(bases, markdown.NewHeader(1, title))
if err := g.writeLines(lines); err != nil {
return err
}
// version
year, month, day := g.t.Date()
versionHeader := fmt.Sprintf("%s (%d-%d-%d)", g.version, year, month, day)
bases = append(bases, markdown.NewHeader(2, versionHeader))
return nil
// new
bases = append(bases, newBases...)
// old
oldBases := g.getOldBases()
bases = append(bases, oldBases...)
return markdown.Generate(bases)
}
func (g *MarkdownGenerator) getLines(commits []convention.Commit) []string {
func (g *MarkdownGenerator) getNewMarkdownBases(commits []convention.Commit) []markdown.Base {
if len(commits) == 0 {
return nil
}
lines := make([]string, 0, defaultLinesLen)
lines = append(lines, markdownTitle)
lines = append(lines, g.composeVersionHeader())
result := make([]markdown.Base, 0, defaultBasesLen)
addedLines := make([]string, 0, defaultLinesLen)
fixedLines := make([]string, 0, defaultLinesLen)
othersLines := make([]string, 0, defaultLinesLen)
commitBases := make(map[string][]markdown.Base)
commitBases[addedType] = make([]markdown.Base, 0, defaultBasesLen)
commitBases[fixedType] = make([]markdown.Base, 0, defaultBasesLen)
commitBases[othersType] = make([]markdown.Base, 0, defaultBasesLen)
for _, commit := range commits {
t := getType(commit.Type)
switch t {
case addedType:
addedLines = append(addedLines, g.composeListItem(commit.RawHeader))
commitBases[addedType] = append(commitBases[addedType], markdown.NewListItem(commit.RawHeader))
case fixedType:
fixedLines = append(fixedLines, g.composeListItem(commit.RawHeader))
commitBases[fixedType] = append(commitBases[fixedType], markdown.NewListItem(commit.RawHeader))
case othersType:
othersLines = append(othersLines, g.composeListItem(commit.RawHeader))
commitBases[othersType] = append(commitBases[othersType], markdown.NewListItem(commit.RawHeader))
default:
continue
}
}
if len(addedLines) != 0 {
lines = append(lines, g.composeTypeHeader(addedType))
lines = append(lines, addedLines...)
if len(commitBases[addedType]) != 0 {
result = append(result, markdown.NewHeader(3, addedType))
result = append(result, commitBases[addedType]...)
}
if len(fixedLines) != 0 {
lines = append(lines, g.composeTypeHeader(fixedType))
lines = append(lines, fixedLines...)
if len(commitBases[fixedType]) != 0 {
result = append(result, markdown.NewHeader(3, fixedType))
result = append(result, commitBases[addedType]...)
}
if len(othersLines) != 0 {
lines = append(lines, g.composeTypeHeader(othersType))
lines = append(lines, othersLines...)
if len(commitBases[othersType]) != 0 {
result = append(result, markdown.NewHeader(3, othersType))
result = append(result, commitBases[othersType]...)
}
return lines
return result
}
func (g *MarkdownGenerator) getPreviousLines() []string {
prevData, err := ioutil.ReadFile(g.path)
if err != nil {
return nil
func (g *MarkdownGenerator) getOldBases() []markdown.Base {
result := make([]markdown.Base, 0, defaultBasesLen)
lines := strings.Split(g.oldData, string(markdown.NewlineToken))
result = append(result, markdown.Parse(lines)...)
if len(result) > 0 && markdown.Equal(result[0], markdown.NewHeader(1, title)) {
result = result[1:]
}
prevLines := strings.Split(string(prevData), "\n")
finalPrevLines := make([]string, 0, len(prevLines))
for _, prevLine := range prevLines {
prevLine = strings.TrimSpace(prevLine)
if prevLine == "" || prevLine == markdownTitle {
continue
}
finalPrevLines = append(finalPrevLines, prevLine)
}
return finalPrevLines
}
func (g *MarkdownGenerator) writeLines(lines []string) error {
data := strings.Join(lines, "\n\n")
if err := ioutil.WriteFile(g.path, []byte(data), 0644); err != nil {
return err
}
return nil
}
func (g *MarkdownGenerator) composeVersionHeader() string {
year, month, day := g.t.Date()
return fmt.Sprintf("## %s (%d-%d-%d)", g.version, year, month, day)
}
func (g *MarkdownGenerator) composeTypeHeader(t string) string {
return fmt.Sprintf("### %s", t)
}
func (g *MarkdownGenerator) composeListItem(text string) string {
return fmt.Sprintf("- %s", text)
return result
}

View File

@ -1,78 +0,0 @@
package changelog
import (
"testing"
"time"
"github.com/haunt98/changeloguru/pkg/convention"
"github.com/sebdah/goldie/v2"
)
func TestMarkdownGeneratorGetLines(t *testing.T) {
tests := []struct {
name string
commits []convention.Commit
}{
{
name: "empty",
},
{
name: "1 commit feat",
commits: []convention.Commit{
{
RawHeader: "feat: description",
Type: convention.FeatType,
},
},
},
{
name: "1 commit fixed",
commits: []convention.Commit{
{
RawHeader: "fix: description",
Type: convention.FixType,
},
},
},
{
name: "1 commit other",
commits: []convention.Commit{
{
RawHeader: "ci: description",
Type: convention.CiType,
},
},
},
{
name: "mixed",
commits: []convention.Commit{
{
RawHeader: "feat: description feat",
Type: convention.FeatType,
},
{
RawHeader: "fix: description fix",
Type: convention.FixType,
},
{
RawHeader: "ci: description ci",
Type: convention.CiType,
},
{
RawHeader: "build: description build",
Type: convention.BuildType,
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
g := goldie.New(t)
generator := NewMarkdownGenerator("path", "v1.0.0", time.Time{})
lines := generator.getLines(tc.commits)
g.AssertJson(t, t.Name(), lines)
})
}
}

View File

@ -1,6 +0,0 @@
[
"# CHANGELOG",
"## v1.0.0 (1-1-1)",
"### Added",
"- feat: description"
]

View File

@ -1,6 +0,0 @@
[
"# CHANGELOG",
"## v1.0.0 (1-1-1)",
"### Fixed",
"- fix: description"
]

View File

@ -1,6 +0,0 @@
[
"# CHANGELOG",
"## v1.0.0 (1-1-1)",
"### Others",
"- ci: description"
]

View File

@ -1,11 +0,0 @@
[
"# CHANGELOG",
"## v1.0.0 (1-1-1)",
"### Added",
"- feat: description feat",
"### Fixed",
"- fix: description fix",
"### Others",
"- ci: description ci",
"- build: description build"
]

View File

@ -9,7 +9,7 @@ const (
defaultListToken = '-'
alternativeListToken = '*'
spaceToken = ' '
newlineToken = '\n'
NewlineToken = '\n'
)
// Base is single markdown syntax representation
@ -60,3 +60,7 @@ func (i listItem) String() string {
return string(defaultListToken) + string(spaceToken) + text
}
func Equal(base1, base2 Base) bool {
return base1.String() == base2.String()
}

View File

@ -9,5 +9,5 @@ func Generate(bases []Base) string {
lines[i] = base.String()
}
return strings.Join(lines, string(newlineToken)+string(newlineToken))
return strings.Join(lines, string(NewlineToken)+string(NewlineToken))
}

View File

@ -26,7 +26,7 @@ func TestGenerate(t *testing.T) {
text: "item 2",
},
},
want: "# header\n- item 1\n- item 2",
want: "# header\n\n- item 1\n\n- item 2",
},
}