github.com/amane3/goreleaser@v0.182.0/internal/pipe/git/git.go (about)

     1  package git
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/apex/log"
    12  
    13  	"github.com/amane3/goreleaser/internal/git"
    14  	"github.com/amane3/goreleaser/internal/pipe"
    15  	"github.com/amane3/goreleaser/pkg/context"
    16  )
    17  
    18  // Pipe that sets up git state.
    19  type Pipe struct{}
    20  
    21  func (Pipe) String() string {
    22  	return "getting and validating git state"
    23  }
    24  
    25  // Run the pipe.
    26  func (Pipe) Run(ctx *context.Context) error {
    27  	if _, err := exec.LookPath("git"); err != nil {
    28  		return ErrNoGit
    29  	}
    30  	info, err := getInfo(ctx)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	ctx.Git = info
    35  	log.Infof("releasing %s, commit %s", info.CurrentTag, info.Commit)
    36  	ctx.Version = strings.TrimPrefix(ctx.Git.CurrentTag, "v")
    37  	return validate(ctx)
    38  }
    39  
    40  // nolint: gochecknoglobals
    41  var fakeInfo = context.GitInfo{
    42  	Branch:      "none",
    43  	CurrentTag:  "v0.0.0",
    44  	Commit:      "none",
    45  	ShortCommit: "none",
    46  	FullCommit:  "none",
    47  }
    48  
    49  func getInfo(ctx *context.Context) (context.GitInfo, error) {
    50  	if !git.IsRepo() && ctx.Snapshot {
    51  		log.Warn("accepting to run without a git repo because this is a snapshot")
    52  		return fakeInfo, nil
    53  	}
    54  	if !git.IsRepo() {
    55  		return context.GitInfo{}, ErrNotRepository
    56  	}
    57  	info, err := getGitInfo()
    58  	if err != nil && ctx.Snapshot {
    59  		log.WithError(err).Warn("ignoring errors because this is a snapshot")
    60  		if info.Commit == "" {
    61  			info = fakeInfo
    62  		}
    63  		return info, nil
    64  	}
    65  	return info, err
    66  }
    67  
    68  func getGitInfo() (context.GitInfo, error) {
    69  	branch, err := getBranch()
    70  	if err != nil {
    71  		return context.GitInfo{}, fmt.Errorf("couldn't get current branch: %w", err)
    72  	}
    73  	short, err := getShortCommit()
    74  	if err != nil {
    75  		return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
    76  	}
    77  	full, err := getFullCommit()
    78  	if err != nil {
    79  		return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
    80  	}
    81  	date, err := getCommitDate()
    82  	if err != nil {
    83  		return context.GitInfo{}, fmt.Errorf("couldn't get commit date: %w", err)
    84  	}
    85  	url, err := getURL()
    86  	if err != nil {
    87  		return context.GitInfo{}, fmt.Errorf("couldn't get remote URL: %w", err)
    88  	}
    89  	tag, err := getTag()
    90  	if err != nil {
    91  		return context.GitInfo{
    92  			Branch:      branch,
    93  			Commit:      full,
    94  			FullCommit:  full,
    95  			ShortCommit: short,
    96  			CommitDate:  date,
    97  			URL:         url,
    98  			CurrentTag:  "v0.0.0",
    99  		}, ErrNoTag
   100  	}
   101  	return context.GitInfo{
   102  		Branch:      branch,
   103  		CurrentTag:  tag,
   104  		Commit:      full,
   105  		FullCommit:  full,
   106  		ShortCommit: short,
   107  		CommitDate:  date,
   108  		URL:         url,
   109  	}, nil
   110  }
   111  
   112  func validate(ctx *context.Context) error {
   113  	if ctx.Snapshot {
   114  		return pipe.ErrSnapshotEnabled
   115  	}
   116  	if ctx.SkipValidate {
   117  		return pipe.ErrSkipValidateEnabled
   118  	}
   119  	out, err := git.Run("status", "--porcelain")
   120  	if strings.TrimSpace(out) != "" || err != nil {
   121  		return ErrDirty{status: out}
   122  	}
   123  	_, err = git.Clean(git.Run("describe", "--exact-match", "--tags", "--match", ctx.Git.CurrentTag))
   124  	if err != nil {
   125  		return ErrWrongRef{
   126  			commit: ctx.Git.Commit,
   127  			tag:    ctx.Git.CurrentTag,
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  func getBranch() (string, error) {
   134  	return git.Clean(git.Run("rev-parse", "--abbrev-ref", "HEAD", "--quiet"))
   135  }
   136  
   137  func getCommitDate() (time.Time, error) {
   138  	ct, err := git.Clean(git.Run("show", "--format='%ct'", "HEAD", "--quiet"))
   139  	if err != nil {
   140  		return time.Time{}, err
   141  	}
   142  	if ct == "" {
   143  		return time.Time{}, nil
   144  	}
   145  	i, err := strconv.ParseInt(ct, 10, 64)
   146  	if err != nil {
   147  		return time.Time{}, err
   148  	}
   149  	t := time.Unix(i, 0).UTC()
   150  	return t, nil
   151  }
   152  
   153  func getShortCommit() (string, error) {
   154  	return git.Clean(git.Run("show", "--format='%h'", "HEAD", "--quiet"))
   155  }
   156  
   157  func getFullCommit() (string, error) {
   158  	return git.Clean(git.Run("show", "--format='%H'", "HEAD", "--quiet"))
   159  }
   160  
   161  func getTag() (string, error) {
   162  	var tag string
   163  	var err error
   164  	for _, fn := range []func() (string, error){
   165  		func() (string, error) {
   166  			return os.Getenv("GORELEASER_CURRENT_TAG"), nil
   167  		},
   168  		func() (string, error) {
   169  			return git.Clean(git.Run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
   170  		},
   171  		func() (string, error) {
   172  			return git.Clean(git.Run("describe", "--tags", "--abbrev=0"))
   173  		},
   174  	} {
   175  		tag, err = fn()
   176  		if tag != "" || err != nil {
   177  			return tag, err
   178  		}
   179  	}
   180  
   181  	return tag, err
   182  }
   183  
   184  func getURL() (string, error) {
   185  	return git.Clean(git.Run("ls-remote", "--get-url"))
   186  }