github.com/droot/goreleaser@v0.66.2-0.20180420030140-c2db5fb17157/pipeline/changelog/changelog.go (about)

     1  // Package changelog provides the release changelog to goreleaser.
     2  package changelog
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/goreleaser/goreleaser/context"
    12  	"github.com/goreleaser/goreleaser/internal/git"
    13  	"github.com/goreleaser/goreleaser/pipeline"
    14  )
    15  
    16  // ErrInvalidSortDirection happens when the sort order is invalid
    17  var ErrInvalidSortDirection = errors.New("invalid sort direction")
    18  
    19  // Pipe for checksums
    20  type Pipe struct{}
    21  
    22  func (Pipe) String() string {
    23  	return "generating changelog"
    24  }
    25  
    26  // Run the pipe
    27  func (Pipe) Run(ctx *context.Context) error {
    28  	if ctx.ReleaseNotes != "" {
    29  		return pipeline.Skip("release notes already provided via --release-notes")
    30  	}
    31  	if ctx.Snapshot {
    32  		return pipeline.Skip("not available for snapshots")
    33  	}
    34  	if err := checkSortDirection(ctx.Config.Changelog.Sort); err != nil {
    35  		return err
    36  	}
    37  	entries, err := buildChangelog(ctx)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", strings.Join(entries, "\n"))
    42  	return nil
    43  }
    44  
    45  func checkSortDirection(mode string) error {
    46  	switch mode {
    47  	case "":
    48  		fallthrough
    49  	case "asc":
    50  		fallthrough
    51  	case "desc":
    52  		return nil
    53  	}
    54  	return ErrInvalidSortDirection
    55  }
    56  
    57  func buildChangelog(ctx *context.Context) ([]string, error) {
    58  	log, err := getChangelog(ctx.Git.CurrentTag)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	var entries = strings.Split(log, "\n")
    63  	entries = entries[0 : len(entries)-1]
    64  	entries, err = filterEntries(ctx, entries)
    65  	if err != nil {
    66  		return entries, err
    67  	}
    68  	return sortEntries(ctx, entries), nil
    69  }
    70  
    71  func filterEntries(ctx *context.Context, entries []string) ([]string, error) {
    72  	for _, filter := range ctx.Config.Changelog.Filters.Exclude {
    73  		r, err := regexp.Compile(filter)
    74  		if err != nil {
    75  			return entries, err
    76  		}
    77  		entries = remove(r, entries)
    78  	}
    79  	return entries, nil
    80  }
    81  
    82  func sortEntries(ctx *context.Context, entries []string) []string {
    83  	var direction = ctx.Config.Changelog.Sort
    84  	if direction == "" {
    85  		return entries
    86  	}
    87  	var result = make([]string, len(entries))
    88  	copy(result, entries)
    89  	sort.Slice(result, func(i, j int) bool {
    90  		_, imsg := extractCommitInfo(result[i])
    91  		_, jmsg := extractCommitInfo(result[j])
    92  		if direction == "asc" {
    93  			return strings.Compare(imsg, jmsg) < 0
    94  		}
    95  		return strings.Compare(imsg, jmsg) > 0
    96  	})
    97  	return result
    98  }
    99  
   100  func remove(filter *regexp.Regexp, entries []string) (result []string) {
   101  	for _, entry := range entries {
   102  		_, msg := extractCommitInfo(entry)
   103  		if !filter.MatchString(msg) {
   104  			result = append(result, entry)
   105  		}
   106  	}
   107  	return result
   108  }
   109  
   110  func extractCommitInfo(line string) (hash, msg string) {
   111  	ss := strings.Split(line, " ")
   112  	return ss[0], strings.Join(ss[1:], " ")
   113  }
   114  
   115  func getChangelog(tag string) (string, error) {
   116  	prev, err := previous(tag)
   117  	if err != nil {
   118  		return "", err
   119  	}
   120  	if !prev.Tag {
   121  		return gitLog(prev.SHA, tag)
   122  	}
   123  	return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag))
   124  }
   125  
   126  func gitLog(refs ...string) (string, error) {
   127  	var args = []string{"log", "--pretty=oneline", "--abbrev-commit", "--no-decorate", "--no-color"}
   128  	args = append(args, refs...)
   129  	return git.Run(args...)
   130  }
   131  
   132  func previous(tag string) (result ref, err error) {
   133  	result.Tag = true
   134  	result.SHA, err = git.Clean(git.Run("describe", "--tags", "--abbrev=0", tag+"^"))
   135  	if err != nil {
   136  		result.Tag = false
   137  		result.SHA, err = git.Clean(git.Run("rev-list", "--max-parents=0", "HEAD"))
   138  	}
   139  	return
   140  }
   141  
   142  type ref struct {
   143  	Tag bool
   144  	SHA string
   145  }