< Summary - go-semantic-release Coverage

Information
Line coverage
93%
Covered lines: 40
Uncovered lines: 3
Coverable lines: 43
Total lines: 160
Line coverage: 93%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Generate0%0081.25%
buildTemplateData0%00100%

File(s)

/home/runner/work/go-semantic-release/go-semantic-release/internal/adapters/changelog/generator.go

#LineLine coverage
 1package changelog
 2
 3import (
 4  "bytes"
 5  "fmt"
 6  "strings"
 7  "text/template"
 8  "time"
 9
 10  "github.com/jedi-knights/go-semantic-release/internal/domain"
 11  "github.com/jedi-knights/go-semantic-release/internal/ports"
 12)
 13
 14// Compile-time interface compliance check.
 15var _ ports.ChangelogGenerator = (*TemplateGenerator)(nil)
 16
 17const defaultTemplate = `## {{if .Project}}[{{.Project}}] {{end}}{{.Version}} ({{.Date}})
 18{{range .Sections}}
 19### {{.Title}}
 20
 21{{range .Commits}}- {{if .Scope}}**{{.Scope}}:** {{end}}{{.Description}} ({{.ShortHash}})
 22{{end}}{{end}}`
 23
 24// TemplateGenerator implements ports.ChangelogGenerator using Go templates.
 25type TemplateGenerator struct {
 26  customTemplate string
 27}
 28
 29// NewTemplateGenerator creates a changelog generator with an optional custom template.
 30func NewTemplateGenerator(customTemplate string) *TemplateGenerator {
 31  return &TemplateGenerator{customTemplate: customTemplate}
 32}
 33
 34type templateData struct {
 35  Version  string
 36  Project  string
 37  Date     string
 38  Sections []sectionData
 39}
 40
 41type sectionData struct {
 42  Title   string
 43  Commits []commitData
 44}
 45
 46type commitData struct {
 47  Hash        string
 48  ShortHash   string
 49  Type        string
 50  Scope       string
 51  Description string
 52  Author      string
 53  Breaking    bool
 54}
 55
 56func (g *TemplateGenerator) Generate(
 57  version domain.Version,
 58  project string,
 59  commits []domain.Commit,
 60  sections []domain.ChangelogSectionConfig,
 1061) (string, error) {
 1062  data := g.buildTemplateData(version, project, commits, sections)
 1063
 1064  tmplStr := defaultTemplate
 265  if g.customTemplate != "" {
 266    tmplStr = g.customTemplate
 267  }
 68
 1069  tmpl, err := template.New("changelog").Parse(tmplStr)
 170  if err != nil {
 171    return "", fmt.Errorf("parsing changelog template: %w", err)
 172  }
 73
 974  var buf bytes.Buffer
 075  if err := tmpl.Execute(&buf, data); err != nil {
 076    return "", fmt.Errorf("executing changelog template: %w", err)
 077  }
 978  return strings.TrimSpace(buf.String()), nil
 79}
 80
 81func (g *TemplateGenerator) buildTemplateData(
 82  version domain.Version,
 83  project string,
 84  commits []domain.Commit,
 85  sections []domain.ChangelogSectionConfig,
 1086) templateData {
 1087  // Group breaking changes separately.
 1088  breakingCommits := filterBreakingCommits(commits)
 1089  commitsByType := groupCommitsByType(commits)
 1090
 1091  secs := make([]sectionData, 0, len(sections))
 1092  for _, sec := range sections {
 6393    if sec.Hidden {
 6394      continue
 95    }
 96
 4597    var sectionCommits []domain.Commit
 998    if sec.Type == "breaking" {
 999      sectionCommits = breakingCommits
 9100    } else {
 36101      sectionCommits = commitsByType[sec.Type]
 36102    }
 103
 38104    if len(sectionCommits) == 0 {
 38105      continue
 106    }
 107
 7108    secs = append(secs, sectionData{
 7109      Title:   sec.Title,
 7110      Commits: toCommitData(sectionCommits),
 7111    })
 112  }
 113
 10114  return templateData{
 10115    Version:  version.String(),
 10116    Project:  project,
 10117    Date:     time.Now().Format("2006-01-02"),
 10118    Sections: secs,
 10119  }
 120}
 121
 122func filterBreakingCommits(commits []domain.Commit) []domain.Commit {
 123  var result []domain.Commit
 124  for i := range commits {
 125    if commits[i].IsBreakingChange {
 126      result = append(result, commits[i])
 127    }
 128  }
 129  return result
 130}
 131
 132func groupCommitsByType(commits []domain.Commit) map[string][]domain.Commit {
 133  groups := make(map[string][]domain.Commit)
 134  for i := range commits {
 135    if commits[i].Type != "" {
 136      groups[commits[i].Type] = append(groups[commits[i].Type], commits[i])
 137    }
 138  }
 139  return groups
 140}
 141
 142func toCommitData(commits []domain.Commit) []commitData {
 143  result := make([]commitData, 0, len(commits))
 144  for i := range commits {
 145    short := commits[i].Hash
 146    if len(short) > 7 {
 147      short = short[:7]
 148    }
 149    result = append(result, commitData{
 150      Hash:        commits[i].Hash,
 151      ShortHash:   short,
 152      Type:        commits[i].Type,
 153      Scope:       commits[i].Scope,
 154      Description: commits[i].Description,
 155      Author:      commits[i].Author,
 156      Breaking:    commits[i].IsBreakingChange,
 157    })
 158  }
 159  return result
 160}

Methods/Properties

Generate
buildTemplateData