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:
parent
2c93bd7c7f
commit
39bf6668bc
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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: ¤t,
|
||||
}
|
||||
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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
4
internal/client/testdata/github/compare.json
vendored
4
internal/client/testdata/github/compare.json
vendored
@ -6,7 +6,9 @@
|
||||
"message": "Fix all the bugs\nlalalal"
|
||||
},
|
||||
"author": {
|
||||
"login": "octocat"
|
||||
"login": "octocat",
|
||||
"name": "Octocat",
|
||||
"email": "octo@cat"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -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 {
|
||||
|
@ -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{
|
||||
|
@ -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"`
|
||||
|
||||
|
@ -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{},
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user