From b5d9f9c535bb418aa0b65f4b4f9a5d5f4f7a95f8 Mon Sep 17 00:00:00 2001 From: Beatriz Vieira Date: Sun, 30 Jan 2022 18:45:16 -0300 Subject: [PATCH 1/2] refactor: extract templates from code and use go:embed issue: #40 --- sv/formatter.go | 53 +++------------ sv/formatter_test.go | 68 +++++++++++++++++++ sv/resources/templates/changelog-md.tpl | 6 ++ sv/resources/templates/releasenotes-md.tpl | 6 ++ .../rn-md-section-breaking-changes.tpl | 7 ++ sv/resources/templates/rn-md-section-item.tpl | 1 + sv/resources/templates/rn-md-section.tpl | 7 ++ 7 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 sv/resources/templates/changelog-md.tpl create mode 100644 sv/resources/templates/releasenotes-md.tpl create mode 100644 sv/resources/templates/rn-md-section-breaking-changes.tpl create mode 100644 sv/resources/templates/rn-md-section-item.tpl create mode 100644 sv/resources/templates/rn-md-section.tpl diff --git a/sv/formatter.go b/sv/formatter.go index 9d93367..896de9f 100644 --- a/sv/formatter.go +++ b/sv/formatter.go @@ -2,6 +2,7 @@ package sv import ( "bytes" + "embed" "text/template" ) @@ -13,40 +14,9 @@ type releaseNoteTemplateVariables struct { BreakingChanges BreakingChangeSection } -const ( - cglTemplate = `# Changelog -{{- range .}} - -{{template "rnTemplate" .}} ---- -{{- end}} -` - - rnSectionItem = "- {{if .Message.Scope}}**{{.Message.Scope}}:** {{end}}{{.Message.Description}} ({{.Hash}}){{if .Message.Metadata.issue}} ({{.Message.Metadata.issue}}){{end}}" - - rnSection = `{{- if .}}{{- if ne .Name ""}} - -### {{.Name}} -{{range $k,$v := .Items}} -{{template "rnSectionItem" $v}} -{{- end}} -{{- end}}{{- end}}` - - rnSectionBreakingChanges = `{{- if ne .Name ""}} - -### {{.Name}} -{{range $k,$v := .Messages}} -- {{$v}} -{{- end}} -{{- end}}` - - rnTemplate = `## {{if .Release}}{{.Release}}{{end}}{{if and .Date .Release}} ({{end}}{{.Date}}{{if and .Date .Release}}){{end}} -{{- $sections := .Sections }} -{{- range $key := .Order }} -{{- template "rnSection" (index $sections $key) }} -{{- end}} -{{- template "rnSectionBreakingChanges" .BreakingChanges}} -` +var ( + //go:embed resources/templates/* + defaultTemplatesFS embed.FS ) // OutputFormatter output formatter interface. @@ -57,24 +27,19 @@ type OutputFormatter interface { // OutputFormatterImpl formater for release note and changelog. type OutputFormatterImpl struct { - releasenoteTemplate *template.Template - changelogTemplate *template.Template + templates *template.Template } // NewOutputFormatter TemplateProcessor constructor. func NewOutputFormatter() *OutputFormatterImpl { - cgl := template.Must(template.New("cglTemplate").Parse(cglTemplate)) - rn := template.Must(cgl.New("rnTemplate").Parse(rnTemplate)) - template.Must(rn.New("rnSectionItem").Parse(rnSectionItem)) - template.Must(rn.New("rnSection").Parse(rnSection)) - template.Must(rn.New("rnSectionBreakingChanges").Parse(rnSectionBreakingChanges)) - return &OutputFormatterImpl{releasenoteTemplate: rn, changelogTemplate: cgl} + tpls := template.Must(template.New("templates").ParseFS(defaultTemplatesFS, "resources/templates/*")) + return &OutputFormatterImpl{templates: tpls} } // FormatReleaseNote format a release note. func (p OutputFormatterImpl) FormatReleaseNote(releasenote ReleaseNote) (string, error) { var b bytes.Buffer - if err := p.releasenoteTemplate.Execute(&b, releaseNoteVariables(releasenote)); err != nil { + if err := p.templates.ExecuteTemplate(&b, "releasenotes-md.tpl", releaseNoteVariables(releasenote)); err != nil { return "", err } return b.String(), nil @@ -88,7 +53,7 @@ func (p OutputFormatterImpl) FormatChangelog(releasenotes []ReleaseNote) (string } var b bytes.Buffer - if err := p.changelogTemplate.Execute(&b, templateVars); err != nil { + if err := p.templates.ExecuteTemplate(&b, "changelog-md.tpl", templateVars); err != nil { return "", err } return b.String(), nil diff --git a/sv/formatter_test.go b/sv/formatter_test.go index 73a5104..eabf4f5 100644 --- a/sv/formatter_test.go +++ b/sv/formatter_test.go @@ -1,7 +1,9 @@ package sv import ( + "bytes" "testing" + "text/template" "time" "github.com/Masterminds/semver/v3" @@ -85,3 +87,69 @@ func fullReleaseNote(tag string, date time.Time) ReleaseNote { } return releaseNote(v, tag, date, sections, []string{"break change message"}) } + +func releaseNotesVariables(release string) releaseNoteTemplateVariables { + return releaseNoteTemplateVariables{ + Release: release, + Date: "2006-01-02", + Sections: map[string]ReleaseNoteSection{ + "build": newReleaseNoteSection("Build", []GitCommitLog{commitlog("build", map[string]string{})}), + "feat": newReleaseNoteSection("Features", []GitCommitLog{commitlog("feat", map[string]string{})}), + "fix": newReleaseNoteSection("Bug Fixes", []GitCommitLog{commitlog("fix", map[string]string{})}), + }, + Order: []string{"feat", "fix", "refactor", "perf", "test", "build", "ci", "chore", "docs", "style"}, + BreakingChanges: BreakingChangeSection{"Breaking Changes", []string{"break change message"}}, + } +} + +func changelogVariables(releases ...string) []releaseNoteTemplateVariables { + var variables []releaseNoteTemplateVariables + for _, r := range releases { + variables = append(variables, releaseNotesVariables(r)) + } + return variables + +} + +func Test_checkTemplatesFiles(t *testing.T) { + tests := []string{ + "resources/templates/changelog-md.tpl", + "resources/templates/releasenotes-md.tpl", + } + for _, tt := range tests { + t.Run(tt, func(t *testing.T) { + got, err := defaultTemplatesFS.ReadFile(tt) + if err != nil { + t.Errorf("missing template error = %v", err) + return + } + if len(got) <= 0 { + t.Errorf("empty template") + } + }) + } +} + +func Test_checkTemplatesExecution(t *testing.T) { + tpls := template.Must(template.New("templates").ParseFS(defaultTemplatesFS, "resources/templates/*")) + tests := []struct { + template string + variables interface{} + }{ + {"changelog-md.tpl", changelogVariables("v1.0.0", "v1.0.1")}, + {"releasenotes-md.tpl", releaseNotesVariables("v1.0.0")}, + } + for _, tt := range tests { + t.Run(tt.template, func(t *testing.T) { + var b bytes.Buffer + err := tpls.ExecuteTemplate(&b, tt.template, tt.variables) + if err != nil { + t.Errorf("invalid template err = %v", err) + return + } + if len(b.Bytes()) <= 0 { + t.Errorf("empty template") + } + }) + } +} diff --git a/sv/resources/templates/changelog-md.tpl b/sv/resources/templates/changelog-md.tpl new file mode 100644 index 0000000..d478272 --- /dev/null +++ b/sv/resources/templates/changelog-md.tpl @@ -0,0 +1,6 @@ +# Changelog +{{- range .}} + +{{template "releasenotes-md.tpl" .}} +--- +{{- end}} \ No newline at end of file diff --git a/sv/resources/templates/releasenotes-md.tpl b/sv/resources/templates/releasenotes-md.tpl new file mode 100644 index 0000000..440cb6b --- /dev/null +++ b/sv/resources/templates/releasenotes-md.tpl @@ -0,0 +1,6 @@ +## {{if .Release}}{{.Release}}{{end}}{{if and .Date .Release}} ({{end}}{{.Date}}{{if and .Date .Release}}){{end}} +{{- $sections := .Sections }} +{{- range $key := .Order }} +{{- template "rn-md-section.tpl" (index $sections $key) }} +{{- end}} +{{- template "rn-md-section-breaking-changes.tpl" .BreakingChanges}} diff --git a/sv/resources/templates/rn-md-section-breaking-changes.tpl b/sv/resources/templates/rn-md-section-breaking-changes.tpl new file mode 100644 index 0000000..862b1dc --- /dev/null +++ b/sv/resources/templates/rn-md-section-breaking-changes.tpl @@ -0,0 +1,7 @@ +{{- if ne .Name ""}} + +### {{.Name}} +{{range $k,$v := .Messages}} +- {{$v}} +{{- end}} +{{- end}} \ No newline at end of file diff --git a/sv/resources/templates/rn-md-section-item.tpl b/sv/resources/templates/rn-md-section-item.tpl new file mode 100644 index 0000000..f0783a3 --- /dev/null +++ b/sv/resources/templates/rn-md-section-item.tpl @@ -0,0 +1 @@ +- {{if .Message.Scope}}**{{.Message.Scope}}:** {{end}}{{.Message.Description}} ({{.Hash}}){{if .Message.Metadata.issue}} ({{.Message.Metadata.issue}}){{end}} \ No newline at end of file diff --git a/sv/resources/templates/rn-md-section.tpl b/sv/resources/templates/rn-md-section.tpl new file mode 100644 index 0000000..32c7759 --- /dev/null +++ b/sv/resources/templates/rn-md-section.tpl @@ -0,0 +1,7 @@ +{{- if .}}{{- if ne .Name ""}} + +### {{.Name}} +{{range $k,$v := .Items}} +{{template "rn-md-section-item.tpl" $v}} +{{- end}} +{{- end}}{{- end}} \ No newline at end of file From 0230b1e00ff8f05ab03ab8b8ad80660119917e75 Mon Sep 17 00:00:00 2001 From: Beatriz Vieira Date: Sun, 6 Feb 2022 18:15:36 -0300 Subject: [PATCH 2/2] refactor: move templates from sv to cmd/git-sv issue: #40 --- cmd/git-sv/main.go | 19 +++-- .../resources/templates/changelog-md.tpl | 0 .../resources/templates/releasenotes-md.tpl | 0 .../rn-md-section-breaking-changes.tpl | 0 .../templates/rn-md-section-item.tpl | 0 .../resources/templates/rn-md-section.tpl | 0 cmd/git-sv/resources_test.go | 24 +++++++ sv/formatter.go | 11 +-- sv/formatter_test.go | 72 ++++++++----------- 9 files changed, 70 insertions(+), 56 deletions(-) rename {sv => cmd/git-sv}/resources/templates/changelog-md.tpl (100%) rename {sv => cmd/git-sv}/resources/templates/releasenotes-md.tpl (100%) rename {sv => cmd/git-sv}/resources/templates/rn-md-section-breaking-changes.tpl (100%) rename {sv => cmd/git-sv}/resources/templates/rn-md-section-item.tpl (100%) rename {sv => cmd/git-sv}/resources/templates/rn-md-section.tpl (100%) create mode 100644 cmd/git-sv/resources_test.go diff --git a/cmd/git-sv/main.go b/cmd/git-sv/main.go index fa2053f..806d4b2 100644 --- a/cmd/git-sv/main.go +++ b/cmd/git-sv/main.go @@ -1,6 +1,8 @@ package main import ( + "embed" + "io/fs" "log" "os" "path/filepath" @@ -17,6 +19,16 @@ const ( repoConfigFilename = ".sv4git.yml" ) +var ( + //go:embed resources/templates/*.tpl + defaultTemplatesFS embed.FS +) + +func templateFS() fs.FS { + defaultTemplatesFS, _ := fs.Sub(defaultTemplatesFS, "resources/templates") + return defaultTemplatesFS +} + func main() { log.SetFlags(0) @@ -25,7 +37,7 @@ func main() { git := sv.NewGit(messageProcessor, cfg.Tag) semverProcessor := sv.NewSemVerCommitsProcessor(cfg.Versioning, cfg.CommitMessage) releasenotesProcessor := sv.NewReleaseNoteProcessor(cfg.ReleaseNotes) - outputFormatter := sv.NewOutputFormatter() + outputFormatter := sv.NewOutputFormatter(templateFS()) app := cli.NewApp() app.Name = "sv" @@ -146,10 +158,9 @@ func main() { } func loadCfg() Config { - envCfg := loadEnvConfig() - cfg := defaultConfig() + envCfg := loadEnvConfig() if envCfg.Home != "" { if homeCfg, err := readConfig(filepath.Join(envCfg.Home, configFilename)); err == nil { if merr := merge(&cfg, homeCfg); merr != nil { @@ -160,7 +171,7 @@ func loadCfg() Config { repoPath, rerr := getRepoPath() if rerr != nil { - log.Fatal("failed to get repository path, error: ", rerr) + log.Fatal("failed to discovery repository top level, error: ", rerr) } if repoCfg, err := readConfig(filepath.Join(repoPath, repoConfigFilename)); err == nil { diff --git a/sv/resources/templates/changelog-md.tpl b/cmd/git-sv/resources/templates/changelog-md.tpl similarity index 100% rename from sv/resources/templates/changelog-md.tpl rename to cmd/git-sv/resources/templates/changelog-md.tpl diff --git a/sv/resources/templates/releasenotes-md.tpl b/cmd/git-sv/resources/templates/releasenotes-md.tpl similarity index 100% rename from sv/resources/templates/releasenotes-md.tpl rename to cmd/git-sv/resources/templates/releasenotes-md.tpl diff --git a/sv/resources/templates/rn-md-section-breaking-changes.tpl b/cmd/git-sv/resources/templates/rn-md-section-breaking-changes.tpl similarity index 100% rename from sv/resources/templates/rn-md-section-breaking-changes.tpl rename to cmd/git-sv/resources/templates/rn-md-section-breaking-changes.tpl diff --git a/sv/resources/templates/rn-md-section-item.tpl b/cmd/git-sv/resources/templates/rn-md-section-item.tpl similarity index 100% rename from sv/resources/templates/rn-md-section-item.tpl rename to cmd/git-sv/resources/templates/rn-md-section-item.tpl diff --git a/sv/resources/templates/rn-md-section.tpl b/cmd/git-sv/resources/templates/rn-md-section.tpl similarity index 100% rename from sv/resources/templates/rn-md-section.tpl rename to cmd/git-sv/resources/templates/rn-md-section.tpl diff --git a/cmd/git-sv/resources_test.go b/cmd/git-sv/resources_test.go new file mode 100644 index 0000000..4d22540 --- /dev/null +++ b/cmd/git-sv/resources_test.go @@ -0,0 +1,24 @@ +package main + +import ( + "testing" +) + +func Test_checkTemplatesFiles(t *testing.T) { + tests := []string{ + "resources/templates/changelog-md.tpl", + "resources/templates/releasenotes-md.tpl", + } + for _, tt := range tests { + t.Run(tt, func(t *testing.T) { + got, err := defaultTemplatesFS.ReadFile(tt) + if err != nil { + t.Errorf("missing template error = %v", err) + return + } + if len(got) <= 0 { + t.Errorf("empty template") + } + }) + } +} diff --git a/sv/formatter.go b/sv/formatter.go index 896de9f..f47d345 100644 --- a/sv/formatter.go +++ b/sv/formatter.go @@ -2,7 +2,7 @@ package sv import ( "bytes" - "embed" + "io/fs" "text/template" ) @@ -14,11 +14,6 @@ type releaseNoteTemplateVariables struct { BreakingChanges BreakingChangeSection } -var ( - //go:embed resources/templates/* - defaultTemplatesFS embed.FS -) - // OutputFormatter output formatter interface. type OutputFormatter interface { FormatReleaseNote(releasenote ReleaseNote) (string, error) @@ -31,8 +26,8 @@ type OutputFormatterImpl struct { } // NewOutputFormatter TemplateProcessor constructor. -func NewOutputFormatter() *OutputFormatterImpl { - tpls := template.Must(template.New("templates").ParseFS(defaultTemplatesFS, "resources/templates/*")) +func NewOutputFormatter(templatesFS fs.FS) *OutputFormatterImpl { + tpls := template.Must(template.New("templates").ParseFS(templatesFS, "*")) return &OutputFormatterImpl{templates: tpls} } diff --git a/sv/formatter_test.go b/sv/formatter_test.go index eabf4f5..e186548 100644 --- a/sv/formatter_test.go +++ b/sv/formatter_test.go @@ -2,6 +2,7 @@ package sv import ( "bytes" + "os" "testing" "text/template" "time" @@ -9,6 +10,8 @@ import ( "github.com/Masterminds/semver/v3" ) +var templatesFS = os.DirFS("../cmd/git-sv/resources/templates") + var dateChangelog = `## v1.0.0 (2020-05-01) ` @@ -57,7 +60,7 @@ func TestOutputFormatterImpl_FormatReleaseNote(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewOutputFormatter().FormatReleaseNote(tt.input) + got, err := NewOutputFormatter(templatesFS).FormatReleaseNote(tt.input) if got != tt.want { t.Errorf("OutputFormatterImpl.FormatReleaseNote() = %v, want %v", got, tt.want) } @@ -88,6 +91,30 @@ func fullReleaseNote(tag string, date time.Time) ReleaseNote { return releaseNote(v, tag, date, sections, []string{"break change message"}) } +func Test_checkTemplatesExecution(t *testing.T) { + tpls := template.Must(template.New("templates").ParseFS(templatesFS, "*")) + tests := []struct { + template string + variables interface{} + }{ + {"changelog-md.tpl", changelogVariables("v1.0.0", "v1.0.1")}, + {"releasenotes-md.tpl", releaseNotesVariables("v1.0.0")}, + } + for _, tt := range tests { + t.Run(tt.template, func(t *testing.T) { + var b bytes.Buffer + err := tpls.ExecuteTemplate(&b, tt.template, tt.variables) + if err != nil { + t.Errorf("invalid template err = %v", err) + return + } + if len(b.Bytes()) <= 0 { + t.Errorf("empty template") + } + }) + } +} + func releaseNotesVariables(release string) releaseNoteTemplateVariables { return releaseNoteTemplateVariables{ Release: release, @@ -110,46 +137,3 @@ func changelogVariables(releases ...string) []releaseNoteTemplateVariables { return variables } - -func Test_checkTemplatesFiles(t *testing.T) { - tests := []string{ - "resources/templates/changelog-md.tpl", - "resources/templates/releasenotes-md.tpl", - } - for _, tt := range tests { - t.Run(tt, func(t *testing.T) { - got, err := defaultTemplatesFS.ReadFile(tt) - if err != nil { - t.Errorf("missing template error = %v", err) - return - } - if len(got) <= 0 { - t.Errorf("empty template") - } - }) - } -} - -func Test_checkTemplatesExecution(t *testing.T) { - tpls := template.Must(template.New("templates").ParseFS(defaultTemplatesFS, "resources/templates/*")) - tests := []struct { - template string - variables interface{} - }{ - {"changelog-md.tpl", changelogVariables("v1.0.0", "v1.0.1")}, - {"releasenotes-md.tpl", releaseNotesVariables("v1.0.0")}, - } - for _, tt := range tests { - t.Run(tt.template, func(t *testing.T) { - var b bytes.Buffer - err := tpls.ExecuteTemplate(&b, tt.template, tt.variables) - if err != nil { - t.Errorf("invalid template err = %v", err) - return - } - if len(b.Bytes()) <= 0 { - t.Errorf("empty template") - } - }) - } -}