github.com/tomsquest/goreleaser@v0.34.3-0.20171008022654-7d6ef4d338b3/pipeline/git/git.go (about)

     1  // Package git implements the Pipe interface getting and validating the
     2  // current git repository state
     3  package git
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"regexp"
     9  	"strings"
    10  	"text/template"
    11  	"time"
    12  
    13  	"github.com/apex/log"
    14  	"github.com/goreleaser/goreleaser/context"
    15  	"github.com/goreleaser/goreleaser/internal/git"
    16  	"github.com/goreleaser/goreleaser/pipeline"
    17  )
    18  
    19  // Pipe for brew deployment
    20  type Pipe struct{}
    21  
    22  // Description of the pipe
    23  func (Pipe) Description() string {
    24  	return "Getting and validating git state"
    25  }
    26  
    27  // Run the pipe
    28  func (Pipe) Run(ctx *context.Context) (err error) {
    29  	tag, commit, err := getInfo()
    30  	if err != nil {
    31  		return
    32  	}
    33  	if tag == "" && !ctx.Snapshot {
    34  		return ErrNoTag
    35  	}
    36  	ctx.Git = context.GitInfo{
    37  		CurrentTag: tag,
    38  		Commit:     commit,
    39  	}
    40  	log.Infof("releasing %s, commit %s", tag, commit)
    41  	if err = setLog(ctx, tag, commit); err != nil {
    42  		return
    43  	}
    44  	if err = setVersion(ctx, tag, commit); err != nil {
    45  		return
    46  	}
    47  	if !ctx.Validate {
    48  		return pipeline.Skip("--skip-validate is set")
    49  	}
    50  	return validate(ctx, commit, tag)
    51  }
    52  
    53  func setVersion(ctx *context.Context, tag, commit string) (err error) {
    54  	if ctx.Snapshot {
    55  		snapshotName, err := getSnapshotName(ctx, tag, commit)
    56  		if err != nil {
    57  			return fmt.Errorf("failed to generate snapshot name: %s", err.Error())
    58  		}
    59  		ctx.Version = snapshotName
    60  		return nil
    61  	}
    62  	// removes usual `v` prefix
    63  	ctx.Version = strings.TrimPrefix(tag, "v")
    64  	return
    65  }
    66  
    67  func setLog(ctx *context.Context, tag, commit string) (err error) {
    68  	if ctx.ReleaseNotes != "" {
    69  		return
    70  	}
    71  	var log string
    72  	if tag == "" {
    73  		log, err = getChangelog(commit)
    74  	} else {
    75  		log, err = getChangelog(tag)
    76  	}
    77  	if err != nil {
    78  		return err
    79  	}
    80  	ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log)
    81  	return nil
    82  }
    83  
    84  type snapshotNameData struct {
    85  	Commit    string
    86  	Tag       string
    87  	Timestamp int64
    88  }
    89  
    90  func getSnapshotName(ctx *context.Context, tag, commit string) (string, error) {
    91  	tmpl, err := template.New("snapshot").Parse(ctx.Config.Snapshot.NameTemplate)
    92  	var out bytes.Buffer
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  	var data = snapshotNameData{
    97  		Commit:    commit,
    98  		Tag:       tag,
    99  		Timestamp: time.Now().Unix(),
   100  	}
   101  	err = tmpl.Execute(&out, data)
   102  	return out.String(), err
   103  }
   104  
   105  func validate(ctx *context.Context, commit, tag string) error {
   106  	out, err := git.Run("status", "--porcelain")
   107  	if strings.TrimSpace(out) != "" || err != nil {
   108  		return ErrDirty{out}
   109  	}
   110  	if ctx.Snapshot {
   111  		return nil
   112  	}
   113  	if !regexp.MustCompile("^[0-9.]+").MatchString(ctx.Version) {
   114  		return ErrInvalidVersionFormat{ctx.Version}
   115  	}
   116  	_, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag)
   117  	if err != nil {
   118  		return ErrWrongRef{commit, tag}
   119  	}
   120  	return nil
   121  }
   122  
   123  func getChangelog(tag string) (string, error) {
   124  	prev, err := previous(tag)
   125  	if err != nil {
   126  		return "", err
   127  	}
   128  	if !prev.Tag {
   129  		return gitLog(prev.SHA, tag)
   130  	}
   131  	return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag))
   132  }
   133  
   134  func gitLog(refs ...string) (string, error) {
   135  	var args = []string{"log", "--pretty=oneline", "--abbrev-commit"}
   136  	args = append(args, refs...)
   137  	return git.Run(args...)
   138  }
   139  
   140  func getInfo() (tag, commit string, err error) {
   141  	tag, err = cleanGit("describe", "--tags", "--abbrev=0")
   142  	if err != nil {
   143  		log.WithError(err).Info("failed to retrieve current tag")
   144  	}
   145  	commit, err = cleanGit("show", "--format='%H'", "HEAD")
   146  	return
   147  }
   148  
   149  func previous(tag string) (result ref, err error) {
   150  	result.Tag = true
   151  	result.SHA, err = cleanGit("describe", "--tags", "--abbrev=0", tag+"^")
   152  	if err != nil {
   153  		result.Tag = false
   154  		result.SHA, err = cleanGit("rev-list", "--max-parents=0", "HEAD")
   155  	}
   156  	return
   157  }
   158  
   159  type ref struct {
   160  	Tag bool
   161  	SHA string
   162  }