From ab4ec6792515559bd7190d26d66f9cfc5f8edf1a Mon Sep 17 00:00:00 2001 From: Nguyen Tran Hau Date: Wed, 14 Apr 2021 17:06:48 +0700 Subject: [PATCH] refactor: better changelog parser and generator (#11) * refactor(changelog): remove old, new prefix * refactor: using new changelog func in cli * refactor: replace markdown.Generate to GenerateText * feat: move --verbose flag for generate command * fix: diff new changelog with old changelog * chore(markdown): remove unused GenerateLines Co-authored-by: Tran Hau --- pkg/changelog/markdown.go | 94 ++++++------------- pkg/changelog/markdown_test.go | 31 +++--- .../empty_old_data.golden | 0 .../ignore_commits_outside_scope.golden | 0 .../many_commits.golden | 0 pkg/cli/action_generate.go | 28 ++++-- pkg/markdown/generate.go | 4 +- pkg/markdown/generate_test.go | 2 +- pkg/markdown/parse.go | 4 + 9 files changed, 68 insertions(+), 95 deletions(-) rename pkg/changelog/testdata/{TestMarkdownGeneratorGenerate => TestGenerateMarkdown}/empty_old_data.golden (100%) rename pkg/changelog/testdata/{TestMarkdownGeneratorGenerate => TestGenerateMarkdown}/ignore_commits_outside_scope.golden (100%) rename pkg/changelog/testdata/{TestMarkdownGeneratorGenerate => TestGenerateMarkdown}/many_commits.golden (100%) diff --git a/pkg/changelog/markdown.go b/pkg/changelog/markdown.go index 1aa51c3..cdef66b 100644 --- a/pkg/changelog/markdown.go +++ b/pkg/changelog/markdown.go @@ -19,51 +19,11 @@ const ( thirdLevel = 3 ) -type MarkdownGenerator struct { - oldData string - version string - t time.Time -} - -func NewMarkdownGenerator(oldData, version string, t time.Time) *MarkdownGenerator { - return &MarkdownGenerator{ - oldData: oldData, - version: version, - t: t, - } -} - -func (g *MarkdownGenerator) Generate(commits []convention.Commit, scopes map[string]struct{}) string { - newBases := g.getNewNodes(commits, scopes) - if len(newBases) == 0 { - return "" - } - - nodes := make([]markdown.Node, 0, defaultNodesLen) - - // title - nodes = append(nodes, markdown.NewHeader(firstLevel, title)) - - // version - nodes = append(nodes, markdown.NewHeader(secondLevel, g.getVersionHeader())) - - // new - nodes = append(nodes, newBases...) - - // old - oldNodes := g.getOldNodes() - nodes = append(nodes, oldNodes...) - - return markdown.Generate(nodes) -} - -func (g *MarkdownGenerator) getNewNodes(commits []convention.Commit, scopes map[string]struct{}) []markdown.Node { +func GenerateMarkdown(commits []convention.Commit, scopes map[string]struct{}, version string, t time.Time) []markdown.Node { if len(commits) == 0 { return nil } - result := make([]markdown.Node, 0, defaultNodesLen) - commitBases := make(map[string][]markdown.Node) commitBases[addedType] = make([]markdown.Node, 0, defaultNodesLen) commitBases[fixedType] = make([]markdown.Node, 0, defaultNodesLen) @@ -91,44 +51,46 @@ func (g *MarkdownGenerator) getNewNodes(commits []convention.Commit, scopes map[ } } + // Adding each type and header to nodes + nodes := make([]markdown.Node, 0, len(commitBases[addedType])+len(commitBases[fixedType])+len(commitBases[othersType])) + if len(commitBases[addedType]) != 0 { - result = append(result, markdown.NewHeader(thirdLevel, addedType)) - result = append(result, commitBases[addedType]...) + nodes = append(nodes, markdown.NewHeader(thirdLevel, addedType)) + nodes = append(nodes, commitBases[addedType]...) } if len(commitBases[fixedType]) != 0 { - result = append(result, markdown.NewHeader(thirdLevel, fixedType)) - result = append(result, commitBases[fixedType]...) + nodes = append(nodes, markdown.NewHeader(thirdLevel, fixedType)) + nodes = append(nodes, commitBases[fixedType]...) } if len(commitBases[othersType]) != 0 { - result = append(result, markdown.NewHeader(thirdLevel, othersType)) - result = append(result, commitBases[othersType]...) + nodes = append(nodes, markdown.NewHeader(thirdLevel, othersType)) + nodes = append(nodes, commitBases[othersType]...) } - return result + // Adding title, version to nodes + nodes = append([]markdown.Node{ + markdown.NewHeader(firstLevel, title), + markdown.NewHeader(secondLevel, getVersionHeader(version, t)), + }, nodes...) + + return nodes } -func (g *MarkdownGenerator) getOldNodes() []markdown.Node { - if g.oldData == "" { - return nil +func ParseMarkdown(data string) []markdown.Node { + lines := strings.Split(data, string(markdown.NewlineToken)) + nodes := markdown.Parse(lines) + + // remove title + if len(nodes) > 0 && markdown.Equal(nodes[0], markdown.NewHeader(firstLevel, title)) { + nodes = nodes[1:] } - result := make([]markdown.Node, 0, defaultNodesLen) - - lines := strings.Split(g.oldData, string(markdown.NewlineToken)) - - result = append(result, markdown.Parse(lines)...) - - // remove title as we will add later - if len(result) > 0 && markdown.Equal(result[0], markdown.NewHeader(firstLevel, title)) { - result = result[1:] - } - - return result + return nodes } -func (g *MarkdownGenerator) getVersionHeader() string { - year, month, day := g.t.Date() - return fmt.Sprintf("%s (%d-%d-%d)", g.version, year, month, day) +func getVersionHeader(version string, t time.Time) string { + year, month, day := t.Date() + return fmt.Sprintf("%s (%d-%d-%d)", version, year, month, day) } diff --git a/pkg/changelog/markdown_test.go b/pkg/changelog/markdown_test.go index 1382d15..edf963a 100644 --- a/pkg/changelog/markdown_test.go +++ b/pkg/changelog/markdown_test.go @@ -5,22 +5,20 @@ import ( "time" "github.com/haunt98/changeloguru/pkg/convention" + "github.com/haunt98/changeloguru/pkg/markdown" "github.com/sebdah/goldie/v2" ) -func TestMarkdownGeneratorGenerate(t *testing.T) { +func TestGenerateMarkdown(t *testing.T) { tests := []struct { name string - oldData string - version string - t time.Time commits []convention.Commit scopes map[string]struct{} + version string + t time.Time }{ { - name: "empty old data", - version: "v1.0.0", - t: time.Date(2020, 1, 18, 0, 0, 0, 0, time.Local), + name: "empty old data", commits: []convention.Commit{ { RawHeader: "feat: new feature", @@ -35,11 +33,11 @@ func TestMarkdownGeneratorGenerate(t *testing.T) { Type: convention.ChoreType, }, }, - }, - { - name: "many commits", version: "v1.0.0", t: time.Date(2020, 1, 18, 0, 0, 0, 0, time.Local), + }, + { + name: "many commits", commits: []convention.Commit{ { RawHeader: "feat: new feature", @@ -74,11 +72,11 @@ func TestMarkdownGeneratorGenerate(t *testing.T) { Type: convention.MiscType, }, }, + version: "v1.0.0", + t: time.Date(2020, 1, 18, 0, 0, 0, 0, time.Local), }, { - name: "ignore commits outside scope", - version: "v1.0.0", - t: time.Date(2020, 3, 22, 0, 0, 0, 0, time.Local), + name: "ignore commits outside scope", commits: []convention.Commit{ { RawHeader: "feat: new feature", @@ -116,15 +114,16 @@ func TestMarkdownGeneratorGenerate(t *testing.T) { scopes: map[string]struct{}{ "a": struct{}{}, }, + version: "v1.0.0", + t: time.Date(2020, 3, 22, 0, 0, 0, 0, time.Local), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { g := goldie.New(t) - markdownGenerator := NewMarkdownGenerator(tc.oldData, tc.version, tc.t) - got := markdownGenerator.Generate(tc.commits, tc.scopes) - g.Assert(t, t.Name(), []byte(got)) + got := GenerateMarkdown(tc.commits, tc.scopes, tc.version, tc.t) + g.Assert(t, t.Name(), []byte(markdown.GenerateText(got))) }) } } diff --git a/pkg/changelog/testdata/TestMarkdownGeneratorGenerate/empty_old_data.golden b/pkg/changelog/testdata/TestGenerateMarkdown/empty_old_data.golden similarity index 100% rename from pkg/changelog/testdata/TestMarkdownGeneratorGenerate/empty_old_data.golden rename to pkg/changelog/testdata/TestGenerateMarkdown/empty_old_data.golden diff --git a/pkg/changelog/testdata/TestMarkdownGeneratorGenerate/ignore_commits_outside_scope.golden b/pkg/changelog/testdata/TestGenerateMarkdown/ignore_commits_outside_scope.golden similarity index 100% rename from pkg/changelog/testdata/TestMarkdownGeneratorGenerate/ignore_commits_outside_scope.golden rename to pkg/changelog/testdata/TestGenerateMarkdown/ignore_commits_outside_scope.golden diff --git a/pkg/changelog/testdata/TestMarkdownGeneratorGenerate/many_commits.golden b/pkg/changelog/testdata/TestGenerateMarkdown/many_commits.golden similarity index 100% rename from pkg/changelog/testdata/TestMarkdownGeneratorGenerate/many_commits.golden rename to pkg/changelog/testdata/TestGenerateMarkdown/many_commits.golden diff --git a/pkg/cli/action_generate.go b/pkg/cli/action_generate.go index b4fae53..997056a 100644 --- a/pkg/cli/action_generate.go +++ b/pkg/cli/action_generate.go @@ -104,27 +104,35 @@ func (a *action) getVersion() (string, error) { } func (a *action) generateMarkdownChangelog(output, version string, commits []convention.Commit) error { - // If CHANGELOG file already exist - var oldData string + // If changelog file already exist, parse markdown from exist file + var oldNodes []markdown.Node bytes, err := os.ReadFile(output) if err == nil { - oldData = string(bytes) + oldNodes = changelog.ParseMarkdown(string(bytes)) } - markdownGenerator := changelog.NewMarkdownGenerator(oldData, version, time.Now()) - newData := markdownGenerator.Generate(commits, a.flags.scopes) + // Generate markdown from commits + newNodes := changelog.GenerateMarkdown(commits, a.flags.scopes, version, time.Now()) + // Final changelog + nodes := append(newNodes, oldNodes...) + changelogText := markdown.GenerateText(nodes) + + // Demo run if a.flags.dryRun { - oldLines := strings.Split(oldData, string(markdown.NewlineToken)) - newLines := strings.Split(newData, string(markdown.NewlineToken)) - if err := diff.Slices("old", "new", oldLines, newLines, os.Stdout); err != nil { - return fmt.Errorf("failed to diff old and new data: %w", err) + oldLines := strings.Split(string(bytes), string(markdown.NewlineToken)) + newLines := strings.Split(changelogText, string(markdown.NewlineToken)) + if err := diff.Slices("old", "new", + oldLines, newLines, + os.Stdout); err != nil { + return fmt.Errorf("failed to diff old and new changelog: %w", err) } return nil } - if err := os.WriteFile(output, []byte(newData), 0o644); err != nil { + // Actually writing to changelog file + if err := os.WriteFile(output, []byte(changelogText), 0644); err != nil { return fmt.Errorf("failed to write file %s: %w", output, err) } diff --git a/pkg/markdown/generate.go b/pkg/markdown/generate.go index c3326af..8253d47 100644 --- a/pkg/markdown/generate.go +++ b/pkg/markdown/generate.go @@ -2,8 +2,8 @@ package markdown import "strings" -// Generate return string which represents all markdown nodes -func Generate(bases []Node) string { +// GenerateText return single string which represents all markdown nodes +func GenerateText(bases []Node) string { lines := make([]string, len(bases)) for i, base := range bases { lines[i] = base.String() diff --git a/pkg/markdown/generate_test.go b/pkg/markdown/generate_test.go index b15fa4e..0257987 100644 --- a/pkg/markdown/generate_test.go +++ b/pkg/markdown/generate_test.go @@ -32,7 +32,7 @@ func TestGenerate(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := Generate(tc.bases) + got := GenerateText(tc.bases) assert.Equal(t, tc.want, got) }) } diff --git a/pkg/markdown/parse.go b/pkg/markdown/parse.go index 3ad34bc..4b99d85 100644 --- a/pkg/markdown/parse.go +++ b/pkg/markdown/parse.go @@ -8,6 +8,10 @@ const ( // Parse return all markdown nodes from lines func Parse(lines []string) []Node { + if len(lines) == 0 { + return nil + } + bases := make([]Node, 0, defaultBaseLen) for _, line := range lines {