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 ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -155,10 +156,17 @@ func (a *action) generateChangelog(c *cli.Context, path string, commits []conven
return fmt.Errorf("invalid semver %s", version) 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 { markdownGenerator := changelog.NewMarkdownGenerator(oldData, version, time.Now())
return err 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 return nil

View File

@ -2,135 +2,113 @@ package changelog
import ( import (
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"time" "time"
"github.com/haunt98/changeloguru/pkg/convention" "github.com/haunt98/changeloguru/pkg/convention"
"github.com/haunt98/changeloguru/pkg/markdown"
) )
// https://guides.github.com/features/mastering-markdown/
const ( const (
markdownTitle = "# CHANGELOG" title = "CHANGELOG"
defaultLinesLen = 10 defaultBasesLen = 10
) )
type MarkdownGenerator struct { type MarkdownGenerator struct {
path string oldData string
version string version string
t time.Time t time.Time
} }
func NewMarkdownGenerator(path string, version string, t time.Time) *MarkdownGenerator { func NewMarkdownGenerator(oldData, version string, t time.Time) *MarkdownGenerator {
return &MarkdownGenerator{ return &MarkdownGenerator{
path: path, oldData: oldData,
version: version, version: version,
t: t, t: t,
} }
} }
func (g *MarkdownGenerator) Generate(commits []convention.Commit) error { func (g *MarkdownGenerator) Generate(commits []convention.Commit) string {
lines := g.getLines(commits) newBases := g.getNewMarkdownBases(commits)
if len(lines) == 0 { if len(newBases) == 0 {
return nil 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 { // version
return err 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))
// new
bases = append(bases, newBases...)
// old
oldBases := g.getOldBases()
bases = append(bases, oldBases...)
return markdown.Generate(bases)
} }
return nil func (g *MarkdownGenerator) getNewMarkdownBases(commits []convention.Commit) []markdown.Base {
}
func (g *MarkdownGenerator) getLines(commits []convention.Commit) []string {
if len(commits) == 0 { if len(commits) == 0 {
return nil return nil
} }
lines := make([]string, 0, defaultLinesLen) result := make([]markdown.Base, 0, defaultBasesLen)
lines = append(lines, markdownTitle)
lines = append(lines, g.composeVersionHeader())
addedLines := make([]string, 0, defaultLinesLen) commitBases := make(map[string][]markdown.Base)
fixedLines := make([]string, 0, defaultLinesLen) commitBases[addedType] = make([]markdown.Base, 0, defaultBasesLen)
othersLines := make([]string, 0, defaultLinesLen) commitBases[fixedType] = make([]markdown.Base, 0, defaultBasesLen)
commitBases[othersType] = make([]markdown.Base, 0, defaultBasesLen)
for _, commit := range commits { for _, commit := range commits {
t := getType(commit.Type) t := getType(commit.Type)
switch t { switch t {
case addedType: case addedType:
addedLines = append(addedLines, g.composeListItem(commit.RawHeader)) commitBases[addedType] = append(commitBases[addedType], markdown.NewListItem(commit.RawHeader))
case fixedType: case fixedType:
fixedLines = append(fixedLines, g.composeListItem(commit.RawHeader)) commitBases[fixedType] = append(commitBases[fixedType], markdown.NewListItem(commit.RawHeader))
case othersType: case othersType:
othersLines = append(othersLines, g.composeListItem(commit.RawHeader)) commitBases[othersType] = append(commitBases[othersType], markdown.NewListItem(commit.RawHeader))
default: default:
continue continue
} }
} }
if len(addedLines) != 0 { if len(commitBases[addedType]) != 0 {
lines = append(lines, g.composeTypeHeader(addedType)) result = append(result, markdown.NewHeader(3, addedType))
lines = append(lines, addedLines...) result = append(result, commitBases[addedType]...)
} }
if len(fixedLines) != 0 { if len(commitBases[fixedType]) != 0 {
lines = append(lines, g.composeTypeHeader(fixedType)) result = append(result, markdown.NewHeader(3, fixedType))
lines = append(lines, fixedLines...) result = append(result, commitBases[addedType]...)
} }
if len(othersLines) != 0 { if len(commitBases[othersType]) != 0 {
lines = append(lines, g.composeTypeHeader(othersType)) result = append(result, markdown.NewHeader(3, othersType))
lines = append(lines, othersLines...) result = append(result, commitBases[othersType]...)
} }
return lines return result
} }
func (g *MarkdownGenerator) getPreviousLines() []string { func (g *MarkdownGenerator) getOldBases() []markdown.Base {
prevData, err := ioutil.ReadFile(g.path) result := make([]markdown.Base, 0, defaultBasesLen)
if err != nil {
return nil 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") return result
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)
} }

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 = '-' defaultListToken = '-'
alternativeListToken = '*' alternativeListToken = '*'
spaceToken = ' ' spaceToken = ' '
newlineToken = '\n' NewlineToken = '\n'
) )
// Base is single markdown syntax representation // Base is single markdown syntax representation
@ -60,3 +60,7 @@ func (i listItem) String() string {
return string(defaultListToken) + string(spaceToken) + text 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() 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", text: "item 2",
}, },
}, },
want: "# header\n- item 1\n- item 2", want: "# header\n\n- item 1\n\n- item 2",
}, },
} }