1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2024-12-29 01:44:39 +02:00

feat(changelog): custom commit format (#4802)

This allows to use templates for commit messages in the changelog when
using `github`, `gitea`, or `gitlab` as the changelog implementation.

closes #4800

---------

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Carlos Alexandro Becker 2024-04-24 09:08:20 -03:00 committed by GitHub
parent 2c93bd7c7f
commit 39bf6668bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 127 additions and 42 deletions

View File

@ -56,11 +56,20 @@ type Client interface {
CreateRelease(ctx *context.Context, body string) (releaseID string, err error)
PublishRelease(ctx *context.Context, releaseID string) (err error)
Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error)
Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error)
Changelog(ctx *context.Context, repo Repo, prev, current string) ([]ChangelogItem, error)
ReleaseURLTemplater
FileCreator
}
// ChangelogItem represents a changelog item, basically, a commit and its author.
type ChangelogItem struct {
SHA string
Message string
AuthorName string
AuthorEmail string
AuthorUsername string
}
// ReleaseURLTemplater provides the release URL as a template, containing the
// artifact name as well.
type ReleaseURLTemplater interface {

View File

@ -75,22 +75,23 @@ func newGitea(ctx *context.Context, token string) (*giteaClient, error) {
}
// Changelog fetches the changelog between two revisions.
func (c *giteaClient) Changelog(_ *context.Context, repo Repo, prev, current string) (string, error) {
func (c *giteaClient) Changelog(_ *context.Context, repo Repo, prev, current string) ([]ChangelogItem, error) {
result, _, err := c.client.CompareCommits(repo.Owner, repo.Name, prev, current)
if err != nil {
return "", err
return nil, err
}
var log []string
var log []ChangelogItem
for _, commit := range result.Commits {
log = append(log, fmt.Sprintf(
"%s: %s (@%s)",
commit.SHA[:7],
strings.Split(commit.RepoCommit.Message, "\n")[0],
commit.Author.UserName,
))
log = append(log, ChangelogItem{
SHA: commit.SHA[:7],
Message: strings.Split(commit.RepoCommit.Message, "\n")[0],
AuthorName: commit.Author.FullName,
AuthorEmail: commit.Author.Email,
AuthorUsername: commit.Author.UserName,
})
}
return strings.Join(log, "\n"), nil
return log, nil
}
// CloseMilestone closes a given milestone.

View File

@ -617,6 +617,8 @@ func TestGiteaChangelog(t *testing.T) {
},
Author: &gitea.User{
UserName: "johndoe",
FullName: "John Doe",
Email: "nope@nope.nope",
},
RepoCommit: &gitea.RepoCommit{
Message: "feat: impl something\n\nnsome other lines",
@ -646,7 +648,15 @@ func TestGiteaChangelog(t *testing.T) {
result, err := client.Changelog(ctx, repo, "v1.0.0", "v1.1.0")
require.NoError(t, err)
require.Equal(t, "c8488dc: feat: impl something (@johndoe)", result)
require.Equal(t, []ChangelogItem{
{
SHA: "c8488dc",
Message: "feat: impl something",
AuthorUsername: "johndoe",
AuthorName: "John Doe",
AuthorEmail: "nope@nope.nope",
},
}, result)
}
func TestGiteatGetInstanceURL(t *testing.T) {

View File

@ -101,23 +101,24 @@ func (c *githubClient) GenerateReleaseNotes(ctx *context.Context, repo Repo, pre
return notes.Body, err
}
func (c *githubClient) Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error) {
func (c *githubClient) Changelog(ctx *context.Context, repo Repo, prev, current string) ([]ChangelogItem, error) {
c.checkRateLimit(ctx)
var log []string
var log []ChangelogItem
opts := &github.ListOptions{PerPage: 100}
for {
result, resp, err := c.client.Repositories.CompareCommits(ctx, repo.Owner, repo.Name, prev, current, opts)
if err != nil {
return "", err
return nil, err
}
for _, commit := range result.Commits {
log = append(log, fmt.Sprintf(
"%s: %s (@%s)",
commit.GetSHA(),
strings.Split(commit.Commit.GetMessage(), "\n")[0],
commit.GetAuthor().GetLogin(),
))
log = append(log, ChangelogItem{
SHA: commit.GetSHA(),
Message: strings.Split(commit.Commit.GetMessage(), "\n")[0],
AuthorName: commit.GetAuthor().GetName(),
AuthorEmail: commit.GetAuthor().GetEmail(),
AuthorUsername: commit.GetAuthor().GetLogin(),
})
}
if resp.NextPage == 0 {
break
@ -125,7 +126,7 @@ func (c *githubClient) Changelog(ctx *context.Context, repo Repo, prev, current
opts.Page = resp.NextPage
}
return strings.Join(log, "\n"), nil
return log, nil
}
// getDefaultBranch returns the default branch of a github repo

View File

@ -300,7 +300,15 @@ func TestGitHubChangelog(t *testing.T) {
log, err := client.Changelog(ctx, repo, "v1.0.0", "v1.1.0")
require.NoError(t, err)
require.Equal(t, "6dcb09b5b57875f334f61aebed695e2e4193db5e: Fix all the bugs (@octocat)", log)
require.Equal(t, []ChangelogItem{
{
SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e",
Message: "Fix all the bugs",
AuthorName: "Octocat",
AuthorEmail: "octo@cat",
AuthorUsername: "octocat",
},
}, log)
}
func TestGitHubReleaseNotes(t *testing.T) {

View File

@ -64,27 +64,26 @@ func newGitLab(ctx *context.Context, token string) (*gitlabClient, error) {
return &gitlabClient{client: client}, nil
}
func (c *gitlabClient) Changelog(_ *context.Context, repo Repo, prev, current string) (string, error) {
func (c *gitlabClient) Changelog(_ *context.Context, repo Repo, prev, current string) ([]ChangelogItem, error) {
cmpOpts := &gitlab.CompareOptions{
From: &prev,
To: &current,
}
result, _, err := c.client.Repositories.Compare(repo.String(), cmpOpts)
var log []string
var log []ChangelogItem
if err != nil {
return "", err
return nil, err
}
for _, commit := range result.Commits {
log = append(log, fmt.Sprintf(
"%s: %s (%s <%s>)",
commit.ShortID,
strings.Split(commit.Message, "\n")[0],
commit.AuthorName,
commit.AuthorEmail,
))
log = append(log, ChangelogItem{
SHA: commit.ShortID,
Message: strings.Split(commit.Message, "\n")[0],
AuthorName: commit.AuthorName,
AuthorEmail: commit.AuthorEmail,
})
}
return strings.Join(log, "\n"), nil
return log, nil
}
// getDefaultBranch get the default branch

View File

@ -484,7 +484,15 @@ func TestGitLabChangelog(t *testing.T) {
log, err := client.Changelog(ctx, repo, "v1.0.0", "v1.1.0")
require.NoError(t, err)
require.Equal(t, "6dcb09b5: Fix all the bugs (Joey User <joey@user.edu>)", log)
require.Equal(t, []ChangelogItem{
{
SHA: "6dcb09b5",
Message: "Fix all the bugs",
AuthorName: "Joey User",
AuthorEmail: "joey@user.edu",
AuthorUsername: "",
},
}, log)
}
func TestGitLabCreateFile(t *testing.T) {

View File

@ -39,7 +39,7 @@ type Mock struct {
Lock sync.Mutex
ClosedMilestone string
FailToCloseMilestone bool
Changes string
Changes []ChangelogItem
ReleaseNotes string
ReleaseNotesParams []string
OpenedPullRequest bool
@ -56,11 +56,11 @@ func (c *Mock) OpenPullRequest(_ *context.Context, _, _ Repo, _ string, _ bool)
return nil
}
func (c *Mock) Changelog(_ *context.Context, _ Repo, _, _ string) (string, error) {
if c.Changes != "" {
func (c *Mock) Changelog(_ *context.Context, _ Repo, _, _ string) ([]ChangelogItem, error) {
if len(c.Changes) > 0 {
return c.Changes, nil
}
return "", ErrNotImplemented
return nil, ErrNotImplemented
}
func (c *Mock) GenerateReleaseNotes(_ *context.Context, _ Repo, prev, current string) (string, error) {

View File

@ -6,7 +6,9 @@
"message": "Fix all the bugs\nlalalal"
},
"author": {
"login": "octocat"
"login": "octocat",
"name": "Octocat",
"email": "octo@cat"
}
}
]

View File

@ -41,6 +41,7 @@ const (
type Pipe struct{}
func (Pipe) String() string { return "generating changelog" }
func (Pipe) Skip(ctx *context.Context) (bool, error) {
if ctx.Snapshot {
return true, nil
@ -53,6 +54,13 @@ func (Pipe) Skip(ctx *context.Context) (bool, error) {
return tmpl.New(ctx).Bool(ctx.Config.Changelog.Disable)
}
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Changelog.Format == "" {
ctx.Config.Changelog.Format = "{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})"
}
return nil
}
// Run the pipe.
func (Pipe) Run(ctx *context.Context) error {
notes, err := loadContent(ctx, ctx.ReleaseNotesFile, ctx.ReleaseNotesTmpl)
@ -445,7 +453,25 @@ type scmChangeloger struct {
func (c *scmChangeloger) Log(ctx *context.Context) (string, error) {
prev, current := comparePair(ctx)
return c.client.Changelog(ctx, c.repo, prev, current)
items, err := c.client.Changelog(ctx, c.repo, prev, current)
if err != nil {
return "", err
}
var lines []string
for _, item := range items {
line, err := tmpl.New(ctx).WithExtraFields(tmpl.Fields{
"SHA": item.SHA,
"Message": item.Message,
"AuthorUsername": item.AuthorUsername,
"AuthorName": item.AuthorName,
"AuthorEmail": item.AuthorEmail,
}).Apply(ctx.Config.Changelog.Format)
if err != nil {
return "", err
}
lines = append(lines, line)
}
return strings.Join(lines, "\n"), nil
}
type githubNativeChangeloger struct {

View File

@ -515,10 +515,19 @@ func TestGetChangelogGitHub(t *testing.T) {
Use: useGitHub,
},
}, testctx.WithCurrentTag("v0.180.2"), testctx.WithPreviousTag("v0.180.1"))
require.NoError(t, Pipe{}.Default(ctx))
expected := "c90f1085f255d0af0b055160bfff5ee40f47af79: fix: do not skip any defaults (#2521) (@caarlos0)"
mock := client.NewMock()
mock.Changes = expected
mock.Changes = []client.ChangelogItem{
{
SHA: "c90f1085f255d0af0b055160bfff5ee40f47af79",
Message: "fix: do not skip any defaults (#2521)",
AuthorName: "Carlos",
AuthorEmail: "nope@nope.com",
AuthorUsername: "caarlos0",
},
}
l := scmChangeloger{
client: mock,
repo: client.Repo{

View File

@ -1111,6 +1111,7 @@ type Changelog struct {
Sort string `yaml:"sort,omitempty" json:"sort,omitempty" jsonschema:"enum=asc,enum=desc,enum=,default="`
Disable string `yaml:"disable,omitempty" json:"disable,omitempty" jsonschema:"oneof_type=string;boolean"`
Use string `yaml:"use,omitempty" json:"use,omitempty" jsonschema:"enum=git,enum=github,enum=github-native,enum=gitlab,default=git"`
Format string `yaml:"format,omitempty" json:"omitempty"`
Groups []ChangelogGroup `yaml:"groups,omitempty" json:"groups,omitempty"`
Abbrev int `yaml:"abbrev,omitempty" json:"abbrev,omitempty"`

View File

@ -12,6 +12,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/bluesky"
"github.com/goreleaser/goreleaser/internal/pipe/brew"
"github.com/goreleaser/goreleaser/internal/pipe/build"
"github.com/goreleaser/goreleaser/internal/pipe/changelog"
"github.com/goreleaser/goreleaser/internal/pipe/checksums"
"github.com/goreleaser/goreleaser/internal/pipe/chocolatey"
"github.com/goreleaser/goreleaser/internal/pipe/discord"
@ -64,6 +65,7 @@ var Defaulters = []Defaulter{
snapshot.Pipe{},
release.Pipe{},
project.Pipe{},
changelog.Pipe{},
gomod.Pipe{},
build.Pipe{},
universalbinary.Pipe{},

View File

@ -27,6 +27,15 @@ changelog:
# Default: 'git'
use: github
# Format to use for commit formatting.
# Only available when use is one of `github`, `gitea`, or `gitlab`.
#
# Default: '{{ .SHA }}: {{ .Message }} ({{ with .AuthorUsername }}@{{ . }}{{ else }}{{ .AuthorName }} <{{ .AuthorEmail }}>{{ end }})'
# Extra template fields: `SHA`, `Message`, `AuthorName`, `AuthorEmail`, and
# `AuthorUsername`.
# Since: v1.26
format: "{{.SHA}}: {{.Message}} (@{{.AuthorUsername}})"
# Sorts the changelog by the commit's messages.
# Could either be asc, desc or empty
# Empty means 'no sorting', it'll use the output of `git log` as is.