1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-26 04:22:05 +02:00

257 lines
6.1 KiB
Go
Raw Normal View History

2017-01-14 12:34:22 -02:00
package git
2017-01-29 21:55:32 -02:00
import (
"fmt"
"net/url"
"os"
"os/exec"
"strconv"
2017-01-29 21:55:32 -02:00
"strings"
"time"
2017-01-29 21:55:32 -02:00
2017-06-22 00:09:14 -03:00
"github.com/apex/log"
2017-08-19 12:47:04 -03:00
"github.com/goreleaser/goreleaser/internal/git"
2018-09-12 14:18:01 -03:00
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/pkg/context"
2017-01-29 21:55:32 -02:00
)
2017-01-14 12:34:22 -02:00
// Pipe that sets up git state.
2017-01-14 12:34:22 -02:00
type Pipe struct{}
func (Pipe) String() string {
return "getting and validating git state"
2017-01-14 12:34:22 -02:00
}
// Run the pipe.
func (Pipe) Run(ctx *context.Context) error {
if _, err := exec.LookPath("git"); err != nil {
return ErrNoGit
}
info, err := getInfo(ctx)
2017-01-14 12:34:22 -02:00
if err != nil {
return err
2017-01-14 12:34:22 -02:00
}
ctx.Git = info
log.WithField("commit", info.Commit).WithField("latest tag", info.CurrentTag).Info("building...")
2018-12-13 10:09:36 -02:00
ctx.Version = strings.TrimPrefix(ctx.Git.CurrentTag, "v")
return validate(ctx)
}
2018-11-07 22:04:49 -02:00
// nolint: gochecknoglobals
var fakeInfo = context.GitInfo{
Branch: "none",
2018-10-03 17:44:31 -03:00
CurrentTag: "v0.0.0",
Commit: "none",
ShortCommit: "none",
FullCommit: "none",
Summary: "none",
}
func getInfo(ctx *context.Context) (context.GitInfo, error) {
if !git.IsRepo() && ctx.Snapshot {
log.Warn("accepting to run without a git repo because this is a snapshot")
return fakeInfo, nil
}
if !git.IsRepo() {
return context.GitInfo{}, ErrNotRepository
}
info, err := getGitInfo()
if err != nil && ctx.Snapshot {
log.WithError(err).Warn("ignoring errors because this is a snapshot")
if info.Commit == "" {
info = fakeInfo
}
return info, nil
}
return info, err
}
func getGitInfo() (context.GitInfo, error) {
branch, err := getBranch()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get current branch: %w", err)
}
2018-10-03 17:44:31 -03:00
short, err := getShortCommit()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
2018-10-03 17:44:31 -03:00
}
full, err := getFullCommit()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get current commit: %w", err)
2017-01-14 14:06:57 -02:00
}
date, err := getCommitDate()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get commit date: %w", err)
}
summary, err := getSummary()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get summary: %w", err)
}
gitURL, err := getURL()
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get remote URL: %w", err)
}
if strings.HasPrefix(gitURL, "https://") {
u, err := url.Parse(gitURL)
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't parse remote URL: %w", err)
}
u.User = nil
gitURL = u.String()
}
tag, err := getTag()
if err != nil {
return context.GitInfo{
Branch: branch,
Commit: full,
2018-10-03 17:44:31 -03:00
FullCommit: full,
ShortCommit: short,
CommitDate: date,
URL: gitURL,
CurrentTag: "v0.0.0",
Summary: summary,
}, ErrNoTag
2017-05-01 10:39:57 -03:00
}
subject, err := getTagSubject(tag)
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get tag subject: %w", err)
}
contents, err := getTagContents(tag)
if err != nil {
return context.GitInfo{}, fmt.Errorf("couldn't get tag contents: %w", err)
}
previous, err := getPreviousTag(tag)
if err != nil {
// shouldn't error, will only affect templates
log.Warnf("couldn't find any tags before %q", tag)
}
return context.GitInfo{
Branch: branch,
2018-10-03 17:44:31 -03:00
CurrentTag: tag,
PreviousTag: previous,
Commit: full,
2018-10-03 17:44:31 -03:00
FullCommit: full,
ShortCommit: short,
CommitDate: date,
URL: gitURL,
Summary: summary,
TagSubject: subject,
TagContents: contents,
}, nil
2017-05-01 10:39:57 -03:00
}
func validate(ctx *context.Context) error {
if ctx.Snapshot {
2018-09-12 14:18:01 -03:00
return pipe.ErrSnapshotEnabled
2018-03-08 08:42:33 -03:00
}
if ctx.SkipValidate {
2018-09-12 14:18:01 -03:00
return pipe.ErrSkipValidateEnabled
}
if _, err := os.Stat(".git/shallow"); err == nil {
log.Warn("running against a shallow clone - check your CI documentation at https://goreleaser.com/ci")
}
if err := CheckDirty(); err != nil {
return err
2017-04-15 16:11:47 -03:00
}
_, err := git.Clean(git.Run("describe", "--exact-match", "--tags", "--match", ctx.Git.CurrentTag))
2017-05-01 10:57:37 -03:00
if err != nil {
2018-06-19 15:53:14 -03:00
return ErrWrongRef{
commit: ctx.Git.Commit,
tag: ctx.Git.CurrentTag,
}
2017-04-15 16:11:47 -03:00
}
return nil
2017-01-14 12:34:22 -02:00
}
// CheckDirty returns an error if the current git repository is dirty.
func CheckDirty() error {
out, err := git.Run("status", "--porcelain")
if strings.TrimSpace(out) != "" || err != nil {
return ErrDirty{status: out}
}
return nil
}
func getBranch() (string, error) {
return git.Clean(git.Run("rev-parse", "--abbrev-ref", "HEAD", "--quiet"))
}
func getCommitDate() (time.Time, error) {
ct, err := git.Clean(git.Run("show", "--format='%ct'", "HEAD", "--quiet"))
if err != nil {
return time.Time{}, err
}
if ct == "" {
return time.Time{}, nil
}
i, err := strconv.ParseInt(ct, 10, 64)
if err != nil {
return time.Time{}, err
}
t := time.Unix(i, 0).UTC()
return t, nil
}
2018-10-03 17:44:31 -03:00
func getShortCommit() (string, error) {
return git.Clean(git.Run("show", "--format='%h'", "HEAD", "--quiet"))
2018-10-03 17:44:31 -03:00
}
func getFullCommit() (string, error) {
return git.Clean(git.Run("show", "--format='%H'", "HEAD", "--quiet"))
}
func getSummary() (string, error) {
return git.Clean(git.Run("describe", "--always", "--dirty", "--tags"))
}
func getTagSubject(tag string) (string, error) {
return git.Clean(git.Run("tag", "-l", "--format='%(contents:subject)'", tag))
}
func getTagContents(tag string) (string, error) {
out, err := git.Run("tag", "-l", "--format='%(contents)'", tag)
return strings.TrimSuffix(strings.ReplaceAll(out, "'", ""), "\n\n"), err
}
func getTag() (string, error) {
var tag string
var err error
for _, fn := range []func() (string, error){
func() (string, error) {
return os.Getenv("GORELEASER_CURRENT_TAG"), nil
},
func() (string, error) {
fix: sort tags by version not day of the week (#2377) * fix: tag sorting When the HEAD commit has multiple tags, they are sorted in order to select the latest so that it can be released. However, the existing sort would not work if there were multiple commits across a Wednesday and a Thursday. For example: ``` git tag --points-at HEAD --format='%(creatordate)%09%(refname)' Wed Jul 28 07:13:19 2021 +0000 refs/tags/v0.0.183 Thu Jul 29 13:31:07 2021 +0000 refs/tags/v0.0.184 Thu Jul 29 13:38:42 2021 +0000 refs/tags/v0.0.185 Thu Jul 29 13:57:44 2021 +0000 refs/tags/v0.0.186 ``` When using the existing sort the `creatordate` field was targeted and reversed. Alphabetically Thursday comes before Wednesday, so that is reversed and the Wednesday release always comes first: ``` git tag --points-at HEAD --sort=-version:creatordate --format='%(creatordate)%09%(refname)' Wed Jul 28 07:13:19 2021 +0000 refs/tags/v0.0.183 Thu Jul 29 13:57:44 2021 +0000 refs/tags/v0.0.186 Thu Jul 29 13:38:42 2021 +0000 refs/tags/v0.0.185 Thu Jul 29 13:31:07 2021 +0000 refs/tags/v0.0.184 ``` This would make goreleaser attempt to release that existing tag again, and fail. If we instead sort by reversed `refname` we get the tags ordered by their numeric value, which ignore the day of the week of release: ``` git tag --points-at HEAD --sort=-version:refname --format='%(creatordate)%09%(refname)' Thu Jul 29 13:57:44 2021 +0000 refs/tags/v0.0.186 Thu Jul 29 13:38:42 2021 +0000 refs/tags/v0.0.185 Thu Jul 29 13:31:07 2021 +0000 refs/tags/v0.0.184 Wed Jul 28 07:13:19 2021 +0000 refs/tags/v0.0.183 ``` Allowing the latest version, 0.0.186 in this case, to be targeted for release. * corrected test case * add space * remove space
2021-08-02 17:20:09 +01:00
return git.Clean(git.Run("tag", "--points-at", "HEAD", "--sort", "-version:refname"))
},
func() (string, error) {
return git.Clean(git.Run("describe", "--tags", "--abbrev=0"))
},
} {
tag, err = fn()
if tag != "" || err != nil {
return tag, err
}
}
return tag, err
2017-04-23 16:33:44 -03:00
}
func getPreviousTag(current string) (string, error) {
if tag := os.Getenv("GORELEASER_PREVIOUS_TAG"); tag != "" {
return tag, nil
}
return git.Clean(git.Run("describe", "--tags", "--abbrev=0", fmt.Sprintf("tags/%s^", current)))
}
func getURL() (string, error) {
return git.Clean(git.Run("ls-remote", "--get-url"))
}