feat(changelog): omit version heading in forge release notes

The forge ui usually shows the release name right above the description,
so this removes an unecessary duplicate bit of information.

In addition this also cleans up the changelog interface a bit and moves
functionality where it belongs. Prepares a bit for custom changelogs in
the future.

Closes #32
This commit is contained in:
Julian Tölle 2024-09-22 14:00:30 +02:00 committed by GitHub
parent 997b6492de
commit 2621c48d75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 71 additions and 34 deletions

View file

@ -26,27 +26,37 @@ func init() {
} }
} }
func NewChangelogEntry(logger *slog.Logger, commits []commitparser.AnalyzedCommit, version, link, prefix, suffix string) (string, error) { func DefaultTemplate() *template.Template {
features := make([]commitparser.AnalyzedCommit, 0) return changelogTemplate
fixes := make([]commitparser.AnalyzedCommit, 0) }
for _, commit := range commits { type Data struct {
switch commit.Type { Commits map[string][]commitparser.AnalyzedCommit
case "feat": Version string
features = append(features, commit) VersionLink string
case "fix": Prefix string
fixes = append(fixes, commit) Suffix string
} }
func New(commits map[string][]commitparser.AnalyzedCommit, version, versionLink, prefix, suffix string) Data {
return Data{
Commits: commits,
Version: version,
VersionLink: versionLink,
Prefix: prefix,
Suffix: suffix,
} }
}
type Formatting struct {
HideVersionTitle bool
}
func Entry(logger *slog.Logger, tpl *template.Template, data Data, formatting Formatting) (string, error) {
var changelog bytes.Buffer var changelog bytes.Buffer
err := changelogTemplate.Execute(&changelog, map[string]any{ err := tpl.Execute(&changelog, map[string]any{
"Features": features, "Data": data,
"Fixes": fixes, "Formatting": formatting,
"Version": version,
"VersionLink": link,
"Prefix": prefix,
"Suffix": suffix,
}) })
if err != nil { if err != nil {
return "", err return "", err

View file

@ -1,22 +1,24 @@
## [{{.Version}}]({{.VersionLink}}) {{define "entry" -}}
{{- if .Prefix }} - {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}}
{{ .Prefix }} {{ end }}
{{- if not .Formatting.HideVersionTitle }}
## [{{.Data.Version}}]({{.Data.VersionLink}})
{{ end -}} {{ end -}}
{{- if (gt (len .Features) 0) }} {{- if .Data.Prefix }}
{{ .Data.Prefix }}
{{ end -}}
{{- with .Data.Commits.feat }}
### Features ### Features
{{ range .Features -}} {{ range . -}}{{template "entry" .}}{{end}}
- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}}
{{ end -}}
{{- end -}} {{- end -}}
{{- if (gt (len .Fixes) 0) }} {{- with .Data.Commits.fix }}
### Bug Fixes ### Bug Fixes
{{ range .Fixes -}} {{ range . -}}{{template "entry" .}}{{end}}
- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}}
{{ end -}}
{{- end -}} {{- end -}}
{{- if .Suffix }} {{- if .Data.Suffix }}
{{ .Suffix }} {{ .Data.Suffix }}
{{ end }} {{ end }}

View file

@ -168,7 +168,8 @@ This version is compatible with flux-compensator v2.2 - v2.9.
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := NewChangelogEntry(slog.Default(), tt.args.analyzedCommits, tt.args.version, tt.args.link, tt.args.prefix, tt.args.suffix) data := New(commitparser.ByType(tt.args.analyzedCommits), tt.args.version, tt.args.link, tt.args.prefix, tt.args.suffix)
got, err := Entry(slog.Default(), DefaultTemplate(), data, Formatting{})
if !tt.wantErr(t, err) { if !tt.wantErr(t, err) {
return return
} }

View file

@ -15,3 +15,18 @@ type AnalyzedCommit struct {
Scope *string Scope *string
BreakingChange bool BreakingChange bool
} }
// ByType groups the Commits by the type field. Used by the Changelog.
func ByType(in []AnalyzedCommit) map[string][]AnalyzedCommit {
out := map[string][]AnalyzedCommit{}
for _, commit := range in {
if out[commit.Type] == nil {
out[commit.Type] = make([]AnalyzedCommit, 0, 1)
}
out[commit.Type] = append(out[commit.Type], commit)
}
return out
}

View file

@ -243,7 +243,9 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
return err return err
} }
changelogEntry, err := changelog.NewChangelogEntry(logger, analyzedCommits, nextVersion, rp.forge.ReleaseURL(nextVersion), releaseOverrides.Prefix, releaseOverrides.Suffix) changelogData := changelog.New(commitparser.ByType(analyzedCommits), nextVersion, rp.forge.ReleaseURL(nextVersion), releaseOverrides.Prefix, releaseOverrides.Suffix)
changelogEntry, err := changelog.Entry(logger, changelog.DefaultTemplate(), changelogData, changelog.Formatting{})
if err != nil { if err != nil {
return fmt.Errorf("failed to build changelog entry: %w", err) return fmt.Errorf("failed to build changelog entry: %w", err)
} }
@ -289,9 +291,16 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
logger.InfoContext(ctx, "file content is already up-to-date in remote branch, skipping push") logger.InfoContext(ctx, "file content is already up-to-date in remote branch, skipping push")
} }
// We do not need the version title here. In the pull request the version is available from the title, and in the
// release on the Forge its usually in a heading somewhere above the text.
changelogEntryPullRequest, err := changelog.Entry(logger, changelog.DefaultTemplate(), changelogData, changelog.Formatting{HideVersionTitle: true})
if err != nil {
return fmt.Errorf("failed to build pull request changelog entry: %w", err)
}
// Open/Update PR // Open/Update PR
if pr == nil { if pr == nil {
pr, err = releasepr.NewReleasePullRequest(rpBranch, rp.targetBranch, nextVersion, changelogEntry) pr, err = releasepr.NewReleasePullRequest(rpBranch, rp.targetBranch, nextVersion, changelogEntryPullRequest)
if err != nil { if err != nil {
return err return err
} }
@ -308,7 +317,7 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
err = pr.SetDescription(changelogEntry, overrides) err = pr.SetDescription(changelogEntryPullRequest, overrides)
if err != nil { if err != nil {
return err return err
} }