diff --git a/internal/changelog/changelog.go b/internal/changelog/changelog.go index 6004829..d6386b8 100644 --- a/internal/changelog/changelog.go +++ b/internal/changelog/changelog.go @@ -26,27 +26,37 @@ func init() { } } -func NewChangelogEntry(logger *slog.Logger, commits []commitparser.AnalyzedCommit, version, link, prefix, suffix string) (string, error) { - features := make([]commitparser.AnalyzedCommit, 0) - fixes := make([]commitparser.AnalyzedCommit, 0) - - for _, commit := range commits { - switch commit.Type { - case "feat": - features = append(features, commit) - case "fix": - fixes = append(fixes, commit) - } +func DefaultTemplate() *template.Template { + return changelogTemplate +} + +type Data struct { + Commits map[string][]commitparser.AnalyzedCommit + Version string + VersionLink string + Prefix string + 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 - err := changelogTemplate.Execute(&changelog, map[string]any{ - "Features": features, - "Fixes": fixes, - "Version": version, - "VersionLink": link, - "Prefix": prefix, - "Suffix": suffix, + err := tpl.Execute(&changelog, map[string]any{ + "Data": data, + "Formatting": formatting, }) if err != nil { return "", err diff --git a/internal/changelog/changelog.md.tpl b/internal/changelog/changelog.md.tpl index 1f7dd42..50907eb 100644 --- a/internal/changelog/changelog.md.tpl +++ b/internal/changelog/changelog.md.tpl @@ -1,22 +1,24 @@ -## [{{.Version}}]({{.VersionLink}}) -{{- if .Prefix }} -{{ .Prefix }} +{{define "entry" -}} +- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}} +{{ end }} + +{{- if not .Formatting.HideVersionTitle }} +## [{{.Data.Version}}]({{.Data.VersionLink}}) +{{ end -}} +{{- if .Data.Prefix }} +{{ .Data.Prefix }} {{ end -}} -{{- if (gt (len .Features) 0) }} +{{- with .Data.Commits.feat }} ### Features -{{ range .Features -}} -- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}} -{{ end -}} +{{ range . -}}{{template "entry" .}}{{end}} {{- end -}} -{{- if (gt (len .Fixes) 0) }} +{{- with .Data.Commits.fix }} ### Bug Fixes -{{ range .Fixes -}} -- {{ if .Scope }}**{{.Scope}}**: {{end}}{{.Description}} -{{ end -}} +{{ range . -}}{{template "entry" .}}{{end}} {{- end -}} -{{- if .Suffix }} -{{ .Suffix }} +{{- if .Data.Suffix }} +{{ .Data.Suffix }} {{ end }} diff --git a/internal/changelog/changelog_test.go b/internal/changelog/changelog_test.go index e6582c7..a969730 100644 --- a/internal/changelog/changelog_test.go +++ b/internal/changelog/changelog_test.go @@ -168,7 +168,8 @@ This version is compatible with flux-compensator v2.2 - v2.9. for _, tt := range tests { 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) { return } diff --git a/internal/commitparser/commitparser.go b/internal/commitparser/commitparser.go index 484d733..023c6b4 100644 --- a/internal/commitparser/commitparser.go +++ b/internal/commitparser/commitparser.go @@ -15,3 +15,18 @@ type AnalyzedCommit struct { Scope *string 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 +} diff --git a/releaserpleaser.go b/releaserpleaser.go index a69ef9e..2b1754f 100644 --- a/releaserpleaser.go +++ b/releaserpleaser.go @@ -243,7 +243,9 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error { 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 { 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") } + // 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 if pr == nil { - pr, err = releasepr.NewReleasePullRequest(rpBranch, rp.targetBranch, nextVersion, changelogEntry) + pr, err = releasepr.NewReleasePullRequest(rpBranch, rp.targetBranch, nextVersion, changelogEntryPullRequest) if err != nil { return err } @@ -308,7 +317,7 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error { if err != nil { return err } - err = pr.SetDescription(changelogEntry, overrides) + err = pr.SetDescription(changelogEntryPullRequest, overrides) if err != nil { return err }