feat(changelog): use newly markdown parser and generate
parent
c8e5bf3ffc
commit
25ead5004d
14
main.go
14
main.go
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
[
|
|
||||||
"# CHANGELOG",
|
|
||||||
"## v1.0.0 (1-1-1)",
|
|
||||||
"### Added",
|
|
||||||
"- feat: description"
|
|
||||||
]
|
|
|
@ -1,6 +0,0 @@
|
||||||
[
|
|
||||||
"# CHANGELOG",
|
|
||||||
"## v1.0.0 (1-1-1)",
|
|
||||||
"### Fixed",
|
|
||||||
"- fix: description"
|
|
||||||
]
|
|
|
@ -1,6 +0,0 @@
|
||||||
[
|
|
||||||
"# CHANGELOG",
|
|
||||||
"## v1.0.0 (1-1-1)",
|
|
||||||
"### Others",
|
|
||||||
"- ci: description"
|
|
||||||
]
|
|
|
@ -1 +0,0 @@
|
||||||
null
|
|
|
@ -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"
|
|
||||||
]
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue