2017-10-16 00:21:35 +02:00
|
|
|
// Package changelog provides the release changelog to goreleaser.
|
|
|
|
package changelog
|
|
|
|
|
|
|
|
import (
|
2017-10-20 00:44:12 +02:00
|
|
|
"errors"
|
2017-10-16 00:21:35 +02:00
|
|
|
"fmt"
|
2018-05-02 05:54:16 +02:00
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
2017-10-18 03:45:19 +02:00
|
|
|
"regexp"
|
2017-10-20 00:44:12 +02:00
|
|
|
"sort"
|
2017-10-16 00:40:53 +02:00
|
|
|
"strings"
|
2017-10-16 00:21:35 +02:00
|
|
|
|
2018-05-02 05:54:16 +02:00
|
|
|
"github.com/apex/log"
|
2017-10-16 00:21:35 +02:00
|
|
|
"github.com/goreleaser/goreleaser/context"
|
|
|
|
"github.com/goreleaser/goreleaser/internal/git"
|
|
|
|
"github.com/goreleaser/goreleaser/pipeline"
|
|
|
|
)
|
|
|
|
|
2017-10-20 00:44:12 +02:00
|
|
|
// ErrInvalidSortDirection happens when the sort order is invalid
|
|
|
|
var ErrInvalidSortDirection = errors.New("invalid sort direction")
|
|
|
|
|
2017-10-16 00:21:35 +02:00
|
|
|
// Pipe for checksums
|
|
|
|
type Pipe struct{}
|
|
|
|
|
2017-12-02 23:53:19 +02:00
|
|
|
func (Pipe) String() string {
|
|
|
|
return "generating changelog"
|
2017-10-16 00:21:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the pipe
|
2017-10-16 00:33:39 +02:00
|
|
|
func (Pipe) Run(ctx *context.Context) error {
|
2017-10-16 00:21:35 +02:00
|
|
|
if ctx.ReleaseNotes != "" {
|
|
|
|
return pipeline.Skip("release notes already provided via --release-notes")
|
|
|
|
}
|
2017-10-16 00:33:39 +02:00
|
|
|
if ctx.Snapshot {
|
|
|
|
return pipeline.Skip("not available for snapshots")
|
2017-10-16 00:21:35 +02:00
|
|
|
}
|
2017-10-20 00:44:12 +02:00
|
|
|
if err := checkSortDirection(ctx.Config.Changelog.Sort); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entries, err := buildChangelog(ctx)
|
2017-10-16 00:21:35 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-05-06 14:31:55 +02:00
|
|
|
ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v\n", strings.Join(entries, "\n"))
|
2018-05-02 05:54:16 +02:00
|
|
|
var path = filepath.Join(ctx.Config.Dist, "CHANGELOG.md")
|
|
|
|
log.WithField("changelog", path).Info("writing")
|
|
|
|
return ioutil.WriteFile(path, []byte(ctx.ReleaseNotes), 0644)
|
2017-10-20 00:44:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func checkSortDirection(mode string) error {
|
|
|
|
switch mode {
|
|
|
|
case "":
|
|
|
|
fallthrough
|
|
|
|
case "asc":
|
|
|
|
fallthrough
|
|
|
|
case "desc":
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ErrInvalidSortDirection
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildChangelog(ctx *context.Context) ([]string, error) {
|
|
|
|
log, err := getChangelog(ctx.Git.CurrentTag)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-10-16 00:40:53 +02:00
|
|
|
var entries = strings.Split(log, "\n")
|
2017-10-20 00:44:12 +02:00
|
|
|
entries = entries[0 : len(entries)-1]
|
|
|
|
entries, err = filterEntries(ctx, entries)
|
|
|
|
if err != nil {
|
|
|
|
return entries, err
|
|
|
|
}
|
|
|
|
return sortEntries(ctx, entries), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func filterEntries(ctx *context.Context, entries []string) ([]string, error) {
|
2017-10-16 00:40:53 +02:00
|
|
|
for _, filter := range ctx.Config.Changelog.Filters.Exclude {
|
2017-10-18 03:45:19 +02:00
|
|
|
r, err := regexp.Compile(filter)
|
|
|
|
if err != nil {
|
2017-10-20 00:44:12 +02:00
|
|
|
return entries, err
|
2017-10-18 03:45:19 +02:00
|
|
|
}
|
|
|
|
entries = remove(r, entries)
|
2017-10-16 00:40:53 +02:00
|
|
|
}
|
2017-10-20 00:44:12 +02:00
|
|
|
return entries, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func sortEntries(ctx *context.Context, entries []string) []string {
|
|
|
|
var direction = ctx.Config.Changelog.Sort
|
|
|
|
if direction == "" {
|
|
|
|
return entries
|
|
|
|
}
|
|
|
|
var result = make([]string, len(entries))
|
|
|
|
copy(result, entries)
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
|
|
_, imsg := extractCommitInfo(result[i])
|
|
|
|
_, jmsg := extractCommitInfo(result[j])
|
|
|
|
if direction == "asc" {
|
|
|
|
return strings.Compare(imsg, jmsg) < 0
|
|
|
|
}
|
|
|
|
return strings.Compare(imsg, jmsg) > 0
|
|
|
|
})
|
|
|
|
return result
|
2017-10-16 00:21:35 +02:00
|
|
|
}
|
|
|
|
|
2017-10-18 03:45:19 +02:00
|
|
|
func remove(filter *regexp.Regexp, entries []string) (result []string) {
|
2017-10-16 00:40:53 +02:00
|
|
|
for _, entry := range entries {
|
2017-10-20 00:44:12 +02:00
|
|
|
_, msg := extractCommitInfo(entry)
|
|
|
|
if !filter.MatchString(msg) {
|
2017-10-16 00:40:53 +02:00
|
|
|
result = append(result, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-10-20 00:44:12 +02:00
|
|
|
func extractCommitInfo(line string) (hash, msg string) {
|
|
|
|
ss := strings.Split(line, " ")
|
|
|
|
return ss[0], strings.Join(ss[1:], " ")
|
2017-10-18 03:45:19 +02:00
|
|
|
}
|
|
|
|
|
2017-10-16 00:21:35 +02:00
|
|
|
func getChangelog(tag string) (string, error) {
|
|
|
|
prev, err := previous(tag)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !prev.Tag {
|
|
|
|
return gitLog(prev.SHA, tag)
|
|
|
|
}
|
|
|
|
return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag))
|
|
|
|
}
|
|
|
|
|
|
|
|
func gitLog(refs ...string) (string, error) {
|
2018-02-20 19:23:45 +02:00
|
|
|
var args = []string{"log", "--pretty=oneline", "--abbrev-commit", "--no-decorate", "--no-color"}
|
2017-10-16 00:21:35 +02:00
|
|
|
args = append(args, refs...)
|
|
|
|
return git.Run(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func previous(tag string) (result ref, err error) {
|
|
|
|
result.Tag = true
|
|
|
|
result.SHA, err = git.Clean(git.Run("describe", "--tags", "--abbrev=0", tag+"^"))
|
|
|
|
if err != nil {
|
|
|
|
result.Tag = false
|
|
|
|
result.SHA, err = git.Clean(git.Run("rev-list", "--max-parents=0", "HEAD"))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type ref struct {
|
|
|
|
Tag bool
|
|
|
|
SHA string
|
|
|
|
}
|