github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/internal/pipe/git/git.go (about)

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